File indexing completed on 2025-01-05 03:53:54
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-04-09 0007 * Description : Collection location management - location helpers. 0008 * 0009 * SPDX-FileCopyrightText: 2007-2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "collectionmanager_p.h" 0016 0017 namespace Digikam 0018 { 0019 0020 CollectionLocation CollectionManager::addLocation(const QUrl& fileUrl, const QString& label) 0021 { 0022 qCDebug(DIGIKAM_DATABASE_LOG) << "addLocation" << fileUrl; 0023 QString path = fileUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0024 0025 if (!locationForPath(path).isNull()) 0026 { 0027 return CollectionLocation(); 0028 } 0029 0030 QList<SolidVolumeInfo> volumes = d->listVolumes(); 0031 SolidVolumeInfo volume = d->findVolumeForUrl(fileUrl, volumes); 0032 0033 if (!volume.isNull()) 0034 { 0035 // volume.path has a trailing slash. We want to split in front of this. 0036 0037 QString specificPath = path.mid(volume.path.length() - 1); 0038 CollectionLocation::Type type; 0039 0040 if (volume.isRemovable) 0041 { 0042 type = CollectionLocation::VolumeRemovable; 0043 } 0044 else 0045 { 0046 type = CollectionLocation::VolumeHardWired; 0047 } 0048 0049 ChangingDB changing(d); 0050 CoreDbAccess().db()->addAlbumRoot(type, d->volumeIdentifier(volume), specificPath, label); 0051 } 0052 else 0053 { 0054 // Empty volumes indicates that Solid is not working correctly. 0055 0056 if (volumes.isEmpty()) 0057 { 0058 qCDebug(DIGIKAM_DATABASE_LOG) << "Solid did not return any storage volumes on your system."; 0059 qCDebug(DIGIKAM_DATABASE_LOG) << "This indicates a missing implementation or a problem with your installation"; 0060 qCDebug(DIGIKAM_DATABASE_LOG) << "On Linux, check that Solid and HAL are working correctly. " 0061 "Problems with RAID partitions have been reported, " 0062 "if you have RAID this error may be normal."; 0063 qCDebug(DIGIKAM_DATABASE_LOG) << "On Windows, Solid may not be fully implemented, " 0064 "if you are running Windows this error may be normal."; 0065 } 0066 0067 // fall back 0068 0069 qCWarning(DIGIKAM_DATABASE_LOG) << "Unable to identify a path with Solid. Adding the location with path only."; 0070 0071 ChangingDB changing(d); 0072 CoreDbAccess().db()->addAlbumRoot(CollectionLocation::VolumeHardWired, 0073 d->volumeIdentifier(path), QLatin1String("/"), label); 0074 } 0075 0076 // Do not Q_EMIT the locationAdded signal here, it is done in updateLocations() 0077 0078 updateLocations(); 0079 0080 return locationForPath(path); 0081 } 0082 0083 CollectionLocation CollectionManager::addNetworkLocation(const QUrl& fileUrl, const QString& label) 0084 { 0085 qCDebug(DIGIKAM_DATABASE_LOG) << "addLocation" << fileUrl; 0086 QString path = fileUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0087 0088 if (!locationForPath(path).isNull()) 0089 { 0090 return CollectionLocation(); 0091 } 0092 0093 ChangingDB changing(d); 0094 CoreDbAccess().db()->addAlbumRoot(CollectionLocation::Network, 0095 d->networkShareIdentifier(QStringList() << path), 0096 QLatin1String("/"), label); 0097 0098 // Do not Q_EMIT the locationAdded signal here, it is done in updateLocations() 0099 0100 updateLocations(); 0101 0102 return locationForPath(path); 0103 } 0104 0105 CollectionLocation CollectionManager::refreshLocation(const CollectionLocation& location, int newType, 0106 const QStringList& pathList, const QString& label) 0107 { 0108 QUrl fileUrl = QUrl::fromLocalFile(pathList.first()); 0109 qCDebug(DIGIKAM_DATABASE_LOG) << "refreshLocation" << fileUrl; 0110 QString path = fileUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0111 0112 if (location.isNull()) 0113 { 0114 return CollectionLocation(); 0115 } 0116 0117 AlbumRootLocation* albumLoc = nullptr; 0118 0119 { 0120 QReadLocker readLocker(&d->lock); 0121 0122 albumLoc = d->locations.value(location.id()); 0123 0124 if (!albumLoc) 0125 { 0126 return CollectionLocation(); 0127 } 0128 } 0129 0130 QList<SolidVolumeInfo> volumes = d->listVolumes(); 0131 SolidVolumeInfo volume = d->findVolumeForUrl(fileUrl, volumes); 0132 0133 if (!volume.isNull() || (newType == CollectionLocation::Network)) 0134 { 0135 CollectionLocation::Type type; 0136 QString specificPath; 0137 QString identifier; 0138 0139 if (newType == CollectionLocation::VolumeRemovable) 0140 { 0141 // volume.path has a trailing slash. We want to split in front of this. 0142 0143 type = CollectionLocation::VolumeRemovable; 0144 identifier = d->volumeIdentifier(volume); 0145 specificPath = path.mid(volume.path.length() - 1); 0146 } 0147 else if (newType == CollectionLocation::Network) 0148 { 0149 type = CollectionLocation::Network; 0150 specificPath = QLatin1String("/"); 0151 identifier = d->networkShareIdentifier(pathList); 0152 } 0153 else 0154 { 0155 type = CollectionLocation::VolumeHardWired; 0156 identifier = d->volumeIdentifier(volume); 0157 specificPath = path.mid(volume.path.length() - 1); 0158 } 0159 0160 CoreDbAccess access; 0161 ChangingDB changing(d); 0162 access.db()->setAlbumRootLabel(location.id(), label); 0163 access.db()->setAlbumRootType(location.id(), type); 0164 access.db()->migrateAlbumRoot(location.id(), identifier); 0165 access.db()->setAlbumRootPath(location.id(), specificPath); 0166 access.db()->setAlbumRootCaseSensitivity(location.id(), CollectionLocation::UnknownCaseSensitivity); 0167 0168 albumLoc->setLabel(label); 0169 albumLoc->identifier = identifier; 0170 albumLoc->specificPath = specificPath; 0171 albumLoc->setType((CollectionLocation::Type)type); 0172 albumLoc->setCaseSensitivity(CollectionLocation::UnknownCaseSensitivity); 0173 0174 Q_EMIT locationPropertiesChanged(*albumLoc); 0175 } 0176 else 0177 { 0178 // Empty volumes indicates that Solid is not working correctly. 0179 0180 if (volumes.isEmpty()) 0181 { 0182 qCDebug(DIGIKAM_DATABASE_LOG) << "Solid did not return any storage volumes on your system."; 0183 qCDebug(DIGIKAM_DATABASE_LOG) << "This indicates a missing implementation or a problem with your installation"; 0184 qCDebug(DIGIKAM_DATABASE_LOG) << "On Linux, check that Solid and HAL are working correctly. " 0185 "Problems with RAID partitions have been reported, " 0186 "if you have RAID this error may be normal."; 0187 qCDebug(DIGIKAM_DATABASE_LOG) << "On Windows, Solid may not be fully implemented, " 0188 "if you are running Windows this error may be normal."; 0189 } 0190 0191 // fall back 0192 0193 qCWarning(DIGIKAM_DATABASE_LOG) << "Unable to identify a path with Solid. Update the location with path only."; 0194 0195 CoreDbAccess access; 0196 ChangingDB changing(d); 0197 CollectionLocation::Type type = CollectionLocation::VolumeHardWired; 0198 access.db()->setAlbumRootLabel(location.id(), label); 0199 access.db()->setAlbumRootType(location.id(), type); 0200 access.db()->setAlbumRootPath(location.id(), QLatin1String("/")); 0201 access.db()->migrateAlbumRoot(location.id(), d->volumeIdentifier(path)); 0202 access.db()->setAlbumRootCaseSensitivity(location.id(), CollectionLocation::UnknownCaseSensitivity); 0203 0204 albumLoc->setLabel(label); 0205 albumLoc->specificPath = QLatin1String("/"); 0206 albumLoc->setType((CollectionLocation::Type)type); 0207 albumLoc->identifier = d->volumeIdentifier(path); 0208 albumLoc->setCaseSensitivity(CollectionLocation::UnknownCaseSensitivity); 0209 0210 Q_EMIT locationPropertiesChanged(*albumLoc); 0211 } 0212 0213 // Do not Q_EMIT the locationAdded signal here, it is done in updateLocations() 0214 0215 updateLocations(); 0216 0217 return locationForPath(path); 0218 } 0219 0220 CollectionManager::LocationCheckResult CollectionManager::checkLocation(const QUrl& fileUrl, 0221 QList<CollectionLocation>& assumeDeleted, 0222 QString* message, 0223 QString* iconName) 0224 { 0225 if (!fileUrl.isLocalFile()) 0226 { 0227 if (message) 0228 { 0229 *message = i18n("Sorry, digiKam does not support remote URLs as collections."); 0230 } 0231 0232 if (iconName) 0233 { 0234 *iconName = QLatin1String("dialog-error"); 0235 } 0236 0237 return LocationNotAllowed; 0238 } 0239 0240 QString path = fileUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0241 QDir dir(path); 0242 0243 if (!dir.isReadable()) 0244 { 0245 if (message) 0246 { 0247 *message = i18n("The selected folder does not exist or is not readable"); 0248 } 0249 0250 if (iconName) 0251 { 0252 *iconName = QLatin1String("dialog-error"); 0253 } 0254 0255 return LocationNotAllowed; 0256 } 0257 0258 if (d->checkIfExists(path, assumeDeleted)) 0259 { 0260 if (message) 0261 { 0262 *message = i18n("There is already a collection containing the folder \"%1\"", QDir::toNativeSeparators(path)); 0263 } 0264 0265 if (iconName) 0266 { 0267 *iconName = QLatin1String("dialog-error"); 0268 } 0269 0270 return LocationNotAllowed; 0271 } 0272 0273 QList<SolidVolumeInfo> volumes = d->listVolumes(); 0274 SolidVolumeInfo volume = d->findVolumeForUrl(fileUrl, volumes); 0275 0276 if (!volume.isNull()) 0277 { 0278 if (!volume.uuid.isEmpty()) 0279 { 0280 if (volume.isRemovable) 0281 { 0282 if (message) 0283 { 0284 *message = i18n("The storage media can be uniquely identified."); 0285 } 0286 0287 if (iconName) 0288 { 0289 *iconName = QLatin1String("drive-removable-media"); 0290 } 0291 } 0292 else 0293 { 0294 if (message) 0295 { 0296 *message = i18n("The collection is located on your harddisk"); 0297 } 0298 0299 if (iconName) 0300 { 0301 *iconName = QLatin1String("drive-harddisk"); 0302 } 0303 } 0304 0305 return LocationAllRight; 0306 } 0307 else if (!volume.label.isEmpty() && (volume.isOpticalDisc || volume.isRemovable)) 0308 { 0309 if (volume.isOpticalDisc) 0310 { 0311 bool hasOtherLocation = false; 0312 0313 Q_FOREACH (AlbumRootLocation* const otherLocation, d->locations) 0314 { 0315 QUrl otherUrl(otherLocation->identifier); 0316 0317 if ((otherUrl.scheme() == QLatin1String("volumeid")) && 0318 (QUrlQuery(otherUrl).queryItemValue(QLatin1String("label")) == volume.label)) 0319 { 0320 hasOtherLocation = true; 0321 break; 0322 } 0323 } 0324 0325 if (iconName) 0326 { 0327 *iconName = QLatin1String("media-optical"); 0328 } 0329 0330 if (hasOtherLocation) 0331 { 0332 if (message) 0333 { 0334 *message = i18n("This is a CD/DVD, which is identified by the label " 0335 "that you can set in your CD burning application. " 0336 "There is already another entry with the same label. " 0337 "The two will be distinguished by the files in the top directory, " 0338 "so please do not append files to the CD, or it will not be recognized. " 0339 "In the future, please set a unique label on your CDs and DVDs " 0340 "if you intend to use them with digiKam."); 0341 } 0342 0343 return LocationHasProblems; 0344 } 0345 else 0346 { 0347 if (message) 0348 { 0349 *message = i18n("This is a CD/DVD. It will be identified by the label (\"%1\")" 0350 "that you have set in your CD burning application. " 0351 "If you create further CDs for use with digikam in the future, " 0352 "please remember to give them a unique label as well.", 0353 volume.label); 0354 } 0355 0356 return LocationAllRight; 0357 } 0358 } 0359 else 0360 { 0361 // Which situation? HasProblems or AllRight? 0362 0363 if (message) 0364 { 0365 *message = i18n("This is a removable storage medium that will be identified by its label (\"%1\")", 0366 volume.label); 0367 } 0368 0369 if (iconName) 0370 { 0371 *iconName = QLatin1String("drive-removable-media"); 0372 } 0373 0374 return LocationAllRight; 0375 } 0376 } 0377 else 0378 { 0379 if (message) 0380 { 0381 *message = i18n("This entry will only be identified by the path where it is found on your system (\"%1\"). " 0382 "No more specific means of identification (UUID, label) is available.", 0383 QDir::toNativeSeparators(volume.path)); 0384 } 0385 0386 if (iconName) 0387 { 0388 *iconName = QLatin1String("drive-removale-media"); 0389 } 0390 0391 return LocationHasProblems; 0392 } 0393 } 0394 else 0395 { 0396 if (message) 0397 { 0398 *message = i18n("It is not possible on your system to identify the storage medium of this path. " 0399 "It will be added using the file path as the only identifier. " 0400 "This will work well for your local hard disk."); 0401 } 0402 0403 if (iconName) 0404 { 0405 *iconName = QLatin1String("folder-important"); 0406 } 0407 0408 return LocationHasProblems; 0409 } 0410 } 0411 0412 CollectionManager::LocationCheckResult CollectionManager::checkNetworkLocation(const QUrl& fileUrl, 0413 QList<CollectionLocation>& assumeDeleted, 0414 QString* message, 0415 QString* iconName) 0416 { 0417 if (!fileUrl.isLocalFile()) 0418 { 0419 if (message) 0420 { 0421 if (fileUrl.scheme() == QLatin1String("smb")) 0422 { 0423 *message = i18n("You need to locally mount your Samba share. " 0424 "Sorry, digiKam does currently not support smb:// URLs. "); 0425 } 0426 else 0427 { 0428 *message = i18n("Your network storage must be set up to be accessible " 0429 "as files and folders through the operating system. " 0430 "digiKam does not support remote URLs."); 0431 } 0432 } 0433 0434 if (iconName) 0435 { 0436 *iconName = QLatin1String("dialog-error"); 0437 } 0438 0439 return LocationNotAllowed; 0440 } 0441 0442 QString path = fileUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0443 0444 QDir dir(path); 0445 0446 if (!dir.isReadable()) 0447 { 0448 if (message) 0449 { 0450 *message = i18n("The selected folder does not exist or is not readable"); 0451 } 0452 0453 if (iconName) 0454 { 0455 *iconName = QLatin1String("dialog-error"); 0456 } 0457 0458 return LocationNotAllowed; 0459 } 0460 0461 if (d->checkIfExists(path, assumeDeleted)) 0462 { 0463 if (message) 0464 { 0465 *message = i18n("There is already a collection for a network share with the same path."); 0466 } 0467 0468 if (iconName) 0469 { 0470 *iconName = QLatin1String("dialog-error"); 0471 } 0472 0473 return LocationNotAllowed; 0474 } 0475 0476 if (message) 0477 { 0478 *message = i18n("The network share will be identified by the path you selected. " 0479 "If the path is empty, the share will be considered unavailable."); 0480 } 0481 0482 if (iconName) 0483 { 0484 *iconName = QLatin1String("network-wired-activated"); 0485 } 0486 0487 return LocationAllRight; 0488 } 0489 0490 void CollectionManager::removeLocation(const CollectionLocation& location) 0491 { 0492 AlbumRootLocation* albumLoc = nullptr; 0493 0494 { 0495 QReadLocker readLocker(&d->lock); 0496 0497 albumLoc = d->locations.value(location.id()); 0498 0499 if (!albumLoc) 0500 { 0501 return; 0502 } 0503 } 0504 0505 // Ensure that all albums are set to orphan and no images will be permanently deleted, 0506 // as would do only calling deleteAlbumRoot by a Trigger 0507 0508 CoreDbAccess access; 0509 QList<int> albumIds = access.db()->getAlbumsOnAlbumRoot(albumLoc->id()); 0510 0511 ChangingDB changing(d); 0512 CollectionScanner scanner; 0513 CoreDbTransaction transaction(&access); 0514 0515 scanner.safelyRemoveAlbums(albumIds); 0516 access.db()->deleteAlbumRoot(albumLoc->id()); 0517 0518 // Do not Q_EMIT the locationRemoved signal here, it is done in updateLocations() 0519 0520 updateLocations(); 0521 } 0522 0523 QList<CollectionLocation> CollectionManager::checkHardWiredLocations() 0524 { 0525 QList<CollectionLocation> disappearedLocations; 0526 QList<SolidVolumeInfo> volumes = d->listVolumes(); 0527 0528 QReadLocker readLocker(&d->lock); 0529 0530 Q_FOREACH (AlbumRootLocation* const location, d->locations) 0531 { 0532 // Hardwired and unavailable? 0533 0534 if ((location->type() == CollectionLocation::VolumeHardWired) && 0535 (location->status() == CollectionLocation::LocationUnavailable)) 0536 { 0537 disappearedLocations << *location; 0538 } 0539 } 0540 0541 return disappearedLocations; 0542 } 0543 0544 void CollectionManager::migrationCandidates(const CollectionLocation& location, 0545 QString* const description, 0546 QStringList* const candidateIdentifiers, 0547 QStringList* const candidateDescriptions) 0548 { 0549 description->clear(); 0550 candidateIdentifiers->clear(); 0551 candidateDescriptions->clear(); 0552 0553 AlbumRootLocation* albumLoc = nullptr; 0554 0555 { 0556 QReadLocker readLocker(&d->lock); 0557 0558 albumLoc = d->locations.value(location.id()); 0559 0560 if (!albumLoc) 0561 { 0562 return; 0563 } 0564 } 0565 0566 QList<SolidVolumeInfo> volumes = d->listVolumes(); 0567 *description = d->technicalDescription(albumLoc); 0568 0569 // Find possible new volumes where the specific path is found. 0570 0571 Q_FOREACH (const SolidVolumeInfo& info, volumes) 0572 { 0573 if (info.isMounted && !info.path.isEmpty()) 0574 { 0575 QDir dir(info.path + albumLoc->specificPath); 0576 0577 if (dir.exists()) 0578 { 0579 *candidateIdentifiers << d->volumeIdentifier(info); 0580 *candidateDescriptions << dir.absolutePath(); 0581 } 0582 } 0583 } 0584 } 0585 0586 void CollectionManager::migrateToVolume(const CollectionLocation& location, const QString& identifier) 0587 { 0588 AlbumRootLocation* albumLoc = nullptr; 0589 0590 { 0591 QReadLocker readLocker(&d->lock); 0592 0593 albumLoc = d->locations.value(location.id()); 0594 0595 if (!albumLoc) 0596 { 0597 return; 0598 } 0599 } 0600 0601 // update db 0602 0603 ChangingDB db(d); 0604 CoreDbAccess().db()->migrateAlbumRoot(albumLoc->id(), identifier); 0605 0606 // update local structure 0607 0608 albumLoc->identifier = identifier; 0609 0610 updateLocations(); 0611 } 0612 0613 void CollectionManager::setLabel(const CollectionLocation& location, const QString& label) 0614 { 0615 AlbumRootLocation* albumLoc = nullptr; 0616 0617 { 0618 QReadLocker readLocker(&d->lock); 0619 0620 albumLoc = d->locations.value(location.id()); 0621 0622 if (!albumLoc) 0623 { 0624 return; 0625 } 0626 } 0627 0628 // update db 0629 0630 ChangingDB db(d); 0631 CoreDbAccess().db()->setAlbumRootLabel(albumLoc->id(), label); 0632 0633 // update local structure 0634 0635 albumLoc->setLabel(label); 0636 0637 Q_EMIT locationPropertiesChanged(*albumLoc); 0638 } 0639 0640 void CollectionManager::changeType(const CollectionLocation& location, int type) 0641 { 0642 AlbumRootLocation* albumLoc = nullptr; 0643 0644 { 0645 QReadLocker readLocker(&d->lock); 0646 0647 albumLoc = d->locations.value(location.id()); 0648 0649 if (!albumLoc) 0650 { 0651 return; 0652 } 0653 } 0654 0655 // update db 0656 0657 ChangingDB db(d); 0658 CoreDbAccess().db()->setAlbumRootType(albumLoc->id(), (CollectionLocation::Type)type); 0659 0660 // update local structure 0661 0662 albumLoc->setType((CollectionLocation::Type)type); 0663 0664 Q_EMIT locationPropertiesChanged(*albumLoc); 0665 } 0666 0667 QList<CollectionLocation> CollectionManager::allLocations() 0668 { 0669 QReadLocker readLocker(&d->lock); 0670 0671 QList<CollectionLocation> list; 0672 0673 Q_FOREACH (AlbumRootLocation* const location, d->locations) 0674 { 0675 list << *location; 0676 } 0677 0678 return list; 0679 } 0680 0681 QList<CollectionLocation> CollectionManager::allAvailableLocations() 0682 { 0683 QReadLocker readLocker(&d->lock); 0684 0685 QList<CollectionLocation> list; 0686 0687 Q_FOREACH (AlbumRootLocation* const location, d->locations) 0688 { 0689 if (location->status() == CollectionLocation::LocationAvailable) 0690 { 0691 list << *location; 0692 } 0693 } 0694 0695 return list; 0696 } 0697 0698 CollectionLocation CollectionManager::locationForAlbumRootId(int id) 0699 { 0700 QReadLocker readLocker(&d->lock); 0701 0702 AlbumRootLocation* const location = d->locations.value(id); 0703 0704 if (location) 0705 { 0706 return *location; 0707 } 0708 0709 return CollectionLocation(); 0710 } 0711 0712 CollectionLocation CollectionManager::locationForAlbumRoot(const QUrl& fileUrl) 0713 { 0714 return locationForAlbumRootPath(fileUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile()); 0715 } 0716 0717 CollectionLocation CollectionManager::locationForAlbumRootPath(const QString& albumRootPath) 0718 { 0719 // This function is used when an album is created or an external scan is 0720 // initiated by the AlbumWatcher. We check if there is an entry because 0721 // the mount path of a network share may not be available. 0722 0723 if (!QDirIterator(albumRootPath, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot).hasNext()) 0724 { 0725 qCWarning(DIGIKAM_DATABASE_LOG) << "Album root path not exist" << albumRootPath; 0726 qCWarning(DIGIKAM_DATABASE_LOG) << "Drive or network connection broken?"; 0727 0728 updateLocations(); 0729 } 0730 0731 QReadLocker readLocker(&d->lock); 0732 0733 Q_FOREACH (AlbumRootLocation* const location, d->locations) 0734 { 0735 if (location->albumRootPath() == albumRootPath) 0736 { // cppcheck-suppress useStlAlgorithm 0737 return *location; 0738 } 0739 } 0740 0741 return CollectionLocation(); 0742 } 0743 0744 CollectionLocation CollectionManager::locationForUrl(const QUrl& fileUrl) 0745 { 0746 return locationForPath(fileUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile()); 0747 } 0748 0749 CollectionLocation CollectionManager::locationForPath(const QString& givenPath) 0750 { 0751 QReadLocker readLocker(&d->lock); 0752 0753 Q_FOREACH (AlbumRootLocation* const location, d->locations) 0754 { 0755 QString rootPath = location->albumRootPath(); 0756 QString filePath = QDir::fromNativeSeparators(givenPath); 0757 0758 if (!rootPath.isEmpty() && filePath.startsWith(rootPath)) 0759 { 0760 // see also bug #221155 for extra checks 0761 0762 if ((filePath == rootPath) || filePath.startsWith(rootPath + QLatin1Char('/'))) 0763 { 0764 return *location; 0765 } 0766 } 0767 } 0768 0769 return CollectionLocation(); 0770 } 0771 0772 void CollectionManager::updateLocations() 0773 { 0774 QMap<int, AlbumRootLocation*> newLocations; 0775 QMap<int, AlbumRootLocation*> oldLocations; 0776 QList<CollectionLocation::Status> oldStatus; 0777 0778 // read information from database 0779 0780 QList<AlbumRootInfo> infos = CoreDbAccess().db()->getAlbumRoots(); 0781 0782 // synchronize map with database 0783 0784 { 0785 QReadLocker locker(&d->lock); 0786 oldLocations = d->locations; 0787 } 0788 0789 Q_FOREACH (const AlbumRootInfo& info, infos) 0790 { 0791 if (oldLocations.contains(info.id)) 0792 { 0793 newLocations[info.id] = oldLocations.value(info.id); 0794 oldLocations.remove(info.id); 0795 } 0796 else 0797 { 0798 newLocations[info.id] = new AlbumRootLocation(info); 0799 } 0800 } 0801 0802 // update status with current access state, 0803 // store old status in QList oldStatus 0804 0805 // get information from Solid 0806 0807 QList<SolidVolumeInfo> volumes = d->listVolumes(); 0808 0809 Q_FOREACH (AlbumRootLocation* const location, newLocations) 0810 { 0811 oldStatus << location->status(); 0812 bool available = false; 0813 QString absolutePath; 0814 0815 if (location->type() == CollectionLocation::Network) 0816 { 0817 Q_FOREACH (const QString& path, d->networkShareMountPathsFromIdentifier(location)) 0818 { 0819 absolutePath = path; 0820 QUrl url(location->identifier); 0821 QString uuidValue = d->getCollectionUUID(path); 0822 QString queryItem = QUrlQuery(url).queryItemValue(QLatin1String("fileuuid")); 0823 0824 if (!queryItem.isNull() && (queryItem == uuidValue)) 0825 { 0826 available = true; 0827 } 0828 else if (queryItem.isNull()) 0829 { 0830 QFileInfo fileInfo(path); 0831 available = (fileInfo.isReadable() && 0832 QDirIterator(path, QDir::Dirs | 0833 QDir::Files | 0834 QDir::NoDotAndDotDot).hasNext()); 0835 } 0836 0837 if (available) 0838 { 0839 break; 0840 } 0841 } 0842 } 0843 else 0844 { 0845 SolidVolumeInfo info = d->findVolumeForLocation(location, volumes); 0846 0847 if (!info.isNull()) 0848 { 0849 QString volumePath = info.path; 0850 0851 // volume.path has a trailing slash (and this is good) 0852 // but specific path has a leading slash, so remove it 0853 0854 volumePath.chop(1); 0855 0856 // volumePath is the mount point of the volume; 0857 // specific path is the path on the file system of the volume. 0858 0859 absolutePath = volumePath + location->specificPath; 0860 available = (info.isMounted && QFileInfo::exists(absolutePath)); 0861 } 0862 else 0863 { 0864 QString path = d->pathFromIdentifier(location); 0865 0866 if (!path.isNull()) 0867 { 0868 available = true; 0869 0870 // Here we have the absolute path as definition of the volume. 0871 // specificPath is "/" as per convention, but ignored, 0872 // absolute path shall not have a trailing slash. 0873 0874 absolutePath = path; 0875 } 0876 } 0877 } 0878 0879 // set values in location 0880 // Don't touch location->status, do not interfere with "hidden" setting 0881 0882 location->available = available; 0883 location->setAbsolutePath(absolutePath); 0884 0885 if (available) 0886 { 0887 if (d->checkCollectionUUID(location, absolutePath)) 0888 { 0889 ChangingDB changing(d); 0890 CoreDbAccess().db()->migrateAlbumRoot(location->id(), location->identifier); 0891 } 0892 } 0893 0894 if (available && (location->caseSensitivity() == CollectionLocation::UnknownCaseSensitivity)) 0895 { 0896 QFileInfo writeInfo(absolutePath); 0897 0898 if (writeInfo.isWritable()) 0899 { 0900 SafeTemporaryFile* const temp = new SafeTemporaryFile(absolutePath + 0901 QLatin1String("/CaseSensitivity-XXXXXX-Test")); 0902 temp->setAutoRemove(false); 0903 temp->open(); 0904 QFileInfo tempInfo(temp->safeFilePath()); 0905 QFileInfo testInfo(tempInfo.path() + 0906 QLatin1Char('/') + 0907 tempInfo.fileName().toLower()); 0908 bool testCaseSensitivity = testInfo.exists(); 0909 delete temp; 0910 QFile::remove(tempInfo.filePath()); 0911 0912 if (testCaseSensitivity) 0913 { 0914 location->setCaseSensitivity(CollectionLocation::CaseInsensitive); 0915 } 0916 else 0917 { 0918 location->setCaseSensitivity(CollectionLocation::CaseSensitive); 0919 } 0920 0921 ChangingDB changing(d); 0922 CoreDbAccess().db()->setAlbumRootCaseSensitivity(location->id(), 0923 location->caseSensitivity()); 0924 } 0925 } 0926 0927 qCDebug(DIGIKAM_DATABASE_LOG) << "Location for" << absolutePath 0928 << "is available:" << available 0929 << "=>" << "case sensitivity:" 0930 << location->caseSensitivity(); 0931 0932 // set the status depending on "hidden" and "available" 0933 0934 location->setStatusFromFlags(); 0935 } 0936 0937 { 0938 QWriteLocker locker(&d->lock); 0939 d->locations = newLocations; 0940 } 0941 0942 // Q_EMIT deleted old locations 0943 0944 Q_FOREACH (AlbumRootLocation* const location, oldLocations) 0945 { 0946 CollectionLocation::Status statusOld = location->status(); 0947 location->setStatus(CollectionLocation::LocationDeleted); 0948 0949 Q_EMIT locationStatusChanged(*location, statusOld); 0950 0951 delete location; 0952 } 0953 0954 // Q_EMIT status changes (and new locations) 0955 0956 int i = 0; 0957 0958 Q_FOREACH (AlbumRootLocation* const location, newLocations) 0959 { 0960 if (oldStatus.at(i) != location->status()) 0961 { 0962 Q_EMIT locationStatusChanged(*location, oldStatus.at(i)); 0963 } 0964 0965 ++i; 0966 } 0967 } 0968 0969 } // namespace Digikam