1 /**
2 * \file configdialogpages.cpp
3 * Pages for configuration dialog.
4 *
5 * \b Project: Kid3
6 * \author Urs Fleisch
7 * \date 17 Sep 2003
8 *
9 * Copyright (C) 2003-2018 Urs Fleisch
10 *
11 * This file is part of Kid3.
12 *
13 * Kid3 is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * Kid3 is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "configdialogpages.h"
28 #include <QLayout>
29 #include <QPushButton>
30 #include <QLabel>
31 #include <QString>
32 #include <QCheckBox>
33 #include <QComboBox>
34 #include <QLineEdit>
35 #include <QTabWidget>
36 #include <QListView>
37 #include <QSpinBox>
38 #include <QVBoxLayout>
39 #include <QHBoxLayout>
40 #include <QFormLayout>
41 #include <QGroupBox>
42 #include <QStringListModel>
43 #include <QStandardItemModel>
44 #include "formatconfig.h"
45 #include "filenameformatbox.h"
46 #include "tagformatbox.h"
47 #include "tablemodeledit.h"
48 #include "tagconfig.h"
49 #include "fileconfig.h"
50 #include "useractionsconfig.h"
51 #include "guiconfig.h"
52 #include "networkconfig.h"
53 #include "importconfig.h"
54 #include "stringlistedit.h"
55 #include "stringlisteditdialog.h"
56 #include "configtable.h"
57 #include "commandstablemodel.h"
58 #include "checkablestringlistmodel.h"
59 #include "starratingmappingsmodel.h"
60 #include "fileproxymodel.h"
61 #include "contexthelp.h"
62 #include "frame.h"
63 #include "iplatformtools.h"
64
65 namespace {
66
67 /**
68 * @brief Get string representation of folder patterns.
69 * @param folders folder patterns
70 * @param asteriskIfEmpty if true "*" is returned if @a folders is empty
71 * @return string representation of folder patterns.
72 */
folderPatternListToString(const QStringList & folders,bool asteriskIfEmpty=false)73 QString folderPatternListToString(const QStringList& folders,
74 bool asteriskIfEmpty = false)
75 {
76 QStringList patterns;
77 QChar sep = QLatin1Char(' ');
78 for (const QString& folder : folders) {
79 QString pattern = folder.trimmed();
80 if (!pattern.isEmpty()) {
81 if (pattern.contains(QLatin1Char(' '))) {
82 sep = QLatin1Char(';');
83 }
84 patterns.append(pattern); // clazy:exclude=reserve-candidates
85 }
86 }
87 patterns.removeDuplicates();
88 if (patterns.isEmpty())
89 return QLatin1String(asteriskIfEmpty ? "*" : "");
90
91 // Keep semicolon to mark that space is not a separator.
92 if (patterns.size() == 1 && sep == QLatin1Char(';'))
93 return patterns.first() + sep;
94
95 return patterns.join(sep);
96 }
97
98 /**
99 * @brief Get folder patterns from string representation.
100 * @param patterns string representation of folder patterns
101 * @param asteriskIfEmpty if true an empty list is returned for "*"
102 * @return folder pattern list.
103 */
folderPatternListFromString(const QString & patterns,bool asteriskIfEmpty=false)104 QStringList folderPatternListFromString(const QString& patterns,
105 bool asteriskIfEmpty = false)
106 {
107 if (asteriskIfEmpty && patterns == QLatin1String("*"))
108 return QStringList();
109
110 QStringList folders;
111 const QChar sep = patterns.contains(QLatin1Char(';'))
112 ? QLatin1Char(';') : QLatin1Char(' ');
113 const auto patternList = patterns.split(sep);
114 for (const QString& pattern : patternList) {
115 QString folder = pattern.trimmed();
116 if (!folder.isEmpty()) {
117 folders.append(folder); // clazy:exclude=reserve-candidates
118 }
119 }
120
121 return folders;
122 }
123
124 }
125
126 /**
127 * Constructor.
128 */
ConfigDialogPages(IPlatformTools * platformTools,QObject * parent)129 ConfigDialogPages::ConfigDialogPages(IPlatformTools* platformTools,
130 QObject* parent) : QObject(parent),
131 m_platformTools(platformTools),
132 m_loadLastOpenedFileCheckBox(nullptr), m_preserveTimeCheckBox(nullptr),
133 m_markChangesCheckBox(nullptr), m_coverFileNameLineEdit(nullptr),
134 m_nameFilterComboBox(nullptr), m_includeFoldersLineEdit(nullptr),
135 m_excludeFoldersLineEdit(nullptr), m_showHiddenFilesCheckBox(nullptr),
136 m_fileTextEncodingComboBox(nullptr),
137 m_markTruncationsCheckBox(nullptr), m_textEncodingV1ComboBox(nullptr),
138 m_totalNumTracksCheckBox(nullptr), m_commentNameComboBox(nullptr),
139 m_pictureNameComboBox(nullptr), m_markOversizedPicturesCheckBox(nullptr),
140 m_maximumPictureSizeSpinBox(nullptr), m_genreNotNumericCheckBox(nullptr),
141 m_lowercaseId3ChunkCheckBox(nullptr),
142 m_markStandardViolationsCheckBox(nullptr), m_textEncodingComboBox(nullptr),
143 m_id3v2VersionComboBox(nullptr), m_trackNumberDigitsSpinBox(nullptr),
144 m_fnFormatBox(nullptr), m_tagFormatBox(nullptr),
145 m_onlyCustomGenresCheckBox(nullptr), m_genresEditModel(nullptr),
146 m_quickAccessTagsModel(nullptr), m_starRatingMappingsModel(nullptr),
147 m_trackNameComboBox(nullptr), m_playOnDoubleClickCheckBox(nullptr),
148 m_commandsTable(nullptr), m_commandsTableModel(nullptr),
149 m_browserLineEdit(nullptr), m_proxyCheckBox(nullptr),
150 m_proxyLineEdit(nullptr), m_proxyAuthenticationCheckBox(nullptr),
151 m_proxyUserNameLineEdit(nullptr), m_proxyPasswordLineEdit(nullptr),
152 m_enabledMetadataPluginsModel(nullptr), m_enabledPluginsModel(nullptr)
153 {
154 }
155
156 /**
157 * Create page with tags settings.
158 * @return tags page.
159 */
createTagsPage()160 QWidget* ConfigDialogPages::createTagsPage()
161 {
162 QWidget* tagsPage = new QWidget;
163 auto vlayout = new QVBoxLayout(tagsPage);
164
165 QWidget* tag1Page = new QWidget;
166 auto tag1Layout = new QVBoxLayout(tag1Page);
167 QGroupBox* v1GroupBox = new QGroupBox(tr("ID3v1"), tag1Page);
168 auto v1GroupBoxLayout = new QGridLayout(v1GroupBox);
169 m_markTruncationsCheckBox = new QCheckBox(
170 tr("&Mark truncated fields"), v1GroupBox);
171 v1GroupBoxLayout->addWidget(m_markTruncationsCheckBox, 0, 0, 1, 2);
172 QLabel* textEncodingV1Label = new QLabel(tr("Text &encoding:"), v1GroupBox);
173 m_textEncodingV1ComboBox = new QComboBox(v1GroupBox);
174 m_textEncodingV1ComboBox->addItems(TagConfig::getTextCodecNames());
175 m_textEncodingV1ComboBox->setSizePolicy(
176 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
177 textEncodingV1Label->setBuddy(m_textEncodingV1ComboBox);
178 v1GroupBoxLayout->addWidget(textEncodingV1Label, 1, 0);
179 v1GroupBoxLayout->addWidget(m_textEncodingV1ComboBox, 1, 1);
180 tag1Layout->addWidget(v1GroupBox);
181 tag1Layout->addStretch();
182
183 QWidget* tag2Page = new QWidget;
184 auto tag2Layout = new QHBoxLayout(tag2Page);
185 auto tag2LeftLayout = new QVBoxLayout;
186 QGroupBox* v2GroupBox = new QGroupBox(tr("ID3v2"), tag2Page);
187 auto v2GroupBoxLayout = new QGridLayout(v2GroupBox);
188 m_totalNumTracksCheckBox = new QCheckBox(
189 tr("Use &track/total number of tracks format"), v2GroupBox);
190 v2GroupBoxLayout->addWidget(m_totalNumTracksCheckBox, 0, 0, 1, 2);
191 QLabel* trackNumberDigitsLabel = new QLabel(
192 tr("Track number &digits:"), v2GroupBox);
193 m_trackNumberDigitsSpinBox = new QSpinBox(v2GroupBox);
194 m_trackNumberDigitsSpinBox->setMaximum(5);
195 m_genreNotNumericCheckBox = new QCheckBox(
196 tr("&Genre as text instead of numeric string"), v2GroupBox);
197 m_lowercaseId3ChunkCheckBox = new QCheckBox(
198 tr("&WAV files with lowercase id3 chunk"), v2GroupBox);
199 m_markStandardViolationsCheckBox =
200 new QCheckBox(tr("Mar&k standard violations"));
201 QLabel* textEncodingLabel = new QLabel(tr("Text &encoding:"), v2GroupBox);
202 m_textEncodingComboBox = new QComboBox(v2GroupBox);
203 m_textEncodingComboBox->addItems(TagConfig::getTextEncodingNames());
204 m_textEncodingComboBox->setSizePolicy(
205 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
206 textEncodingLabel->setBuddy(m_textEncodingComboBox);
207 v2GroupBoxLayout->addWidget(m_genreNotNumericCheckBox, 1, 0, 1, 2);
208 v2GroupBoxLayout->addWidget(m_lowercaseId3ChunkCheckBox, 2, 0, 1, 2);
209 v2GroupBoxLayout->addWidget(m_markStandardViolationsCheckBox, 3, 0, 1, 2);
210 v2GroupBoxLayout->addWidget(textEncodingLabel, 4, 0);
211 v2GroupBoxLayout->addWidget(m_textEncodingComboBox, 4, 1);
212 const TagConfig& tagCfg = TagConfig::instance();
213 if (!(tagCfg.taggedFileFeatures() &
214 (TaggedFile::TF_ID3v22 | TaggedFile::TF_ID3v23 | TaggedFile::TF_ID3v24))) {
215 m_genreNotNumericCheckBox->hide();
216 textEncodingLabel->hide();
217 m_textEncodingComboBox->hide();
218 }
219 QLabel* id3v2VersionLabel = new QLabel(tr("&Version used for new tags:"),
220 v2GroupBox);
221 m_id3v2VersionComboBox = new QComboBox(v2GroupBox);
222 if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v23)
223 m_id3v2VersionComboBox->addItem(tr("ID3v2.3.0"), TagConfig::ID3v2_3_0);
224 if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v24)
225 m_id3v2VersionComboBox->addItem(tr("ID3v2.4.0"), TagConfig::ID3v2_4_0);
226 m_id3v2VersionComboBox->setSizePolicy(
227 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
228 id3v2VersionLabel->setBuddy(m_id3v2VersionComboBox);
229 v2GroupBoxLayout->addWidget(id3v2VersionLabel, 5, 0);
230 v2GroupBoxLayout->addWidget(m_id3v2VersionComboBox, 5, 1);
231 if (m_id3v2VersionComboBox->count() < 2) {
232 id3v2VersionLabel->hide();
233 m_id3v2VersionComboBox->hide();
234 }
235 trackNumberDigitsLabel->setBuddy(m_trackNumberDigitsSpinBox);
236 v2GroupBoxLayout->addWidget(trackNumberDigitsLabel, 6, 0);
237 v2GroupBoxLayout->addWidget(m_trackNumberDigitsSpinBox, 6, 1);
238 tag2LeftLayout->addWidget(v2GroupBox);
239 QGroupBox* vorbisGroupBox = new QGroupBox(tr("Ogg/Vorbis"), tag2Page);
240 QLabel* commentNameLabel = new QLabel(tr("Co&mment field name:"),
241 vorbisGroupBox);
242 m_commentNameComboBox = new QComboBox(vorbisGroupBox);
243 QLabel* pictureNameLabel = new QLabel(tr("&Picture field name:"),
244 vorbisGroupBox);
245 m_pictureNameComboBox = new QComboBox(vorbisGroupBox);
246 m_commentNameComboBox->setEditable(true);
247 m_commentNameComboBox->addItems(TagConfig::getCommentNames());
248 m_commentNameComboBox->setSizePolicy(
249 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
250 commentNameLabel->setBuddy(m_commentNameComboBox);
251 m_pictureNameComboBox->addItems(TagConfig::getPictureNames());
252 m_pictureNameComboBox->setSizePolicy(
253 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
254 pictureNameLabel->setBuddy(m_pictureNameComboBox);
255 auto vorbisGroupBoxLayout = new QGridLayout(vorbisGroupBox);
256 vorbisGroupBoxLayout->addWidget(commentNameLabel, 0, 0);
257 vorbisGroupBoxLayout->addWidget(m_commentNameComboBox, 0, 1);
258 vorbisGroupBoxLayout->addWidget(pictureNameLabel, 1, 0);
259 vorbisGroupBoxLayout->addWidget(m_pictureNameComboBox, 1, 1);
260 vorbisGroupBox->setLayout(vorbisGroupBoxLayout);
261 tag2LeftLayout->addWidget(vorbisGroupBox);
262 if (!(tagCfg.taggedFileFeatures() & TaggedFile::TF_OggPictures)) {
263 vorbisGroupBox->hide();
264 }
265 QGroupBox* pictureGroupBox = new QGroupBox(tr("Picture"), tag2Page);
266 auto pictureGroupBoxLayout = new QHBoxLayout(pictureGroupBox);
267 m_markOversizedPicturesCheckBox =
268 new QCheckBox(tr("Mark if &larger than (bytes):"));
269 m_maximumPictureSizeSpinBox = new QSpinBox;
270 m_maximumPictureSizeSpinBox->setRange(0, INT_MAX);
271 pictureGroupBoxLayout->addWidget(m_markOversizedPicturesCheckBox);
272 pictureGroupBoxLayout->addWidget(m_maximumPictureSizeSpinBox);
273 tag2LeftLayout->addWidget(pictureGroupBox);
274 tag2LeftLayout->addStretch();
275 tag2Layout->addLayout(tag2LeftLayout);
276
277 auto tag2RightLayout = new QVBoxLayout;
278 QGroupBox* genresGroupBox = new QGroupBox(tr("Custom &Genres"), tag2Page);
279 m_onlyCustomGenresCheckBox = new QCheckBox(tr("&Show only custom genres"),
280 genresGroupBox);
281 m_genresEditModel = new QStringListModel(genresGroupBox);
282 auto genresEdit = new StringListEdit(m_genresEditModel, genresGroupBox);
283 auto vbox = new QVBoxLayout;
284 vbox->addWidget(m_onlyCustomGenresCheckBox);
285 vbox->addWidget(genresEdit);
286 genresGroupBox->setLayout(vbox);
287 tag2RightLayout->addWidget(genresGroupBox);
288
289 QGroupBox* quickAccessTagsGroupBox = new QGroupBox(tr("&Quick Access Frames"));
290 auto quickAccessTagsLayout = new QVBoxLayout(quickAccessTagsGroupBox);
291 auto quickAccessTagsListView = new QListView;
292 m_quickAccessTagsModel = new QStandardItemModel(quickAccessTagsGroupBox);
293 quickAccessTagsListView->setModel(m_quickAccessTagsModel);
294 quickAccessTagsListView->setAcceptDrops(true);
295 quickAccessTagsListView->setDragEnabled(true);
296 quickAccessTagsListView->setDragDropMode(QAbstractItemView::InternalMove);
297 quickAccessTagsListView->setDragDropOverwriteMode(false);
298 quickAccessTagsListView->setDefaultDropAction(Qt::MoveAction);
299 quickAccessTagsListView->setDropIndicatorShown(true);
300 quickAccessTagsLayout->addWidget(quickAccessTagsListView);
301 QLabel* reorderLabel =
302 new QLabel(tr("Use drag and drop to reorder the items"));
303 reorderLabel->setWordWrap(true);
304 quickAccessTagsLayout->addWidget(reorderLabel);
305 tag2RightLayout->addWidget(quickAccessTagsGroupBox);
306 tag2Layout->addLayout(tag2RightLayout);
307
308 QWidget* tag3Page = new QWidget;
309 auto tag3Layout = new QVBoxLayout(tag3Page);
310 QGroupBox* riffGroupBox = new QGroupBox(tr("RIFF INFO"), tag3Page);
311 QLabel* trackNameLabel = new QLabel(tr("Track nu&mber field name:"),
312 riffGroupBox);
313 m_trackNameComboBox = new QComboBox(riffGroupBox);
314 m_trackNameComboBox->setEditable(true);
315 m_trackNameComboBox->addItems(TagConfig::getRiffTrackNames());
316 m_trackNameComboBox->setSizePolicy(
317 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
318 trackNameLabel->setBuddy(m_trackNameComboBox);
319 auto riffGroupBoxLayout = new QGridLayout(riffGroupBox);
320 riffGroupBoxLayout->addWidget(trackNameLabel, 0, 0);
321 riffGroupBoxLayout->addWidget(m_trackNameComboBox, 0, 1);
322 riffGroupBox->setLayout(riffGroupBoxLayout);
323 tag3Layout->addWidget(riffGroupBox);
324 tag3Layout->addStretch();
325
326 QWidget* tag1AndTag2Page = new QWidget;
327 auto tag1AndTag2Layout = new QVBoxLayout(tag1AndTag2Page);
328 QString tagFormatTitle(tr("&Tag Format"));
329 m_tagFormatBox = new TagFormatBox(tagFormatTitle, tag1AndTag2Page);
330 QGroupBox* ratingGroupBox = new QGroupBox(tr("Rating"), tag1AndTag2Page);
331 auto ratingLayout = new QVBoxLayout(ratingGroupBox);
332 m_starRatingMappingsModel = new StarRatingMappingsModel(ratingGroupBox);
333 auto ratingEdit = new TableModelEdit(m_starRatingMappingsModel);
334 ratingLayout->addWidget(ratingEdit);
335 tag1AndTag2Layout->addWidget(m_tagFormatBox);
336 tag1AndTag2Layout->addWidget(ratingGroupBox);
337
338 auto tagsTabWidget = new QTabWidget;
339 if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v11) {
340 tagsTabWidget->addTab(tag1Page, tr("Tag &1"));
341 }
342 tagsTabWidget->addTab(tag2Page, tr("Tag &2"));
343 tagsTabWidget->addTab(tag3Page, tr("Tag &3"));
344 tagsTabWidget->addTab(tag1AndTag2Page, tr("All Ta&gs"));
345 tagsTabWidget->setCurrentIndex(1);
346 vlayout->addWidget(tagsTabWidget);
347 return tagsPage;
348 }
349
350 /**
351 * Create page with files settings.
352 * @return files page.
353 */
createFilesPage()354 QWidget* ConfigDialogPages::createFilesPage()
355 {
356 QWidget* filesPage = new QWidget;
357 auto vlayout = new QVBoxLayout(filesPage);
358 auto hlayout = new QHBoxLayout;
359 auto leftLayout = new QVBoxLayout;
360 auto rightLayout = new QVBoxLayout;
361
362 QGroupBox* startupGroupBox = new QGroupBox(tr("Startup"), filesPage);
363 m_loadLastOpenedFileCheckBox = new QCheckBox(tr("&Load last-opened files"),
364 startupGroupBox);
365 auto startupLayout = new QVBoxLayout;
366 startupLayout->addWidget(m_loadLastOpenedFileCheckBox);
367 startupGroupBox->setLayout(startupLayout);
368 leftLayout->addWidget(startupGroupBox);
369 QGroupBox* saveGroupBox = new QGroupBox(tr("Save"), filesPage);
370 m_preserveTimeCheckBox = new QCheckBox(tr("&Preserve file timestamp"),
371 saveGroupBox);
372 m_markChangesCheckBox = new QCheckBox(tr("&Mark changes"), saveGroupBox);
373 m_coverFileNameLineEdit = new QLineEdit(saveGroupBox);
374 m_fileTextEncodingComboBox = new QComboBox(saveGroupBox);
375 m_fileTextEncodingComboBox->addItems(FileConfig::getTextCodecNames());
376 m_fileTextEncodingComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
377 QSizePolicy::Minimum));
378 auto formLayout = new QFormLayout;
379 formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
380 formLayout->addRow(m_preserveTimeCheckBox);
381 formLayout->addRow(m_markChangesCheckBox);
382 formLayout->addRow(tr("F&ilename for cover:"), m_coverFileNameLineEdit);
383 formLayout->addRow(tr("Text &encoding (Export, Playlist):"),
384 m_fileTextEncodingComboBox);
385 saveGroupBox->setLayout(formLayout);
386 leftLayout->addWidget(saveGroupBox);
387
388 QGroupBox* fileListGroupBox = new QGroupBox(tr("File List"), filesPage);
389 QLabel* nameFilterLabel = new QLabel(tr("Filte&r:"), fileListGroupBox);
390 m_nameFilterComboBox = new QComboBox(fileListGroupBox);
391 m_nameFilterComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
392 QSizePolicy::Minimum));
393 QList<QPair<QString, QString> > filters = FileProxyModel::createNameFilters();
394 for (auto it = filters.constBegin(); it != filters.constEnd(); ++it) {
395 QString nameFilter = m_platformTools->fileDialogNameFilter({*it});
396 m_nameFilterComboBox->addItem(it->first, nameFilter);
397 }
398 nameFilterLabel->setBuddy(m_nameFilterComboBox);
399 QLabel* includeFoldersLabel = new QLabel(tr("Inclu&de folders:"),
400 fileListGroupBox);
401 m_includeFoldersLineEdit = new QLineEdit(fileListGroupBox);
402 includeFoldersLabel->setBuddy(m_includeFoldersLineEdit);
403 QLabel* excludeFoldersLabel = new QLabel(tr("E&xclude folders:"),
404 fileListGroupBox);
405 m_excludeFoldersLineEdit = new QLineEdit(fileListGroupBox);
406 excludeFoldersLabel->setBuddy(m_excludeFoldersLineEdit);
407 m_showHiddenFilesCheckBox = new QCheckBox(tr("&Show hidden files"),
408 fileListGroupBox);
409 auto fileListGroupBoxLayout = new QGridLayout(fileListGroupBox);
410 fileListGroupBoxLayout->addWidget(nameFilterLabel, 0, 0);
411 fileListGroupBoxLayout->addWidget(m_nameFilterComboBox, 0, 1);
412 fileListGroupBoxLayout->addWidget(includeFoldersLabel, 1, 0);
413 fileListGroupBoxLayout->addWidget(m_includeFoldersLineEdit, 1, 1);
414 fileListGroupBoxLayout->addWidget(excludeFoldersLabel, 2, 0);
415 fileListGroupBoxLayout->addWidget(m_excludeFoldersLineEdit, 2, 1);
416 fileListGroupBoxLayout->addWidget(m_showHiddenFilesCheckBox, 3, 0, 1, 2);
417 rightLayout->addWidget(fileListGroupBox);
418
419 auto formatGroupBox = new QGroupBox(tr("Format"), filesPage);
420 auto formatLayout = new QHBoxLayout(formatGroupBox);
421 auto editFormatsFromTagButton =
422 new QPushButton(tr("Filename from tag") + QLatin1String("..."));
423 connect(editFormatsFromTagButton, &QPushButton::clicked,
424 this, &ConfigDialogPages::editFormatsFromTag);
425 formatLayout->addWidget(editFormatsFromTagButton);
426 auto editFormatsToTagButton =
427 new QPushButton(tr("Tag from filename") + QLatin1String("..."));
428 connect(editFormatsToTagButton, &QPushButton::clicked,
429 this, &ConfigDialogPages::editFormatsToTag);
430 formatLayout->addWidget(editFormatsToTagButton);
431 rightLayout->addWidget(formatGroupBox);
432
433 rightLayout->addStretch();
434
435 hlayout->addLayout(leftLayout);
436 hlayout->addLayout(rightLayout);
437 vlayout->addLayout(hlayout);
438
439 QString fnFormatTitle(tr("&Filename Format"));
440 m_fnFormatBox = new FilenameFormatBox(fnFormatTitle, filesPage);
441 vlayout->addWidget(m_fnFormatBox);
442 return filesPage;
443 }
444
445 /**
446 * Open dialog to edit "Filename from tag" formats.
447 */
editFormatsFromTag()448 void ConfigDialogPages::editFormatsFromTag()
449 {
450 QWidget* parentWindow = nullptr;
451 if (auto button = qobject_cast<QPushButton*>(sender())) {
452 parentWindow = button->window();
453 }
454 StringListEditDialog dialog(
455 m_toFilenameFormats, tr("Filename from Tag"), parentWindow);
456 if (dialog.exec() == QDialog::Accepted) {
457 m_toFilenameFormats = dialog.stringList();
458 }
459 }
460
461 /**
462 * Open dialog to edit "Filename to tag" formats.
463 */
editFormatsToTag()464 void ConfigDialogPages::editFormatsToTag()
465 {
466 QWidget* parentWindow = nullptr;
467 if (auto button = qobject_cast<QPushButton*>(sender())) {
468 parentWindow = button->window();
469 }
470 StringListEditDialog dialog(
471 m_fromFilenameFormats, tr("Tag from Filename"), parentWindow);
472 if (dialog.exec() == QDialog::Accepted) {
473 m_fromFilenameFormats = dialog.stringList();
474 }
475 }
476
477 /**
478 * Create page with actions settings.
479 * @return actions page.
480 */
createActionsPage()481 QWidget* ConfigDialogPages::createActionsPage()
482 {
483 QWidget* actionsPage = new QWidget;
484 auto vlayout = new QVBoxLayout(actionsPage);
485 QGroupBox* browserGroupBox = new QGroupBox(tr("Browser"), actionsPage);
486 QLabel* browserLabel = new QLabel(tr("Web &browser:"), browserGroupBox);
487 m_browserLineEdit = new QLineEdit(browserGroupBox);
488 browserLabel->setBuddy(m_browserLineEdit);
489 auto hbox = new QHBoxLayout;
490 hbox->addWidget(browserLabel);
491 hbox->addWidget(m_browserLineEdit);
492 browserGroupBox->setLayout(hbox);
493 vlayout->addWidget(browserGroupBox);
494
495 QGroupBox* commandsGroupBox = new QGroupBox(tr("Context &Menu Commands"),
496 actionsPage);
497 m_playOnDoubleClickCheckBox =
498 new QCheckBox(tr("&Play on double click"), commandsGroupBox);
499 m_commandsTableModel = new CommandsTableModel(commandsGroupBox);
500 m_commandsTable = new ConfigTable(m_commandsTableModel, commandsGroupBox);
501 m_commandsTable->setHorizontalResizeModes(
502 m_commandsTableModel->getHorizontalResizeModes());
503 auto commandsLayout = new QVBoxLayout;
504 commandsLayout->addWidget(m_playOnDoubleClickCheckBox);
505 commandsLayout->addWidget(m_commandsTable);
506 commandsGroupBox->setLayout(commandsLayout);
507 vlayout->addWidget(commandsGroupBox);
508 return actionsPage;
509 }
510
511 /**
512 * Create page with network settings.
513 * @return network page.
514 */
createNetworkPage()515 QWidget* ConfigDialogPages::createNetworkPage()
516 {
517 QWidget* networkPage = new QWidget;
518 auto vlayout = new QVBoxLayout(networkPage);
519 QGroupBox* proxyGroupBox = new QGroupBox(tr("Proxy"), networkPage);
520 m_proxyCheckBox = new QCheckBox(tr("&Proxy:"), proxyGroupBox);
521 m_proxyLineEdit = new QLineEdit(proxyGroupBox);
522 m_proxyAuthenticationCheckBox = new QCheckBox(
523 tr("&Use authentication with proxy"), proxyGroupBox);
524 QLabel* proxyUserNameLabel =
525 new QLabel(tr("Proxy user &name:"), proxyGroupBox);
526 m_proxyUserNameLineEdit = new QLineEdit(proxyGroupBox);
527 proxyUserNameLabel->setBuddy(m_proxyUserNameLineEdit);
528 QLabel* proxyPasswordLabel =
529 new QLabel(tr("Proxy pass&word:"), proxyGroupBox);
530 m_proxyPasswordLineEdit = new QLineEdit(proxyGroupBox);
531 proxyPasswordLabel->setBuddy(m_proxyPasswordLineEdit);
532 m_proxyPasswordLineEdit->setEchoMode(QLineEdit::Password);
533 auto vbox = new QVBoxLayout;
534 auto proxyHbox = new QHBoxLayout;
535 proxyHbox->addWidget(m_proxyCheckBox);
536 proxyHbox->addWidget(m_proxyLineEdit);
537 vbox->addLayout(proxyHbox);
538 vbox->addWidget(m_proxyAuthenticationCheckBox);
539 auto authLayout = new QGridLayout;
540 authLayout->addWidget(proxyUserNameLabel, 0, 0);
541 authLayout->addWidget(m_proxyUserNameLineEdit, 0, 1);
542 authLayout->addWidget(proxyPasswordLabel, 1, 0);
543 authLayout->addWidget(m_proxyPasswordLineEdit, 1, 1);
544 vbox->addLayout(authLayout);
545 proxyGroupBox->setLayout(vbox);
546 vlayout->addWidget(proxyGroupBox);
547
548 auto vspacer = new QSpacerItem(0, 0,
549 QSizePolicy::Minimum, QSizePolicy::Expanding);
550 vlayout->addItem(vspacer);
551 return networkPage;
552 }
553
554 /**
555 * Create page with plugins settings.
556 * @return plugins page.
557 */
createPluginsPage()558 QWidget* ConfigDialogPages::createPluginsPage()
559 {
560 QWidget* pluginsPage = new QWidget;
561 auto vlayout = new QVBoxLayout(pluginsPage);
562 QGroupBox* metadataGroupBox = new QGroupBox(
563 tr("&Metadata Plugins && Priority"), pluginsPage);
564
565
566 auto metadataPluginsLayout = new QVBoxLayout(metadataGroupBox);
567 m_enabledMetadataPluginsModel =
568 new CheckableStringListModel(metadataGroupBox);
569 auto metadataEdit =
570 new StringListEdit(m_enabledMetadataPluginsModel, metadataGroupBox);
571 metadataEdit->setEditingDisabled(true);
572 metadataPluginsLayout->addWidget(metadataEdit);
573 vlayout->addWidget(metadataGroupBox);
574
575 QGroupBox* pluginsGroupBox = new QGroupBox(tr("A&vailable Plugins"));
576 auto pluginsLayout = new QVBoxLayout(pluginsGroupBox);
577 auto pluginsListView = new QListView;
578 pluginsListView->setSelectionMode(QAbstractItemView::NoSelection);
579 m_enabledPluginsModel = new CheckableStringListModel(pluginsGroupBox);
580 pluginsListView->setModel(m_enabledPluginsModel);
581 pluginsLayout->addWidget(pluginsListView);
582 vlayout->addWidget(pluginsGroupBox);
583
584 vlayout->addStretch();
585 vlayout->addWidget(
586 new QLabel(tr("Changes take only effect after a restart!")));
587 return pluginsPage;
588 }
589
590 /**
591 * Set values in pages from default configuration.
592 */
setDefaultConfig()593 void ConfigDialogPages::setDefaultConfig()
594 {
595 FilenameFormatConfig fnCfg;
596 TagFormatConfig id3Cfg;
597 TagConfig tagCfg;
598 tagCfg.setAvailablePlugins(TagConfig::instance().availablePlugins());
599 tagCfg.setTaggedFileFeatures(TagConfig::instance().taggedFileFeatures());
600 tagCfg.setDefaultPluginOrder();
601 FileConfig fileCfg;
602 UserActionsConfig userActionsCfg;
603 userActionsCfg.setDefaultUserActions();
604 GuiConfig guiCfg;
605 NetworkConfig networkCfg;
606 networkCfg.setDefaultBrowser();
607 ImportConfig importCfg;
608 importCfg.setAvailablePlugins(ImportConfig::instance().availablePlugins());
609 setConfigs(fnCfg, id3Cfg, tagCfg, fileCfg, userActionsCfg, guiCfg, networkCfg,
610 importCfg);
611 }
612
613 /**
614 * Set values in dialog from current configuration.
615 */
setConfig()616 void ConfigDialogPages::setConfig()
617 {
618 const FormatConfig& fnCfg = FilenameFormatConfig::instance();
619 const FormatConfig& id3Cfg = TagFormatConfig::instance();
620 const TagConfig& tagCfg = TagConfig::instance();
621 const FileConfig& fileCfg = FileConfig::instance();
622 const UserActionsConfig& userActionsCfg = UserActionsConfig::instance();
623 const GuiConfig& guiCfg = GuiConfig::instance();
624 const NetworkConfig& networkCfg = NetworkConfig::instance();
625 const ImportConfig& importCfg = ImportConfig::instance();
626 setConfigs(fnCfg, id3Cfg, tagCfg, fileCfg, userActionsCfg, guiCfg, networkCfg,
627 importCfg);
628 }
629
630 /**
631 * Set values in dialog from given configurations.
632 */
setConfigs(const FormatConfig & fnCfg,const FormatConfig & id3Cfg,const TagConfig & tagCfg,const FileConfig & fileCfg,const UserActionsConfig & userActionsCfg,const GuiConfig & guiCfg,const NetworkConfig & networkCfg,const ImportConfig & importCfg)633 void ConfigDialogPages::setConfigs(
634 const FormatConfig& fnCfg, const FormatConfig& id3Cfg,
635 const TagConfig& tagCfg, const FileConfig& fileCfg,
636 const UserActionsConfig& userActionsCfg, const GuiConfig& guiCfg,
637 const NetworkConfig& networkCfg, const ImportConfig& importCfg)
638 {
639 m_fnFormatBox->fromFormatConfig(fnCfg);
640 m_tagFormatBox->fromFormatConfig(id3Cfg);
641 m_markTruncationsCheckBox->setChecked(tagCfg.markTruncations());
642 m_totalNumTracksCheckBox->setChecked(tagCfg.enableTotalNumberOfTracks());
643 m_loadLastOpenedFileCheckBox->setChecked(fileCfg.loadLastOpenedFile());
644 m_preserveTimeCheckBox->setChecked(fileCfg.preserveTime());
645 m_markChangesCheckBox->setChecked(fileCfg.markChanges());
646 m_coverFileNameLineEdit->setText(fileCfg.defaultCoverFileName());
647 m_nameFilterComboBox->setCurrentIndex(
648 m_nameFilterComboBox->findData(fileCfg.nameFilter()));
649 m_includeFoldersLineEdit->setText(
650 folderPatternListToString(fileCfg.includeFolders(), true));
651 m_excludeFoldersLineEdit->setText(
652 folderPatternListToString(fileCfg.excludeFolders(), false));
653 m_showHiddenFilesCheckBox->setChecked(fileCfg.showHiddenFiles());
654 m_fileTextEncodingComboBox->setCurrentIndex(fileCfg.textEncodingIndex());
655 m_toFilenameFormats = fileCfg.toFilenameFormats();
656 m_fromFilenameFormats = fileCfg.fromFilenameFormats();
657 m_onlyCustomGenresCheckBox->setChecked(tagCfg.onlyCustomGenres());
658 m_genresEditModel->setStringList(tagCfg.customGenres());
659 m_starRatingMappingsModel->setMappings(tagCfg.starRatingMappings());
660 quint64 frameMask = tagCfg.quickAccessFrames();
661 QList<int> frameTypes = tagCfg.quickAccessFrameOrder();
662 if (frameTypes.size() != Frame::FT_LastFrame + 1) {
663 frameTypes.clear();
664 frameTypes.reserve(Frame::FT_LastFrame - Frame::FT_FirstFrame + 1);
665 for (int i = Frame::FT_FirstFrame; i <= Frame::FT_LastFrame; ++i) {
666 frameTypes.append(i);
667 }
668 }
669 m_quickAccessTagsModel->clear();
670 const auto constFrameTypes = frameTypes;
671 for (int frameType : constFrameTypes) {
672 QStandardItem* item = new QStandardItem(
673 Frame::ExtendedType(static_cast<Frame::Type>(frameType))
674 .getTranslatedName());
675 item->setData(frameType, Qt::UserRole);
676 item->setCheckable(true);
677 item->setCheckState((frameMask & (1ULL << frameType))
678 ? Qt::Checked : Qt::Unchecked);
679 item->setDropEnabled(false);
680 m_quickAccessTagsModel->appendRow(item);
681 }
682 m_commandsTableModel->setCommandList(userActionsCfg.contextMenuCommands());
683 int idx = m_commentNameComboBox->findText(tagCfg.commentName());
684 if (idx >= 0) {
685 m_commentNameComboBox->setCurrentIndex(idx);
686 } else {
687 m_commentNameComboBox->addItem(tagCfg.commentName());
688 m_commentNameComboBox->setCurrentIndex(m_commentNameComboBox->count() - 1);
689 }
690 m_pictureNameComboBox->setCurrentIndex(tagCfg.pictureNameIndex());
691 m_genreNotNumericCheckBox->setChecked(tagCfg.genreNotNumeric());
692 m_lowercaseId3ChunkCheckBox->setChecked(tagCfg.lowercaseId3RiffChunk());
693 m_markStandardViolationsCheckBox->setChecked(tagCfg.markStandardViolations());
694 m_textEncodingV1ComboBox->setCurrentIndex(tagCfg.textEncodingV1Index());
695 m_textEncodingComboBox->setCurrentIndex(tagCfg.textEncoding());
696 m_id3v2VersionComboBox->setCurrentIndex(
697 m_id3v2VersionComboBox->findData(tagCfg.id3v2Version()));
698 m_trackNumberDigitsSpinBox->setValue(tagCfg.trackNumberDigits());
699 m_markOversizedPicturesCheckBox->setChecked(tagCfg.markOversizedPictures());
700 m_maximumPictureSizeSpinBox->setValue(tagCfg.maximumPictureSize());
701 idx = m_trackNameComboBox->findText(tagCfg.riffTrackName());
702 if (idx >= 0) {
703 m_trackNameComboBox->setCurrentIndex(idx);
704 } else {
705 m_trackNameComboBox->addItem(tagCfg.riffTrackName());
706 m_trackNameComboBox->setCurrentIndex(m_trackNameComboBox->count() - 1);
707 }
708 m_browserLineEdit->setText(networkCfg.browser());
709 m_playOnDoubleClickCheckBox->setChecked(guiCfg.playOnDoubleClick());
710 m_proxyCheckBox->setChecked(networkCfg.useProxy());
711 m_proxyLineEdit->setText(networkCfg.proxy());
712 m_proxyAuthenticationCheckBox->setChecked(networkCfg.useProxyAuthentication());
713 m_proxyUserNameLineEdit->setText(networkCfg.proxyUserName());
714 m_proxyPasswordLineEdit->setText(networkCfg.proxyPassword());
715
716 QStringList metadataPlugins;
717 QStringList pluginOrder = tagCfg.pluginOrder();
718 if (!pluginOrder.isEmpty()) {
719 for (int i = 0; i < pluginOrder.size(); ++i) {
720 metadataPlugins.append(QString()); // clazy:exclude=reserve-candidates
721 }
722 const auto pluginNames = tagCfg.availablePlugins();
723 for (const QString& pluginName : pluginNames) {
724 int pluginIdx = pluginOrder.indexOf(pluginName);
725 if (pluginIdx >= 0) {
726 metadataPlugins[pluginIdx] = pluginName;
727 } else {
728 metadataPlugins.append(pluginName); // clazy:exclude=reserve-candidates
729 }
730 }
731 metadataPlugins.removeAll(QString());
732 } else {
733 metadataPlugins = tagCfg.availablePlugins();
734 }
735 quint64 metadataPluginsMask = 0;
736 quint64 mask = 1;
737 QStringList disabledTagPlugins = tagCfg.disabledPlugins();
738 for (int i = 0; i < metadataPlugins.size(); ++i, mask <<= 1) {
739 if (!disabledTagPlugins.contains(metadataPlugins.at(i))) {
740 metadataPluginsMask |= mask;
741 }
742 }
743 m_enabledMetadataPluginsModel->setStringList(metadataPlugins);
744 m_enabledMetadataPluginsModel->setBitMask(metadataPluginsMask);
745
746 QStringList importPlugins = importCfg.availablePlugins();
747 quint64 importPluginsMask = 0;
748 mask = 1;
749 QStringList disabledPlugins = importCfg.disabledPlugins();
750 for (int i = 0; i < importPlugins.size(); ++i, mask <<= 1) {
751 if (!disabledPlugins.contains(importPlugins.at(i))) {
752 importPluginsMask |= mask;
753 }
754 }
755 m_enabledPluginsModel->setStringList(importPlugins);
756 m_enabledPluginsModel->setBitMask(importPluginsMask);
757
758 if (!guiCfg.configWindowGeometry().isEmpty()) {
759 if (QWidget* configDialog = qobject_cast<QWidget*>(parent())) {
760 configDialog->restoreGeometry(guiCfg.configWindowGeometry());
761 }
762 }
763 }
764
765 /**
766 * Get values from dialog and store them in the current configuration.
767 */
getConfig() const768 void ConfigDialogPages::getConfig() const
769 {
770 FormatConfig& fnCfg = FilenameFormatConfig::instance();
771 FormatConfig& id3Cfg = TagFormatConfig::instance();
772 TagConfig& tagCfg = TagConfig::instance();
773 FileConfig& fileCfg = FileConfig::instance();
774 UserActionsConfig& userActionsCfg = UserActionsConfig::instance();
775 GuiConfig& guiCfg = GuiConfig::instance();
776 NetworkConfig& networkCfg = NetworkConfig::instance();
777 ImportConfig& importCfg = ImportConfig::instance();
778
779 m_fnFormatBox->toFormatConfig(fnCfg);
780 m_tagFormatBox->toFormatConfig(id3Cfg);
781 tagCfg.setMarkTruncations(m_markTruncationsCheckBox->isChecked());
782 tagCfg.setEnableTotalNumberOfTracks(m_totalNumTracksCheckBox->isChecked());
783 fileCfg.setLoadLastOpenedFile(m_loadLastOpenedFileCheckBox->isChecked());
784 fileCfg.setPreserveTime(m_preserveTimeCheckBox->isChecked());
785 fileCfg.setMarkChanges(m_markChangesCheckBox->isChecked());
786 fileCfg.setDefaultCoverFileName(m_coverFileNameLineEdit->text());
787 fileCfg.setNameFilter(m_nameFilterComboBox->currentData().toString());
788 fileCfg.setIncludeFolders(
789 folderPatternListFromString(m_includeFoldersLineEdit->text(), true));
790 fileCfg.setExcludeFolders(
791 folderPatternListFromString(m_excludeFoldersLineEdit->text(), false));
792 fileCfg.setShowHiddenFiles(m_showHiddenFilesCheckBox->isChecked());
793 fileCfg.setTextEncodingIndex(m_fileTextEncodingComboBox->currentIndex());
794 fileCfg.setToFilenameFormats(m_toFilenameFormats);
795 fileCfg.setFromFilenameFormats(m_fromFilenameFormats);
796 tagCfg.setOnlyCustomGenres(m_onlyCustomGenresCheckBox->isChecked());
797 tagCfg.setCustomGenres(m_genresEditModel->stringList());
798 tagCfg.setStarRatingMappings(m_starRatingMappingsModel->getMappings());
799 QList<int> frameTypes;
800 bool isStandardFrameOrder = true;
801 quint64 frameMask = 0;
802 const int numQuickAccessTags = m_quickAccessTagsModel->rowCount();
803 frameTypes.reserve(numQuickAccessTags);
804 for (int row = 0; row < numQuickAccessTags; ++row) {
805 QModelIndex index = m_quickAccessTagsModel->index(row, 0);
806 int frameType = index.data(Qt::UserRole).toInt();
807 if (frameType != row) {
808 isStandardFrameOrder = false;
809 }
810 frameTypes.append(frameType);
811 if (m_quickAccessTagsModel->data(
812 index, Qt::CheckStateRole).toInt() == Qt::Checked) {
813 frameMask |= 1ULL << frameType;
814 }
815 }
816 if (isStandardFrameOrder) {
817 frameTypes.clear();
818 }
819 tagCfg.setQuickAccessFrames(frameMask);
820 tagCfg.setQuickAccessFrameOrder(frameTypes);
821 userActionsCfg.setContextMenuCommands(m_commandsTableModel->getCommandList());
822 tagCfg.setCommentName(m_commentNameComboBox->currentText());
823 tagCfg.setPictureNameIndex(m_pictureNameComboBox->currentIndex());
824 tagCfg.setGenreNotNumeric(m_genreNotNumericCheckBox->isChecked());
825 tagCfg.setLowercaseId3RiffChunk(m_lowercaseId3ChunkCheckBox->isChecked());
826 tagCfg.setMarkStandardViolations(m_markStandardViolationsCheckBox->isChecked());
827 tagCfg.setTextEncodingV1Index(m_textEncodingV1ComboBox->currentIndex());
828 tagCfg.setTextEncoding(m_textEncodingComboBox->currentIndex());
829 tagCfg.setId3v2Version(m_id3v2VersionComboBox->itemData(
830 m_id3v2VersionComboBox->currentIndex()).toInt());
831 tagCfg.setTrackNumberDigits(m_trackNumberDigitsSpinBox->value());
832 tagCfg.setMarkOversizedPictures(m_markOversizedPicturesCheckBox->isChecked());
833 tagCfg.setMaximumPictureSize(m_maximumPictureSizeSpinBox->value());
834 tagCfg.setRiffTrackName(m_trackNameComboBox->currentText());
835 networkCfg.setBrowser(m_browserLineEdit->text());
836 guiCfg.setPlayOnDoubleClick(m_playOnDoubleClickCheckBox->isChecked());
837 networkCfg.setUseProxy(m_proxyCheckBox->isChecked());
838 networkCfg.setProxy(m_proxyLineEdit->text());
839 networkCfg.setUseProxyAuthentication(m_proxyAuthenticationCheckBox->isChecked());
840 networkCfg.setProxyUserName(m_proxyUserNameLineEdit->text());
841 networkCfg.setProxyPassword(m_proxyPasswordLineEdit->text());
842
843 QStringList pluginOrder, disabledPlugins;
844 const int numPlugins = m_enabledMetadataPluginsModel->rowCount();
845 pluginOrder.reserve(numPlugins);
846 for (int row = 0; row < numPlugins; ++row) {
847 QString pluginName =
848 m_enabledMetadataPluginsModel->index(row).data().toString();
849 pluginOrder.append(pluginName);
850 if (m_enabledMetadataPluginsModel->index(row).data(Qt::CheckStateRole)
851 .toInt() != Qt::Checked) {
852 disabledPlugins.append(pluginName); // clazy:exclude=reserve-candidates
853 }
854 }
855 tagCfg.setPluginOrder(pluginOrder);
856 tagCfg.setDisabledPlugins(disabledPlugins);
857
858 disabledPlugins.clear();
859 for (int row = 0; row < m_enabledPluginsModel->rowCount(); ++row) {
860 if (m_enabledPluginsModel->index(row).data(Qt::CheckStateRole)
861 .toInt() != Qt::Checked) {
862 disabledPlugins.append(
863 m_enabledPluginsModel->index(row).data().toString());
864 }
865 }
866 importCfg.setDisabledPlugins(disabledPlugins);
867
868 if (QWidget* configDialog = qobject_cast<QWidget*>(parent())) {
869 guiCfg.setConfigWindowGeometry(configDialog->saveGeometry());
870 }
871 }
872