File indexing completed on 2024-05-19 12:06:32
0001 // SPDX-FileCopyrightText: 2003-2020 The KPhotoAlbum Development Team 0002 // SPDX-FileCopyrightText: 2022 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0003 // 0004 // SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 #include "FileVersionDetectionPage.h" 0007 0008 #include <kpabase/SettingsData.h> 0009 0010 #include <KComboBox> 0011 #include <KLocalizedString> 0012 #include <QCheckBox> 0013 #include <QGroupBox> 0014 #include <QLabel> 0015 #include <QLineEdit> 0016 #include <QScrollArea> 0017 #include <QSpinBox> 0018 #include <QVBoxLayout> 0019 0020 Settings::FileVersionDetectionPage::FileVersionDetectionPage(QWidget *parent) 0021 : QWidget(parent) 0022 { 0023 auto *mainLayout = new QVBoxLayout(this); 0024 auto *scrollWidget = new QWidget; 0025 auto *topLayout = new QVBoxLayout(scrollWidget); 0026 0027 QString txt; 0028 0029 // General file searching 0030 { 0031 QGroupBox *generalBox = new QGroupBox(i18n("New File Searches"), this); 0032 topLayout->addWidget(generalBox); 0033 QVBoxLayout *layout = new QVBoxLayout(generalBox); 0034 0035 // Search for images on startup 0036 m_searchForImagesOnStart = new QCheckBox(i18n("Search for new images and videos on startup"), generalBox); 0037 layout->addWidget(m_searchForImagesOnStart); 0038 0039 m_ignoreFileExtension = new QCheckBox(i18n("Ignore file extensions when searching for new images and videos"), generalBox); 0040 layout->addWidget(m_ignoreFileExtension); 0041 0042 m_skipSymlinks = new QCheckBox(i18n("Skip symbolic links when searching for new images"), generalBox); 0043 layout->addWidget(m_skipSymlinks); 0044 0045 m_skipRawIfOtherMatches = new QCheckBox(i18n("Do not read RAW files if a matching JPEG/TIFF file exists"), generalBox); 0046 layout->addWidget(m_skipRawIfOtherMatches); 0047 0048 // Exclude directories from search 0049 QLabel *excludeDirectoriesLabel = new QLabel(i18n("Directories to exclude from new file search:"), generalBox); 0050 layout->addWidget(excludeDirectoriesLabel); 0051 0052 m_excludeDirectories = new QLineEdit(generalBox); 0053 layout->addWidget(m_excludeDirectories); 0054 excludeDirectoriesLabel->setBuddy(m_excludeDirectories); 0055 0056 txt = i18n("<p>Specify a comma-separated list of folder names to ignore.</p>" 0057 "<p>For example, specifying \"<tt>.thumbs,.thumbnails</tt>\" here will cause " 0058 "KPhotoAlbum to ignore these two folder names in any folder when searching for new images.</p>"); 0059 m_excludeDirectories->setWhatsThis(txt); 0060 0061 txt = i18n("<p>KPhotoAlbum is capable of searching for new images and videos when started, this does, " 0062 "however, take some time, so instead you may wish to manually tell KPhotoAlbum to search for new images " 0063 "using <b>Maintenance->Rescan for new images</b></p>"); 0064 m_searchForImagesOnStart->setWhatsThis(txt); 0065 0066 txt = i18n("<p>KPhotoAlbum will normally search new images and videos by their file extension. " 0067 "If this option is set, <em>all</em> files neither in the database nor in the block list " 0068 "will be checked by their Mime type, regardless of their extension. This will take " 0069 "significantly longer than finding files by extension!</p>"); 0070 m_ignoreFileExtension->setWhatsThis(txt); 0071 0072 txt = i18n("<p>KPhotoAlbum attempts to read all image files whether actual files or symbolic links. If you " 0073 "wish to ignore symbolic links, check this option. This is useful if for some reason you have e.g. " 0074 "both the original files and symbolic links to these files within your image folder.</p>"); 0075 m_skipSymlinks->setWhatsThis(txt); 0076 0077 txt = i18n("<p>KPhotoAlbum is capable of reading certain kinds of RAW images. " 0078 "Some cameras store both a RAW image and a matching JPEG or TIFF image. " 0079 "This causes duplicate images to be stored in KPhotoAlbum, which may be undesirable. " 0080 "If this option is checked, KPhotoAlbum will not read RAW files for which matching image files also exist.</p>"); 0081 m_skipRawIfOtherMatches->setWhatsThis(txt); 0082 0083 txt = i18n("<p>Directories defined here (separated by comma <tt>,</tt>) are " 0084 "skipped when searching for new photos. Thumbnail directories of different " 0085 "tools should be configured here. E.g. <tt>xml,ThumbNails,.thumbs,.thumbnails</tt>.</p>"); 0086 excludeDirectoriesLabel->setWhatsThis(txt); 0087 } 0088 0089 // Original/Modified File Support 0090 { 0091 QGroupBox *modifiedBox = new QGroupBox(i18n("File Version Detection Settings"), this); 0092 topLayout->addWidget(modifiedBox); 0093 QVBoxLayout *layout = new QVBoxLayout(modifiedBox); 0094 0095 m_detectModifiedFiles = new QCheckBox(i18n("Try to detect multiple versions of files"), modifiedBox); 0096 layout->addWidget(m_detectModifiedFiles); 0097 0098 QLabel *modifiedFileComponentLabel = new QLabel(i18n("File versions search regexp:"), modifiedBox); 0099 layout->addWidget(modifiedFileComponentLabel); 0100 0101 m_modifiedFileComponent = new QLineEdit(modifiedBox); 0102 layout->addWidget(m_modifiedFileComponent); 0103 0104 QLabel *originalFileComponentLabel = new QLabel(i18n("Original file replacement text:"), modifiedBox); 0105 layout->addWidget(originalFileComponentLabel); 0106 0107 m_originalFileComponent = new QLineEdit(modifiedBox); 0108 layout->addWidget(m_originalFileComponent); 0109 0110 m_moveOriginalContents = new QCheckBox(i18n("Move meta-data (i.e. delete tags from the original)"), modifiedBox); 0111 layout->addWidget(m_moveOriginalContents); 0112 0113 m_autoStackNewFiles = new QCheckBox(i18n("Automatically stack new versions of images"), modifiedBox); 0114 layout->addWidget(m_autoStackNewFiles); 0115 0116 txt = i18n("<p>When KPhotoAlbum searches for new files and finds a file that matches the " 0117 "<i>modified file search regexp</i> it is assumed that an original version of " 0118 "the image may exist. The regexp pattern will be replaced with the <i>original " 0119 "file replacement text</i> and if that file exists, all associated metadata (category " 0120 "information, ratings, etc) will be copied or moved from the original file to the new one.</p>"); 0121 m_detectModifiedFiles->setWhatsThis(txt); 0122 0123 txt = i18n("<p>A perl regular expression that should match a modified file. " 0124 "<ul><li>A dot matches a single character (<tt>\\.</tt> matches a dot)</li> " 0125 " <li>You can use the quantifiers <tt>*</tt>,<tt>+</tt>,<tt>?</tt>, or you can " 0126 " match multiple occurrences of an expression by using curly brackets (e.g. " 0127 "<tt>e{0,1}</tt> matches 0 or 1 occurrences of the character \"e\").</li> " 0128 " <li>You can group parts of the expression using parenthesis.</li> " 0129 "</ul>Example: <tt>-modified\\.(jpg|tiff)</tt> </p>"); 0130 modifiedFileComponentLabel->setWhatsThis(txt); 0131 m_modifiedFileComponent->setWhatsThis(txt); 0132 0133 txt = i18n("<p>A string that is used to replace the match from the <i>File versions search regexp</i>. " 0134 "This can be a semicolon (<tt>;</tt>) separated list. Each string is used to replace the match " 0135 "in the new file's name until an original file is found or we run out of options.</p>"); 0136 originalFileComponentLabel->setWhatsThis(txt); 0137 m_originalFileComponent->setWhatsThis(txt); 0138 0139 txt = i18n("<p>The tagging is moved from the original file to the new file. This way " 0140 "only the latest version of an image is tagged.</p>"); 0141 m_moveOriginalContents->setWhatsThis(txt); 0142 0143 txt = i18n("<p>If this option is set, new versions of an image are automatically stacked " 0144 "and placed to the top of the stack. This way the new image is shown when the " 0145 "stack is in collapsed state - the default state in KPhotoAlbum.</p>"); 0146 m_autoStackNewFiles->setWhatsThis(txt); 0147 } 0148 0149 // Copy File Support 0150 { 0151 QGroupBox *copyBox = new QGroupBox(i18nc("Configure the feature to make a copy of a file first and then open the copied file with an external application", "Copy File and Open with an External Application"), this); 0152 topLayout->addWidget(copyBox); 0153 QVBoxLayout *layout = new QVBoxLayout(copyBox); 0154 0155 QLabel *copyFileComponentLabel = new QLabel(i18n("Copy file search regexp:"), copyBox); 0156 layout->addWidget(copyFileComponentLabel); 0157 0158 m_copyFileComponent = new QLineEdit(copyBox); 0159 layout->addWidget(m_copyFileComponent); 0160 0161 QLabel *copyFileReplacementComponentLabel = new QLabel(i18n("Copy file replacement text:"), copyBox); 0162 layout->addWidget(copyFileReplacementComponentLabel); 0163 0164 m_copyFileReplacementComponent = new QLineEdit(copyBox); 0165 layout->addWidget(m_copyFileReplacementComponent); 0166 0167 txt = i18n("<p>KPhotoAlbum can make a copy of an image before opening it with an external application. This configuration defines how the new file is named.</p>" 0168 "<p>The regular expression defines the part of the original file name that is replaced with the <i>replacement text</i>. " 0169 "E.g. regexp <i>\"\\.(jpg|png)\"</i> and replacement text <i>\"-mod.\\1\"</i> would copy test.jpg to test-mod.jpg and open the new file in selected application.</p>"); 0170 copyFileComponentLabel->setWhatsThis(txt); 0171 m_copyFileComponent->setWhatsThis(txt); 0172 copyFileReplacementComponentLabel->setWhatsThis(txt); 0173 m_copyFileReplacementComponent->setWhatsThis(txt); 0174 } 0175 0176 // Loader Optimization Setting (prototype) 0177 { 0178 QGroupBox *loadOptimizationBox = new QGroupBox(i18n("EXPERIMENTAL: Tune new image loading for best performance."), this); 0179 topLayout->addWidget(loadOptimizationBox); 0180 QGridLayout *layout = new QGridLayout(loadOptimizationBox); 0181 int row = 0; 0182 0183 // Loader preset 0184 QLabel *loadOptimizationPresetLabel = new QLabel(i18n("Type of media on which your images are stored.")); 0185 layout->addWidget(loadOptimizationPresetLabel, row, 0); 0186 layout->setColumnStretch(0, 1); 0187 0188 m_loadOptimizationPreset = new KComboBox; 0189 m_loadOptimizationPreset->addItems(QStringList() << i18n("Hard Disk") 0190 << i18n("Network") 0191 << i18n("SATA SSD") 0192 << i18n("Slow PCIe/NVMe") 0193 << i18n("Fast PCIe/NVMe") 0194 << i18n("Manual Settings")); // manual is expected to be the last item 0195 layout->addWidget(m_loadOptimizationPreset, row, 1); 0196 0197 txt = i18n("<p>Tune image loading for best performance based on the type of storage your image database resides on. If your image database resides on multiple media, choose the slowest media type used.</p>" 0198 "<p>Use Manual Settings to configure details of how the loading is performed.</p>"); 0199 loadOptimizationPresetLabel->setWhatsThis(txt); 0200 m_loadOptimizationPreset->setWhatsThis(txt); 0201 connect(m_loadOptimizationPreset, QOverload<int>::of(&QComboBox::activated), this, &FileVersionDetectionPage::slotUpdateOptimizationUI); 0202 0203 // Overlap load with MD5 computation 0204 ++row; 0205 QLabel *overlapLoadMD5Label = new QLabel(i18n("Calculate MD5 checksum while images are being preloaded")); 0206 layout->addWidget(overlapLoadMD5Label, row, 0); 0207 m_overlapLoadMD5 = new QCheckBox; 0208 layout->addWidget(m_overlapLoadMD5, row, 1); 0209 0210 txt = i18n("<p>Calculate MD5 checksums while images are being preloaded. This works well if the storage is very fast, such as an NVMe drive. If the storage is slow, this degrades performance as the checksum calculation has to wait longer for the images to be preloaded.</p>"); 0211 overlapLoadMD5Label->setWhatsThis(txt); 0212 m_overlapLoadMD5->setWhatsThis(txt); 0213 0214 // Threads for preload 0215 ++row; 0216 QLabel *preloadThreadCountLabel = new QLabel(i18n("Number of threads to use for preloading (scouting) images")); 0217 layout->addWidget(preloadThreadCountLabel, row, 0); 0218 0219 m_preloadThreadCount = new QSpinBox; 0220 m_preloadThreadCount->setRange(1, 16); 0221 m_preloadThreadCount->setSingleStep(1); 0222 layout->addWidget(m_preloadThreadCount, row, 1); 0223 0224 txt = i18n("<p>Number of threads to use for preloading images to have them in memory when their checksums are calculated. This should generally be set higher for faster storage, but not more than the number of cores in your CPU. Default is 1, which works well for mechanical hard disks.</p>"); 0225 preloadThreadCountLabel->setWhatsThis(txt); 0226 m_preloadThreadCount->setWhatsThis(txt); 0227 0228 // Threads for thumbnailPreload 0229 ++row; 0230 QLabel *thumbnailPreloadThreadCountLabel = new QLabel(i18n("Number of threads to use for thumbnailPreloading (scouting) images")); 0231 layout->addWidget(thumbnailPreloadThreadCountLabel, row, 0); 0232 0233 m_thumbnailPreloadThreadCount = new QSpinBox; 0234 m_thumbnailPreloadThreadCount->setRange(1, 16); 0235 m_thumbnailPreloadThreadCount->setSingleStep(1); 0236 layout->addWidget(m_thumbnailPreloadThreadCount, row, 1); 0237 0238 txt = i18n("<p>Number of threads to use for preloading images prior to building thumbnails. Normally this should be set to 1; the exception might be if you have very fast storage.</p>"); 0239 thumbnailPreloadThreadCountLabel->setWhatsThis(txt); 0240 m_thumbnailPreloadThreadCount->setWhatsThis(txt); 0241 0242 // Threads for thumbnailBuilder 0243 ++row; 0244 QLabel *thumbnailBuilderThreadCountLabel = new QLabel(i18n("Number of threads to use for building thumbnails")); 0245 layout->addWidget(thumbnailBuilderThreadCountLabel, row, 0); 0246 0247 m_thumbnailBuilderThreadCount = new QSpinBox(loadOptimizationBox); 0248 m_thumbnailBuilderThreadCount->setRange(0, 16); 0249 m_thumbnailBuilderThreadCount->setSingleStep(1); 0250 layout->addWidget(m_thumbnailBuilderThreadCount, row, 1); 0251 0252 txt = i18n("<p>Number of threads to use for building thumbnails. If set to 0 this will be set automatically one less than the number of cores, at least one and no more than three. If you have fast storage and a CPU with many cores, you may see benefit from setting this to a larger value.</p>" 0253 "<p>KPhotoAlbum must be restarted for changes to take effect.</p>"); 0254 thumbnailBuilderThreadCountLabel->setWhatsThis(txt); 0255 m_thumbnailBuilderThreadCount->setWhatsThis(txt); 0256 } 0257 0258 auto *scrollArea = new QScrollArea; 0259 mainLayout->addWidget(scrollArea); 0260 scrollArea->setWidget(scrollWidget); 0261 } 0262 0263 Settings::FileVersionDetectionPage::~FileVersionDetectionPage() 0264 { 0265 delete m_searchForImagesOnStart; 0266 delete m_ignoreFileExtension; 0267 delete m_skipSymlinks; 0268 delete m_skipRawIfOtherMatches; 0269 delete m_excludeDirectories; 0270 delete m_detectModifiedFiles; 0271 delete m_modifiedFileComponent; 0272 delete m_originalFileComponent; 0273 delete m_moveOriginalContents; 0274 delete m_autoStackNewFiles; 0275 delete m_copyFileComponent; 0276 delete m_copyFileReplacementComponent; 0277 delete m_loadOptimizationPreset; 0278 delete m_overlapLoadMD5; 0279 delete m_preloadThreadCount; 0280 delete m_thumbnailPreloadThreadCount; 0281 delete m_thumbnailBuilderThreadCount; 0282 } 0283 0284 void Settings::FileVersionDetectionPage::loadSettings(Settings::SettingsData *opt) 0285 { 0286 m_searchForImagesOnStart->setChecked(opt->searchForImagesOnStart()); 0287 m_ignoreFileExtension->setChecked(opt->ignoreFileExtension()); 0288 m_skipSymlinks->setChecked(opt->skipSymlinks()); 0289 m_skipRawIfOtherMatches->setChecked(opt->skipRawIfOtherMatches()); 0290 m_excludeDirectories->setText(opt->excludeDirectories()); 0291 m_detectModifiedFiles->setChecked(opt->detectModifiedFiles()); 0292 m_modifiedFileComponent->setText(opt->modifiedFileComponent()); 0293 m_originalFileComponent->setText(opt->originalFileComponent()); 0294 m_moveOriginalContents->setChecked(opt->moveOriginalContents()); 0295 m_autoStackNewFiles->setChecked(opt->autoStackNewFiles()); 0296 m_copyFileComponent->setText(opt->copyFileComponent()); 0297 m_copyFileReplacementComponent->setText(opt->copyFileReplacementComponent()); 0298 m_loadOptimizationPreset->setCurrentIndex(opt->loadOptimizationPreset()); 0299 m_overlapLoadMD5->setChecked(opt->overlapLoadMD5()); 0300 m_preloadThreadCount->setValue(opt->preloadThreadCount()); 0301 m_thumbnailPreloadThreadCount->setValue(opt->thumbnailPreloadThreadCount()); 0302 m_thumbnailBuilderThreadCount->setValue(opt->thumbnailBuilderThreadCount()); 0303 slotUpdateOptimizationUI(); 0304 } 0305 0306 void Settings::FileVersionDetectionPage::saveSettings(Settings::SettingsData *opt) 0307 { 0308 opt->setSearchForImagesOnStart(m_searchForImagesOnStart->isChecked()); 0309 opt->setIgnoreFileExtension(m_ignoreFileExtension->isChecked()); 0310 opt->setSkipSymlinks(m_skipSymlinks->isChecked()); 0311 opt->setSkipRawIfOtherMatches(m_skipRawIfOtherMatches->isChecked()); 0312 opt->setExcludeDirectories(m_excludeDirectories->text()); 0313 opt->setDetectModifiedFiles(m_detectModifiedFiles->isChecked()); 0314 opt->setModifiedFileComponent(m_modifiedFileComponent->text()); 0315 opt->setOriginalFileComponent(m_originalFileComponent->text()); 0316 opt->setAutoStackNewFiles(m_autoStackNewFiles->isChecked()); 0317 opt->setCopyFileComponent(m_copyFileComponent->text()); 0318 opt->setCopyFileReplacementComponent(m_copyFileReplacementComponent->text()); 0319 opt->setLoadOptimizationPreset(m_loadOptimizationPreset->currentIndex()); 0320 opt->setOverlapLoadMD5(m_overlapLoadMD5->isChecked()); 0321 opt->setPreloadThreadCount(m_preloadThreadCount->value()); 0322 opt->setThumbnailPreloadThreadCount(m_thumbnailPreloadThreadCount->value()); 0323 opt->setThumbnailBuilderThreadCount(m_thumbnailBuilderThreadCount->value()); 0324 } 0325 0326 void Settings::FileVersionDetectionPage::slotUpdateOptimizationUI() 0327 { 0328 const bool manualModeIsSelected = (m_loadOptimizationPreset->currentIndex() + 1 == m_loadOptimizationPreset->count()); 0329 m_overlapLoadMD5->setEnabled(manualModeIsSelected); 0330 m_preloadThreadCount->setEnabled(manualModeIsSelected); 0331 m_thumbnailPreloadThreadCount->setEnabled(manualModeIsSelected); 0332 m_thumbnailBuilderThreadCount->setEnabled(manualModeIsSelected); 0333 } 0334 // vi:expandtab:tabstop=4 shiftwidth=4: