1 #include "common/common_pch.h"
2
3 #include <QColorDialog>
4 #include <QFileDialog>
5 #include <QIcon>
6 #include <QInputDialog>
7 #include <QItemSelectionModel>
8 #include <QModelIndex>
9 #include <QStandardItem>
10 #include <QTabWidget>
11 #include <QVector>
12
13 #include "common/chapters/chapters.h"
14 #include "common/qt.h"
15 #include "common/translation.h"
16 #include "mkvtoolnix-gui/app.h"
17 #include "mkvtoolnix-gui/chapter_editor/tool.h"
18 #include "mkvtoolnix-gui/forms/main_window/preferences_dialog.h"
19 #include "mkvtoolnix-gui/main_window/preferences_dialog.h"
20 #include "mkvtoolnix-gui/main_window/prefs_run_program_widget.h"
21 #include "mkvtoolnix-gui/merge/additional_command_line_options_dialog.h"
22 #include "mkvtoolnix-gui/merge/source_file.h"
23 #include "mkvtoolnix-gui/util/container.h"
24 #include "mkvtoolnix-gui/util/file_dialog.h"
25 #include "mkvtoolnix-gui/util/language_dialog.h"
26 #include "mkvtoolnix-gui/util/message_box.h"
27 #include "mkvtoolnix-gui/util/model.h"
28 #include "mkvtoolnix-gui/util/side_by_side_multi_select.h"
29 #include "mkvtoolnix-gui/util/string_list_configuration_widget.h"
30 #include "mkvtoolnix-gui/util/widget.h"
31
32 namespace mtx::gui {
33
34 PreferencesDialog::Page PreferencesDialog::ms_previouslySelectedPage{PreferencesDialog::Page::Gui};
35
PreferencesDialog(QWidget * parent,Page pageToShow)36 PreferencesDialog::PreferencesDialog(QWidget *parent,
37 Page pageToShow)
38 : QDialog{parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint}
39 , ui{new Ui::PreferencesDialog}
40 , m_cfg(Util::Settings::get())
41 , m_previousUiLocale{m_cfg.m_uiLocale}
42 , m_previousDisableToolTips{m_cfg.m_uiDisableToolTips}
43 , m_previousProbeRangePercentage{m_cfg.m_probeRangePercentage}
44 , m_ignoreNextCurrentChange{}
45 {
46 ui->setupUi(this);
47
48 setupPageSelector(pageToShow == Page::PreviouslySelected ? ms_previouslySelectedPage : pageToShow);
49
50 ui->tbOftenUsedXYZ->setTabPosition(m_cfg.m_tabPosition);
51
52 Util::restoreWidgetGeometry(this);
53
54 // GUI page
55 ui->cbGuiDisableToolTips->setChecked(m_cfg.m_uiDisableToolTips);
56 ui->cbGuiCheckForUpdates->setChecked(m_cfg.m_checkForUpdates);
57 ui->cbGuiShowDebuggingMenu->setChecked(m_cfg.m_showDebuggingMenu);
58 ui->cbGuiShowToolSelector->setChecked(m_cfg.m_showToolSelector);
59 ui->cbGuiShowMoveUpDownButtons->setChecked(m_cfg.m_showMoveUpDownButtons);
60 ui->cbGuiElideTabHeaderLabels->setChecked(m_cfg.m_elideTabHeaderLabels);
61 ui->cbGuiUseLegacyFontMIMETypes->setChecked(m_cfg.m_useLegacyFontMIMETypes);
62 ui->cbGuiWarnBeforeClosingModifiedTabs->setChecked(m_cfg.m_warnBeforeClosingModifiedTabs);
63 ui->cbGuiWarnBeforeAbortingJobs->setChecked(m_cfg.m_warnBeforeAbortingJobs);
64 ui->cbGuiWarnBeforeOverwriting->setChecked(m_cfg.m_warnBeforeOverwriting);
65 setupFontAndScaling();
66 setupInterfaceLanguage();
67 setupTabPositions();
68 setupBCP47LanguageEditMode();
69 setupDerivingTrackLanguagesFromFileName();
70 setupWhenToSetDefaultLanguage();
71 setupLanguageShortcuts();
72
73 ui->cbGuiUseDefaultJobDescription->setChecked(m_cfg.m_useDefaultJobDescription);
74 ui->cbGuiShowOutputOfAllJobs->setChecked(m_cfg.m_showOutputOfAllJobs);
75 ui->cbGuiSwitchToJobOutputAfterStarting->setChecked(m_cfg.m_switchToJobOutputAfterStarting);
76 ui->cbGuiResetJobWarningErrorCountersOnExit->setChecked(m_cfg.m_resetJobWarningErrorCountersOnExit);
77 ui->cbGuiRemoveOutputFileOnJobFailure->setChecked(m_cfg.m_removeOutputFileOnJobFailure);
78 ui->cbGuiRemoveOldJobs->setChecked(m_cfg.m_removeOldJobs);
79 ui->sbGuiRemoveOldJobsDays->setValue(m_cfg.m_removeOldJobsDays);
80 ui->sbGuiMaximumConcurrentJobs->setValue(m_cfg.m_maximumConcurrentJobs);
81 adjustRemoveOldJobsControls();
82 setupJobRemovalPolicy();
83
84 setupCommonLanguages(m_cfg.m_useISO639_3Languages);
85 setupCommonRegions();
86 setupCommonCharacterSets();
87
88 ui->cbUseISO639_3Languages->setChecked(m_cfg.m_useISO639_3Languages);
89
90 // Merge page
91 if (!m_cfg.m_mediaInfoExe.isEmpty())
92 ui->leMMediaInfoExe->setText(QDir::toNativeSeparators(m_cfg.m_mediaInfoExe));
93 ui->cbMSortFilesTracksByTypeWhenAdding->setChecked(m_cfg.m_mergeSortFilesTracksByTypeWhenAdding);
94 ui->cbMReconstructSequencesWhenAdding->setChecked(m_cfg.m_mergeReconstructSequencesWhenAdding);
95 ui->cbMAutoSetFileTitle->setChecked(m_cfg.m_autoSetFileTitle);
96 ui->cbMAutoClearFileTitle->setChecked(m_cfg.m_autoClearFileTitle);
97 ui->cbMSetAudioDelayFromFileName->setChecked(m_cfg.m_setAudioDelayFromFileName);
98 ui->cbMDisableCompressionForAllTrackTypes->setChecked(m_cfg.m_disableCompressionForAllTrackTypes);
99 ui->cbMDisableDefaultTrackForSubtitles->setChecked(m_cfg.m_disableDefaultTrackForSubtitles);
100 ui->cbMEnableDialogNormGainRemoval->setChecked(m_cfg.m_mergeEnableDialogNormGainRemoval);
101 ui->cbMAlwaysShowOutputFileControls->setChecked(m_cfg.m_mergeAlwaysShowOutputFileControls);
102 ui->cbMClearMergeSettings->setCurrentIndex(static_cast<int>(m_cfg.m_clearMergeSettings));
103 ui->ldwMDefaultAudioTrackLanguage->setLanguage(m_cfg.m_defaultAudioTrackLanguage);
104 ui->ldwMDefaultAudioTrackLanguage->registerBuddyLabel(*ui->lDefaultAudioTrackLanguage);
105 ui->ldwMDefaultVideoTrackLanguage->setLanguage(m_cfg.m_defaultVideoTrackLanguage);
106 ui->ldwMDefaultVideoTrackLanguage->registerBuddyLabel(*ui->lDefaultVideoTrackLanguage);
107 ui->ldwMDefaultSubtitleTrackLanguage->setLanguage(m_cfg.m_defaultSubtitleTrackLanguage);
108 ui->ldwMDefaultSubtitleTrackLanguage->registerBuddyLabel(*ui->lDefaultSubtitleTrackLanguage);
109 ui->cbMDefaultSubtitleCharset->setAdditionalItems(m_cfg.m_defaultSubtitleCharset).setup(true, QY("– No selection by default –")).setCurrentByData(m_cfg.m_defaultSubtitleCharset);
110 ui->leMDefaultAdditionalCommandLineOptions->setText(m_cfg.m_defaultAdditionalMergeOptions);
111 ui->cbMProbeRangePercentage->setValue(m_cfg.m_probeRangePercentage);
112 ui->cbMAddBlurayCovers->setChecked(m_cfg.m_mergeAddBlurayCovers);
113 ui->cbMAttachmentAlwaysSkipForExistingName->setChecked(m_cfg.m_mergeAttachmentsAlwaysSkipForExistingName);
114
115 setupFileColorsControls();
116 setupProcessPriority();
117 setupPlaylistScanningPolicy();
118 setupOutputFileNamePolicy();
119 setupRecentDestinationDirectoryList();
120 setupEnableMuxingTracksByType();
121 setupEnableMuxingTracksByLanguage();
122 setupMergeAddingAppendingFilesPolicy();
123 setupMergeWarnMissingAudioTrack();
124 setupMergePredefinedItems();
125 setupTrackPropertiesLayout();
126
127 // Info tool page
128 ui->wIDefaultJobSettings->setFileNameVisible(false);
129 ui->wIDefaultJobSettings->setSettings(m_cfg.m_defaultInfoJobSettings);
130
131 // Chapter editor page
132 ui->cbCEDropLastFromBlurayPlaylist->setChecked(m_cfg.m_dropLastChapterFromBlurayPlaylist);
133 ui->cbCETextFileCharacterSet->setAdditionalItems(m_cfg.m_ceTextFileCharacterSet).setup(true, QY("Always ask the user")).setCurrentByData(m_cfg.m_ceTextFileCharacterSet);
134 ui->leCENameTemplate->setText(m_cfg.m_chapterNameTemplate);
135 ui->ldwCEDefaultLanguage->setLanguage(m_cfg.m_defaultChapterLanguage);
136
137 // Header editor page
138 setupHeaderEditorDroppedFilesPolicy();
139 ui->cbHEDateTimeInUTC->setChecked(m_cfg.m_headerEditorDateTimeInUTC);
140
141 setupJobsRunPrograms();
142
143 setupToolTips();
144 setupConnections();
145
146 Util::preventScrollingWithoutFocus(this);
147 }
148
~PreferencesDialog()149 PreferencesDialog::~PreferencesDialog() {
150 Util::saveWidgetGeometry(this);
151 }
152
153 bool
uiLocaleChanged() const154 PreferencesDialog::uiLocaleChanged()
155 const {
156 return m_previousUiLocale != m_cfg.m_uiLocale;
157 }
158
159 bool
disableToolTipsChanged() const160 PreferencesDialog::disableToolTipsChanged()
161 const {
162 return m_previousDisableToolTips != m_cfg.m_uiDisableToolTips;
163 }
164
165 bool
probeRangePercentageChanged() const166 PreferencesDialog::probeRangePercentageChanged()
167 const {
168 return m_previousProbeRangePercentage != m_cfg.m_probeRangePercentage;
169 }
170
171 void
pageSelectionChanged(QItemSelection const & selection)172 PreferencesDialog::pageSelectionChanged(QItemSelection const &selection) {
173 if (selection.indexes().isEmpty())
174 return;
175
176 auto current = selection.indexes().first();
177 if (!current.isValid())
178 return;
179
180 if (m_ignoreNextCurrentChange) {
181 m_ignoreNextCurrentChange = false;
182 return;
183 }
184
185 auto page = qobject_cast<QStandardItemModel *>(ui->pageSelector->model())
186 ->itemFromIndex(current.sibling(current.row(), 0))
187 ->data()
188 .value<int>();
189 ui->pages->setCurrentIndex(page);
190 }
191
192 QModelIndex
modelIndexForPage(int page)193 PreferencesDialog::modelIndexForPage(int page) {
194 auto &model = *qobject_cast<QStandardItemModel *>(ui->pageSelector->model());
195 auto pageIdx = Util::findIndex(model, [page, &model](QModelIndex const &idxToTest) {
196 return model.itemFromIndex(idxToTest)->data().value<int>() == page;
197 });
198
199 return pageIdx;
200 }
201
202 void
setupPageSelector(Page pageToShow)203 PreferencesDialog::setupPageSelector(Page pageToShow) {
204 m_cfg.handleSplitterSizes(ui->pagesSplitter);
205
206 auto pageIndex = 0;
207 auto model = new QStandardItemModel{this};
208 ui->pageSelector->setModel(model);
209
210 auto addItem = [this, model, &pageIndex](Page pageType, QStandardItem *parent, QString const &text, QString const &icon = QString{}) -> QStandardItem * {
211 auto item = new QStandardItem{text};
212
213 item->setData(pageIndex);
214 m_pageIndexes[pageType] = pageIndex++;
215
216 if (!icon.isEmpty())
217 item->setIcon(QIcon{Q(":/icons/16x16/%1.png").arg(icon)});
218
219 if (parent)
220 parent->appendRow(item);
221 else
222 model->appendRow(item);
223
224 return item;
225 };
226
227 auto pGui = addItem(Page::Gui, nullptr, QY("GUI"), "mkvtoolnix-gui");
228 addItem(Page::OftenUsedSelections, pGui, QY("Often used selections"));
229 addItem(Page::LanguagesShortcuts, pGui, QY("Language shortcuts"));
230 auto pMerge = addItem(Page::Merge, nullptr, QY("Multiplexer"), "merge");
231 addItem(Page::PredefinedValues, pMerge, QY("Predefined values"));
232 addItem(Page::DefaultValues, pMerge, QY("Default values"));
233 addItem(Page::DeriveTrackLanguage, pMerge, QY("Deriving track languages"));
234 addItem(Page::Output, pMerge, QY("Destination file name"));
235 addItem(Page::EnablingTracks, pMerge, QY("Enabling items"));
236 addItem(Page::Playlists, pMerge, QY("Playlists & Blu-rays"));
237 addItem(Page::Info, nullptr, QY("Info tool"), "document-preview-archive");
238 addItem(Page::HeaderEditor, nullptr, QY("Header editor"), "document-edit");
239 addItem(Page::ChapterEditor, nullptr, QY("Chapter editor"), "story-editor");
240 auto pJobs = addItem(Page::Jobs, nullptr, QY("Jobs & job queue"), "view-task");
241 addItem(Page::RunPrograms, pJobs, QY("Executing actions"));
242
243 for (auto row = 0, numRows = model->rowCount(); row < numRows; ++row)
244 ui->pageSelector->setExpanded(model->index(row, 0), true);
245
246 ui->pageSelector->setMinimumSize(ui->pageSelector->minimumSizeHint());
247
248 showPage(pageToShow);
249
250 m_ignoreNextCurrentChange = false;
251
252 connect(ui->pageSelector->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PreferencesDialog::pageSelectionChanged);
253 }
254
255 void
setupToolTips()256 PreferencesDialog::setupToolTips() {
257 if (m_cfg.m_uiDisableToolTips)
258 return;
259
260 // GUI page
261 Util::setToolTip(ui->cbGuiDisableHighDPIScaling,
262 Q("%1 %2")
263 .arg(QY("If enabled, automatic scaling for high DPI displays will be disabled."))
264 .arg(QY("Changes to this option will only take effect the next time the application is started.")));
265
266 Util::setToolTip(ui->cbGuiDisableToolTips, QY("If checked, the GUI will not show helpful usage tips in popup windows while hovering over a control — such as this one."));
267 Util::setToolTip(ui->cbGuiDisableDarkStyleSheet,
268 Q("%1 %2")
269 .arg(QY("By default the GUI will start up with a dark color scheme if the color scheme in Windows is set to dark mode."))
270 .arg(QY("If this option is enabled, the GUI will not use its dark mode and fall back to the default color scheme.")));
271
272 Util::setToolTip(ui->cbGuiCheckForUpdates,
273 Q("%1 %2 %3")
274 .arg(QY("If enabled, the program will check online whether or not a new release of MKVToolNix is available on the home page."))
275 .arg(QY("This is done at startup and at most once within 24 hours."))
276 .arg(QY("No information is transmitted to the server.")));
277
278 Util::setToolTip(ui->cbGuiWarnBeforeClosingModifiedTabs,
279 Q("%1 %2")
280 .arg(QY("If checked, the program will ask for confirmation before closing or reloading tabs that have been modified."))
281 .arg(QY("This is also done when quitting the application.")));
282 Util::setToolTip(ui->cbGuiWarnBeforeAbortingJobs,
283 Q("%1 %2")
284 .arg(QY("If checked, the program will ask for confirmation before aborting a running job."))
285 .arg(QY("This happens when clicking the \"abort job\" button in a \"job output\" tab and when quitting the application.")));
286
287 Util::setToolTip(ui->cbGuiShowMoveUpDownButtons,
288 Q("%1 %2")
289 .arg(QY("Normally selected entries in list view can be moved around via drag & drop and with keyboard shortcuts (Ctrl+Up, Ctrl+Down)."))
290 .arg(QY("If checked, additional buttons for moving selected entries up and down will be shown next to several list views.")));
291
292 Util::setToolTip(ui->cbGuiElideTabHeaderLabels, QY("If enabled, the names of tab headers will be shortened so that all tab headers fit into the window's width."));
293
294 Util::setToolTip(ui->cbGuiUseLegacyFontMIMETypes,
295 Q("%1 %2")
296 .arg(QY("If enabled, the GUI will use legacy MIME types when detecting the MIME type of font attachments instead of the current standard MIME types."))
297 .arg(QY("This mostly affects TrueType fonts for which the legacy MIME type ('application/x-truetype-font') might be more widely supported than the standard MIME types ('font/sfnt' and 'font/ttf').")));
298
299 Util::setToolTip(ui->cbGuiWarnBeforeOverwriting, QY("If enabled, the program will ask for confirmation before overwriting files and jobs."));
300
301 Util::setToolTip(ui->cbGuiUseDefaultJobDescription, QY("If disabled, the GUI will let you enter a description for a job when adding it to the queue."));
302 Util::setToolTip(ui->cbGuiShowOutputOfAllJobs, QY("If enabled, the first tab in the \"job output\" tool will not be cleared when a new job starts."));
303 Util::setToolTip(ui->cbGuiSwitchToJobOutputAfterStarting, QY("If enabled, the GUI will automatically switch to the job output tool whenever you start a job (e.g. by pressing \"start multiplexing\")."));
304 Util::setToolTip(ui->cbGuiResetJobWarningErrorCountersOnExit, QY("If enabled, the warning and error counters of all jobs and the global counters in the status bar will be reset to 0 when the program exits."));
305 Util::setToolTip(ui->cbGuiRemoveOutputFileOnJobFailure, QY("If enabled, the GUI will remove the output file created by a job if that job ends with an error or if the user aborts the job."));
306 Util::setToolTip(ui->cbGuiRemoveOldJobs, QY("If enabled, the GUI will remove completed jobs older than the configured number of days no matter their status on exit."));
307 Util::setToolTip(ui->sbGuiRemoveOldJobsDays, QY("If enabled, the GUI will remove completed jobs older than the configured number of days no matter their status on exit."));
308
309 Util::setToolTip(ui->cbGuiRemoveJobs,
310 Q("%1 %2")
311 .arg(QY("Normally completed jobs stay in the queue even over restarts until the user clears them out manually."))
312 .arg(QY("You can opt for having them removed automatically under certain conditions.")));
313
314 Util::setToolTip(ui->leCENameTemplate, ChapterEditor::Tool::chapterNameTemplateToolTip());
315 Util::setToolTip(ui->ldwCEDefaultLanguage, QY("This is the language that newly added chapter names get assigned automatically."));
316 Util::setToolTip(ui->cbCEDropLastFromBlurayPlaylist,
317 Q("%1 %2")
318 .arg(QY("Blu-ray discs often contain a chapter entry very close to the end of the movie."))
319 .arg(QY("If enabled, the last entry will be skipped when loading chapters from such playlists in the chapter editor if it is located within five seconds of the end of the movie.")));
320 Util::setToolTip(ui->cbCETextFileCharacterSet,
321 Q("%1 %2 %3")
322 .arg(QY("The chapter editor needs to know the character set a text chapter file uses in order to display all characters properly."))
323 .arg(QY("By default it always asks the user which character set to use when opening a file for which it cannot be recognized automatically."))
324 .arg(QY("If a character set is selected here, it will be used instead of asking the user.")));
325
326 // Merge page
327 Util::setToolTip(ui->cbMAutoSetFileTitle,
328 Q("<p>%1 %2</p><p>%3</p>")
329 .arg(QYH("Certain file formats have 'title' property."))
330 .arg(QYH("When the user adds a file containing such a title, the program will copy the title into the \"file title\" input box if this option is enabled."))
331 .arg(QYH("Note that even if the option is disabled mkvmerge will copy a source file's title property unless a title is manually set by the user.")));
332 Util::setToolTip(ui->cbMAutoClearFileTitle, QY("If this option is enabled, the GUI will always clear the \"file title\" input box whenever the last source file is removed."));
333
334 auto widgets = QList<mtx::gui::Util::StringListConfigurationWidget *>{} << ui->lwMPredefinedVideoTrackNames << ui->lwMPredefinedAudioTrackNames << ui->lwMPredefinedSubtitleTrackNames;
335 for (auto const &widget : widgets)
336 widget->setToolTips(Q("%1 %2 %3")
337 .arg(QY("If you often use the same names for tracks, you can enter them here."))
338 .arg(QY("The names will be available for easy selection in both the multiplexer and the header editor."))
339 .arg(QY("You can still enter track names not present in this list manually in both tools.")));
340
341 ui->lwMPredefinedSplitSizes->setToolTips(Q("%1 %2 %3")
342 .arg(QY("If you often use the same values when splitting by size, you can enter them here."))
343 .arg(QY("The values will be available for easy selection in the multiplexer."))
344 .arg(QY("You can still enter values not present in this list manually in the multiplexer.")));
345
346 ui->lwMPredefinedSplitDurations->setToolTips(Q("%1 %2 %3")
347 .arg(QY("If you often use the same values when splitting by duration, you can enter them here."))
348 .arg(QY("The values will be available for easy selection in the multiplexer."))
349 .arg(QY("You can still enter values not present in this list manually in the multiplexer.")));
350
351 Util::setToolTip(ui->cbMSetAudioDelayFromFileName,
352 Q("%1 %2")
353 .arg(QY("When a file is added its name is scanned."))
354 .arg(QY("If it contains the word 'DELAY' followed by a number, this number is automatically put into the 'delay' input field for any audio track found in the file.")));
355
356 Util::setToolTip(ui->cbMDisableDefaultTrackForSubtitles, QY("If enabled, all subtitle tracks will have their \"default track\" flag set to \"no\" when they're added."));
357
358 Util::setToolTip(ui->cbMDisableCompressionForAllTrackTypes,
359 Q("%1 %2")
360 .arg(QY("Normally mkvmerge will apply additional lossless compression for subtitle tracks for certain codecs."))
361 .arg(QY("Checking this option causes the GUI to set that compression to \"none\" by default for all track types when adding files.")));
362
363 Util::setToolTip(ui->cbMEnableDialogNormGainRemoval, QY("If enabled, removal of dialog normalization gain will be enabled for all audio tracks for which removal is supported."));
364
365 Util::setToolTip(ui->cbMProbeRangePercentage,
366 Q("%1 %2 %3")
367 .arg(QY("File types such as MPEG program and transport streams (.vob, .m2ts) require parsing a certain amount of data in order to detect all tracks contained in the file."))
368 .arg(QY("This amount is 0.3% of the source file's size or 10 MB, whichever is higher."))
369 .arg(QY("If tracks are known to be present but not found, the percentage to probe can be changed here.")));
370
371 Util::setToolTip(ui->cbMSortFilesTracksByTypeWhenAdding,
372 Q("<p>%1 %2</p><p>%3 %4</p><p>%5</p><p>%6</p>")
373 .arg(QY("If enabled, files and tracks will be sorted by track types when they're added to multiplex settings."))
374 .arg(QY("The order is: video first followed by audio, subtitles and other types."))
375 .arg(QY("For example, a file containing audio tracks but no video tracks will be inserted before the first file that contains neither video nor audio tracks."))
376 .arg(QY("Similarly an audio track will be inserted before the first track that's neither a video nor an audio track."))
377 .arg(QY("If disabled, files and tracks will be inserted after all existing files and tracks."))
378 .arg(QY("This only determines the initial order which can still be changed manually later.")));
379
380 Util::setToolTip(ui->cbMReconstructSequencesWhenAdding,
381 Q("<p>%1 %2 %3</p><p>%4</p><p>%5</p><p>%6</p>")
382 .arg(QY("If enabled, the GUI will analyze the file names when you add multiple files at once."))
383 .arg(QY("It tries to detect sequences of names that likely belong to the same movie by splitting the name into three parts: a prefix, a running number and a suffix that doesn't contain digits."))
384 .arg(QY("Names are considered to be in sequence when the previous file name's prefix & suffix match the current file name's prefix & suffix and the running number is incremented by one."))
385 .arg(QY("An example: 'movie.001.mp4', 'movie.002.mp4' and 'movie.003.mp4'"))
386 .arg(QY("If such a sequence is detected, only the first file is added while the second and following files in the sequence are appended to the first one."))
387 .arg(QY("If disabled, all files selected for adding will always be added regardless of their names.")));
388
389 Util::setToolTip(ui->cbMAlwaysShowOutputFileControls,
390 Q("%1 %2")
391 .arg(QY("If enabled, the destination file name controls will always be visible no matter which tab is currently shown."))
392 .arg(QY("Otherwise they're shown on the 'output' tab.")));
393
394 auto controls = QWidgetList{} << ui->rbMTrackPropertiesLayoutHorizontalScrollArea << ui->rbMTrackPropertiesLayoutHorizontalTwoColumns << ui->rbMTrackPropertiesLayoutVerticalTabWidget;
395 for (auto const &control : controls)
396 Util::setToolTip(control,
397 Q("<p>%1 %2</p><p>%3 %4</p>")
398 .arg(QYH("The track properties on the \"input\" tab can be laid out in three different way in order to serve different workflows."))
399 .arg(QYH("In the most compact layout the track properties are located to the right of the files and tracks lists in a scrollable single column."))
400 .arg(QYH("The other two layouts available are: in two fixed columns to the right or in a tab widget below the files and tracks lists."))
401 .arg(QYH("The horizontal layout with two fixed columns results in a wider window while the vertical tab widget layout results in a higher window.")));
402
403 Util::setToolTip(ui->cbMUseFileAndTrackColors,
404 Q("<p>%1 %2</p>")
405 .arg(QYH("If enabled, small colored boxes will be shown in the file and track lists as a visual clue to help associating tracks with the files they come from."))
406 .arg(QYH("If there are more entries than configured colors, random colors will be used temporarily.")));
407
408
409 Util::setToolTip(ui->cbMClearMergeSettings,
410 Q("<p>%1</p><ol><li>%2 %3</li><li>%4 %5</li><li>%6</li></ol>")
411 .arg(QYH("The GUI can help you start your next multiplex settings after having started a job or having added a one to the job queue."))
412 .arg(QYH("With \"create new settings\" a new set of multiplex settings will be added."))
413 .arg(QYH("The current multiplex settings will be closed."))
414 .arg(QYH("With \"remove source files\" all source files will be removed."))
415 .arg(QYH("Most of the other settings on the output tab will be kept intact, though."))
416 .arg(QYH("With \"close current settings\" the current multiplex settings will be closed without opening new ones.")));
417
418 Util::setToolTip(ui->cbMAddingAppendingFilesPolicy,
419 Q("%1 %2 %3")
420 .arg(QY("When the user drags & drops files from an external application onto the multiplex tool the GUI can take different actions."))
421 .arg(QY("The default is to always add all the files to the current multiplex settings."))
422 .arg(QY("The GUI can also ask the user what to do each time, e.g. appending them instead of adding them, or creating new multiplex settings and adding them to those.")));
423
424 Util::setToolTip(ui->cbMWarnMissingAudioTrack, QY("The GUI can ask for confirmation when you're about to create a file without audio tracks in it."));
425
426 Util::setToolTip(ui->cbMDeriveAudioTrackLanguageFromFileName,
427 Q("<p>%1 %2</p><p>%3 %4</p>")
428 .arg(QYH("Certain file formats have a 'language' property for their tracks."))
429 .arg(QYH("When the user adds such a file the track's language input is set to the language property from the source file."))
430 .arg(QYH("If the source file contains no such property for an audio track, then the language can be derived from the file name if it matches certain patterns (e.g. '…[ger]…' for German)."))
431 .arg(QYH("Depending on this setting the language can also be derived from the file name if the language in the source file is 'undetermined' ('und').")));
432 Util::setToolTip(ui->cbMDeriveVideoTrackLanguageFromFileName,
433 Q("<p>%1 %2</p><p>%3 %4</p>")
434 .arg(QYH("Certain file formats have a 'language' property for their tracks."))
435 .arg(QYH("When the user adds such a file the track's language input is set to the language property from the source file."))
436 .arg(QYH("If the source file contains no such property for a video track, then the language can be derived from the file name if it matches certain patterns (e.g. '…[ger]…' for German)."))
437 .arg(QYH("Depending on this setting the language can also be derived from the file name if the language in the source file is 'undetermined' ('und').")));
438 Util::setToolTip(ui->cbMDeriveSubtitleTrackLanguageFromFileName,
439 Q("<p>%1 %2</p><p>%3 %4</p>")
440 .arg(QYH("Certain file formats have a 'language' property for their tracks."))
441 .arg(QYH("When the user adds such a file the track's language input is set to the language property from the source file."))
442 .arg(QYH("If the source file contains no such property for a subtitle track, then the language can be derived from the file name if it matches certain patterns (e.g. '…[ger]…' for German)."))
443 .arg(QYH("Depending on this setting the language can also be derived from the file name if the language in the source file is 'undetermined' ('und').")));
444 Util::setToolTip(ui->leMDeriveTrackLanguageBoundaryChars,
445 Q("<p>%1 %2</p>")
446 .arg(QYH("When deriving the track language from the file name, the file name is split into parts on the characters in this list."))
447 .arg(QYH("Each part is then matched against the list of languages selected below to determine whether or not to use it as the track language.")));
448 Util::setToolTip(ui->pbMDeriveTrackLanguageRevertBoundaryChars, QY("Revert the entry to its default value."));
449 ui->tbMDeriveTrackLanguageRecognizedLanguages->setToolTips(QY("Only the languages in the 'selected' list on the right will be recognized as track languages in file names."));
450
451 Util::setToolTip(ui->ldwMDefaultAudioTrackLanguage,
452 Q("<p>%1 %2</p><p>%3 %4</p>")
453 .arg(QYH("Certain file formats have a 'language' property for their tracks."))
454 .arg(QYH("When the user adds such a file the track's language input is set to the language property from the source file."))
455 .arg(QYH("The language selected here is used for audio tracks for which their source file contains no such property and for which the language has not been derived from the file name."))
456 .arg(QYH("Depending on the setting below this language can also be used if the language in the source file is 'undetermined' ('und').")));
457 Util::setToolTip(ui->ldwMDefaultVideoTrackLanguage,
458 Q("<p>%1 %2</p><p>%3 %4</p>")
459 .arg(QYH("Certain file formats have a 'language' property for their tracks."))
460 .arg(QYH("When the user adds such a file the track's language input is set to the language property from the source file."))
461 .arg(QYH("The language selected here is used for video tracks for which their source file contains no such property and for which the language has not been derived from the file name."))
462 .arg(QYH("Depending on the setting below this language can also be used if the language in the source file is 'undetermined' ('und').")));
463 Util::setToolTip(ui->ldwMDefaultSubtitleTrackLanguage,
464 Q("<p>%1 %2</p><p>%3 %4</p>")
465 .arg(QYH("Certain file formats have a 'language' property for their tracks."))
466 .arg(QYH("When the user adds such a file the track's language input is set to the language property from the source file."))
467 .arg(QYH("The language selected here is used for subtitle tracks for which their source file contains no such property and for which the language has not been derived from the file name."))
468 .arg(QYH("Depending on the setting below this language can also be used if the language in the source file is 'undetermined' ('und').")));
469
470 Util::setToolTip(ui->cbMDefaultSubtitleCharset, QY("If a character set is selected here, the program will automatically set the character set input to this value for newly added text subtitle tracks."));
471
472 Util::setToolTip(ui->leMDefaultAdditionalCommandLineOptions, QY("The options entered here are set for all new multiplex jobs by default."));
473
474 Util::setToolTip(ui->cbMScanPlaylistsPolicy,
475 Q("<p>%1 %2</p><p>%3</p>")
476 .arg(QYH("Whenever the user adds a playlist the program can automatically scan the directory for other playlists and present the user with a detailed list of the playlists found."))
477 .arg(QYH("The user can then select which playlist to actually add."))
478 .arg(QYH("This is useful for situations like Blu-ray discs on which a multitude of playlists exists in the same directory and where it is not obvious which feature (e.g. main movie, extras etc.) "
479 "a playlist belongs to.")));
480
481 Util::setToolTip(ui->sbMMinPlaylistDuration, QY("Only playlists whose duration are at least this long are considered and offered to the user for selection."));
482 Util::setToolTip(ui->cbMAddBlurayCovers, QY("If enabled, the largest cover image of a Blu-ray will be added as an attachment when adding a Blu-ray playlist."));
483 Util::setToolTip(ui->cbMAttachmentAlwaysSkipForExistingName,
484 Q("<p>%1 %2 %3</p>")
485 .arg(QY("When adding new files as attachments the GUI will check if there are other attachments with the same name."))
486 .arg(QY("If one is found, the GUI will ask whether to skip the file or to add it anyway."))
487 .arg(QY("If enabled, such files will always be skipped without asking.")));
488
489 Util::setToolTip(ui->cbMAutoSetOutputFileName,
490 Q("%1 %2")
491 .arg(QY("If this option is enabled and if there is currently no destination file name set, the program will set one for you when you add a source file."))
492 .arg(QY("The generated destination file name has the same base name as the source file name but with an extension based on the track types present in the file.")));
493
494 Util::setToolTip(ui->cbMAutoDestinationOnlyForVideoFiles,
495 Q("%1 %2")
496 .arg(QY("If this option is enabled, only source files containing video tracks will be used for setting the destination file name."))
497 .arg(QY("Other files are ignored when they're added.")));
498
499 Util::setToolTip(ui->cbMSetDestinationFromTitle,
500 Q("%1 %2")
501 .arg(QY("If this option is enabled, the file title will be used as the basis for the destination file name if a file title is set."))
502 .arg(QY("Otherwise the destination file name is derived from the source file names.")));
503
504 Util::setToolTip(ui->cbMUniqueOutputFileNames,
505 Q("%1 %2")
506 .arg(QY("If checked, the program makes sure the suggested destination file name is unique by adding a number (e.g. ' (1)') to the end of the file name."))
507 .arg(QY("This is done only if there is already a file whose name matches the unmodified destination file name.")));
508 Util::setToolTip(ui->cbMAutoClearOutputFileName, QY("If this option is enabled, the GUI will always clear the \"destination file name\" input box whenever the last source file is removed."));
509
510 ui->tbMEnableMuxingTracksByType->setToolTips(QY("Only items whose type is in the 'selected' list on the right will be set to be copied by default."));
511 Util::setToolTip(ui->cbMEnableMuxingTracksByLanguage,
512 Q("<p>%1 %2 %3</p><p>%4</p>")
513 .arg(QYH("When adding source files all tracks are normally set to be copied into the destination file."))
514 .arg(QYH("If this option is enabled, only those tracks will be set to be copied whose language is selected below."))
515 .arg(QYH("You can exempt certain track types from this restriction by checking the corresponding check box below, e.g. for video tracks."))
516 .arg(QYH("Note that the language \"Undetermined (und)\" is assumed for tracks for which no language is known (e.g. those read from SRT subtitle files).")));
517 Util::setToolTip(ui->cbMEnableMuxingAllVideoTracks, QY("If enabled, tracks of this type will always be set to be copied regardless of their language."));
518 Util::setToolTip(ui->cbMEnableMuxingAllAudioTracks, QY("If enabled, tracks of this type will always be set to be copied regardless of their language."));
519 Util::setToolTip(ui->cbMEnableMuxingAllSubtitleTracks, QY("If enabled, tracks of this type will always be set to be copied regardless of their language."));
520 ui->tbMEnableMuxingTracksByLanguage->setToolTips(QY("Tracks with a language in this list will be set not to be copied by default."),
521 QY("Only tracks with a language in this list will be set to be copied by default."));
522
523 // Often used XYZ page
524 ui->tbOftenUsedLanguages->setToolTips(QY("The languages in the 'selected' list on the right will be shown at the top of all the language drop-down boxes in the program."));
525 Util::setToolTip(ui->cbOftenUsedLanguagesOnly,
526 Q("%1 %2")
527 .arg(QYH("If checked, only the list of often used entries will be included in the selections in the program."))
528 .arg(QYH("Otherwise the often used entries will be included first and the full list of all entries afterwards.")));
529
530 ui->tbOftenUsedRegions->setToolTips(QY("The entries in the 'selected' list on the right will be shown at the top of all the drop-down boxes with countries and regions in the program."));
531 Util::setToolTip(ui->cbOftenUsedRegionsOnly,
532 Q("%1 %2")
533 .arg(QYH("If checked, only the list of often used entries will be included in the selections in the program."))
534 .arg(QYH("Otherwise the often used entries will be included first and the full list of all entries afterwards.")));
535
536 ui->tbOftenUsedCharacterSets->setToolTips(QY("The character sets in the 'selected' list on the right will be shown at the top of all the character set drop-down boxes in the program."));
537 Util::setToolTip(ui->cbOftenUsedCharacterSetsOnly,
538 Q("%1 %2")
539 .arg(QYH("If checked, only the list of often used entries will be included in the selections in the program."))
540 .arg(QYH("Otherwise the often used entries will be included first and the full list of all entries afterwards.")));
541
542 // Header editor page
543 Util::setToolTip(ui->cbHEDroppedFilesPolicy,
544 Q("%1 %2 %3")
545 .arg(QY("When the user drags & drops files from an external application onto a header editor tab the GUI can take different actions."))
546 .arg(QY("The default is to ask the user what to do with the dropped files."))
547 .arg(QY("Apart from asking the GUI can always open the dropped files as new tabs or it can always add them as new attachments to the current tab.")));
548 }
549
550 void
setupConnections()551 PreferencesDialog::setupConnections() {
552 connect(ui->cbUseISO639_3Languages, &QCheckBox::toggled, this, &PreferencesDialog::setupCommonLanguages);
553
554 connect(ui->lwGuiLanguageShortcuts, &QListWidget::itemSelectionChanged, this, &PreferencesDialog::enableLanguageShortcutControls);
555 connect(ui->lwGuiLanguageShortcuts, &QListWidget::itemDoubleClicked, this, &PreferencesDialog::editLanguageShortcut);
556 connect(ui->pbGuiAddLanguageShortcut, &QPushButton::clicked, this, &PreferencesDialog::addLanguageShortcut);
557 connect(ui->pbGuiEditLanguageShortcut, &QPushButton::clicked, this, &PreferencesDialog::editLanguageShortcut);
558 connect(ui->pbGuiRemoveLanguageShortcuts, &QPushButton::clicked, this, &PreferencesDialog::removeLanguageShortcuts);
559
560 connect(ui->pbMEditDefaultAdditionalCommandLineOptions, &QPushButton::clicked, this, &PreferencesDialog::editDefaultAdditionalCommandLineOptions);
561
562 connect(ui->pbMBrowseMediaInfoExe, &QPushButton::clicked, this, &PreferencesDialog::browseMediaInfoExe);
563 connect(ui->cbMAutoSetOutputFileName, &QCheckBox::toggled, this, &PreferencesDialog::enableOutputFileNameControls);
564 connect(ui->rbMAutoSetSameDirectory, &QRadioButton::toggled, this, &PreferencesDialog::enableOutputFileNameControls);
565 connect(ui->rbMAutoSetRelativeDirectory, &QRadioButton::toggled, this, &PreferencesDialog::enableOutputFileNameControls);
566 connect(ui->rbMAutoSetPreviousDirectory, &QRadioButton::toggled, this, &PreferencesDialog::enableOutputFileNameControls);
567 connect(ui->rbMAutoSetFixedDirectory, &QRadioButton::toggled, this, &PreferencesDialog::enableOutputFileNameControls);
568 connect(ui->pbMBrowseAutoSetFixedDirectory, &QPushButton::clicked, this, &PreferencesDialog::browseFixedOutputDirectory);
569
570 connect(ui->cbMEnableMuxingTracksByLanguage, &QCheckBox::toggled, ui->lMEnableMuxingAllTracksOfType, &QLabel::setEnabled);
571 connect(ui->cbMEnableMuxingTracksByLanguage, &QCheckBox::toggled, ui->gbMEnableMuxingExceptions, &QLabel::setEnabled);
572 connect(ui->cbMEnableMuxingTracksByLanguage, &QCheckBox::toggled, ui->cbMEnableMuxingAllVideoTracks, &QLabel::setEnabled);
573 connect(ui->cbMEnableMuxingTracksByLanguage, &QCheckBox::toggled, ui->cbMEnableMuxingAllAudioTracks, &QLabel::setEnabled);
574 connect(ui->cbMEnableMuxingTracksByLanguage, &QCheckBox::toggled, ui->cbMEnableMuxingAllSubtitleTracks, &QLabel::setEnabled);
575 connect(ui->cbMEnableMuxingTracksByLanguage, &QCheckBox::toggled, ui->tbMEnableMuxingTracksByLanguage, &QLabel::setEnabled);
576
577 connect(ui->pbMDeriveTrackLanguageRevertBoundaryChars, &QPushButton::clicked, this, &PreferencesDialog::revertDeriveTrackLanguageFromFileNameChars);
578
579 connect(ui->cbGuiRemoveJobs, &QCheckBox::toggled, ui->cbGuiJobRemovalPolicy, &QComboBox::setEnabled);
580 connect(ui->cbGuiRemoveJobsOnExit, &QCheckBox::toggled, ui->cbGuiJobRemovalOnExitPolicy, &QComboBox::setEnabled);
581 connect(ui->cbGuiRemoveOldJobs, &QCheckBox::toggled, this, &PreferencesDialog::adjustRemoveOldJobsControls);
582 connect(ui->sbGuiRemoveOldJobsDays, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &PreferencesDialog::adjustRemoveOldJobsControls);
583
584 connect(ui->sbMMinPlaylistDuration, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &PreferencesDialog::adjustPlaylistControls);
585
586 connect(ui->buttons, &QDialogButtonBox::accepted, this, &PreferencesDialog::accept);
587 connect(ui->buttons, &QDialogButtonBox::rejected, this, &PreferencesDialog::reject);
588
589 connect(ui->tbOftenUsedLanguages, &Util::SideBySideMultiSelect::listsChanged, this, &PreferencesDialog::enableOftendUsedLanguagesOnly);
590 connect(ui->tbOftenUsedRegions, &Util::SideBySideMultiSelect::listsChanged, this, &PreferencesDialog::enableOftendUsedRegionsOnly);
591 connect(ui->tbOftenUsedCharacterSets, &Util::SideBySideMultiSelect::listsChanged, this, &PreferencesDialog::enableOftendUsedCharacterSetsOnly);
592 }
593
594 void
setupInterfaceLanguage()595 PreferencesDialog::setupInterfaceLanguage() {
596 #if defined(HAVE_LIBINTL_H)
597 using TranslationSorter = std::pair<QString, QString>;
598 auto translations = std::vector<TranslationSorter>{};
599
600 for (auto const &translation : translation_c::ms_available_translations)
601 translations.emplace_back(Q("%1 (%2)").arg(Q(translation.m_translated_name)).arg(Q(translation.m_english_name)), Q(translation.get_locale()));
602
603 std::sort(translations.begin(), translations.end());
604
605 for (auto const &translation : translations)
606 ui->cbGuiInterfaceLanguage->addItem(translation.first, translation.second);
607
608 Util::setComboBoxTextByData(ui->cbGuiInterfaceLanguage, m_cfg.m_uiLocale);
609 Util::fixComboBoxViewWidth(*ui->cbGuiInterfaceLanguage);
610 #endif // HAVE_LIBINTL_H
611 }
612
613 void
setupJobRemovalPolicy()614 PreferencesDialog::setupJobRemovalPolicy() {
615 auto doIt = [](Util::Settings::JobRemovalPolicy policy,
616 QCheckBox &checkBox,
617 QComboBox &comboBox) {
618 auto doRemove = Util::Settings::JobRemovalPolicy::Never != policy;
619 auto idx = std::max(static_cast<int>(policy), 1) - 1;
620
621 checkBox.setChecked(doRemove);
622 comboBox.setEnabled(doRemove);
623 comboBox.setCurrentIndex(idx);
624 };
625
626 doIt(m_cfg.m_jobRemovalPolicy, *ui->cbGuiRemoveJobs, *ui->cbGuiJobRemovalPolicy);
627 doIt(m_cfg.m_jobRemovalOnExitPolicy, *ui->cbGuiRemoveJobsOnExit, *ui->cbGuiJobRemovalOnExitPolicy);
628 }
629
630 void
setupCommonLanguages(bool withISO639_3)631 PreferencesDialog::setupCommonLanguages(bool withISO639_3) {
632 auto &allLanguages = withISO639_3 ? App::iso639Languages() : App::iso639_2Languages();
633 auto languageItems = QList<Util::SideBySideMultiSelect::Item>::fromVector(Util::stdVectorToQVector<Util::SideBySideMultiSelect::Item>(allLanguages));
634
635 ui->tbOftenUsedLanguages->setItems(languageItems, m_cfg.m_oftenUsedLanguages);
636 ui->cbOftenUsedLanguagesOnly->setChecked(m_cfg.m_oftenUsedLanguagesOnly && !m_cfg.m_oftenUsedLanguages.isEmpty());
637 enableOftendUsedLanguagesOnly();
638
639 ui->tbMEnableMuxingTracksByLanguage->setItems(languageItems, m_cfg.m_enableMuxingTracksByTheseLanguages);
640 ui->tbMDeriveTrackLanguageRecognizedLanguages->setItems(languageItems, m_cfg.m_recognizedTrackLanguagesInFileNames);
641 }
642
643 void
setupCommonRegions()644 PreferencesDialog::setupCommonRegions() {
645 auto &allRegions = App::regions();
646
647 ui->tbOftenUsedRegions->setItems(QList<Util::SideBySideMultiSelect::Item>::fromVector(Util::stdVectorToQVector<Util::SideBySideMultiSelect::Item>(allRegions)), m_cfg.m_oftenUsedRegions);
648 ui->cbOftenUsedRegionsOnly->setChecked(m_cfg.m_oftenUsedRegionsOnly && !m_cfg.m_oftenUsedRegions.isEmpty());
649 enableOftendUsedRegionsOnly();
650 }
651
652 void
setupCommonCharacterSets()653 PreferencesDialog::setupCommonCharacterSets() {
654 ui->tbOftenUsedCharacterSets->setItems(QList<QString>::fromVector(Util::stdVectorToQVector<QString>(App::characterSets())), m_cfg.m_oftenUsedCharacterSets);
655 ui->cbOftenUsedCharacterSetsOnly->setChecked(m_cfg.m_oftenUsedCharacterSetsOnly && !m_cfg.m_oftenUsedCharacterSets.isEmpty());
656 enableOftendUsedCharacterSetsOnly();
657 }
658
659 void
setupProcessPriority()660 PreferencesDialog::setupProcessPriority() {
661 #if defined(SYS_WINDOWS)
662 ui->cbMProcessPriority->addItem(QY("Highest priority"), static_cast<int>(Util::Settings::HighestPriority)); // value 4, index 0
663 ui->cbMProcessPriority->addItem(QY("Higher priority"), static_cast<int>(Util::Settings::HighPriority)); // value 3, index 1
664 #endif
665 ui->cbMProcessPriority->addItem(QY("Normal priority"), static_cast<int>(Util::Settings::NormalPriority)); // value 2, index 2/0
666 ui->cbMProcessPriority->addItem(QY("Lower priority"), static_cast<int>(Util::Settings::LowPriority)); // value 1, index 3/1
667 ui->cbMProcessPriority->addItem(QY("Lowest priority"), static_cast<int>(Util::Settings::LowestPriority)); // value 0, index 4/2
668
669 auto numPrios = ui->cbMProcessPriority->count();
670 auto selected = 4 - static_cast<int>(m_cfg.m_priority) - (5 - numPrios);
671 selected = std::min<int>(std::max<int>(0, selected), numPrios);
672
673 ui->cbMProcessPriority->setCurrentIndex(selected);
674 }
675
676 void
setupPlaylistScanningPolicy()677 PreferencesDialog::setupPlaylistScanningPolicy() {
678 auto selected = std::max(std::min(static_cast<int>(m_cfg.m_scanForPlaylistsPolicy), 2), 0);
679
680 ui->cbMScanPlaylistsPolicy->setCurrentIndex(selected);
681 ui->sbMMinPlaylistDuration->setValue(m_cfg.m_minimumPlaylistDuration);
682
683 adjustPlaylistControls();
684
685 Util::fixComboBoxViewWidth(*ui->cbMScanPlaylistsPolicy);
686 }
687
688 void
setupOutputFileNamePolicy()689 PreferencesDialog::setupOutputFileNamePolicy() {
690 auto isChecked = Util::Settings::DontSetOutputFileName != m_cfg.m_outputFileNamePolicy;
691 auto rbToCheck = Util::Settings::ToPreviousDirectory == m_cfg.m_outputFileNamePolicy ? ui->rbMAutoSetPreviousDirectory
692 : Util::Settings::ToFixedDirectory == m_cfg.m_outputFileNamePolicy ? ui->rbMAutoSetFixedDirectory
693 : Util::Settings::ToRelativeOfFirstInputFile == m_cfg.m_outputFileNamePolicy ? ui->rbMAutoSetRelativeDirectory
694 : ui->rbMAutoSetSameDirectory;
695
696 auto dFixed = QDir::toNativeSeparators(m_cfg.m_fixedOutputDir.path());
697 auto dRelative = QDir::toNativeSeparators(m_cfg.m_relativeOutputDir.path());
698
699 m_cfg.m_mergeLastFixedOutputDirs.add(dFixed);
700 m_cfg.m_mergeLastRelativeOutputDirs.add(dRelative);
701
702 ui->cbMAutoSetOutputFileName->setChecked(isChecked);
703 ui->cbMAutoDestinationOnlyForVideoFiles->setChecked(m_cfg.m_autoDestinationOnlyForVideoFiles);
704 ui->cbMSetDestinationFromTitle->setChecked(m_cfg.m_mergeSetDestinationFromTitle);
705 rbToCheck->setChecked(true);
706 ui->cbMAutoSetRelativeDirectory->addItems(m_cfg.m_mergeLastRelativeOutputDirs.items());
707 ui->cbMAutoSetRelativeDirectory->setCurrentText(dRelative);
708 ui->cbMAutoSetRelativeDirectory->lineEdit()->setClearButtonEnabled(true);
709 ui->cbMAutoSetFixedDirectory->addItems(m_cfg.m_mergeLastFixedOutputDirs.items());
710 ui->cbMAutoSetFixedDirectory->setCurrentText(dFixed);
711 ui->cbMAutoSetFixedDirectory->lineEdit()->setClearButtonEnabled(true);
712 ui->cbMUniqueOutputFileNames->setChecked(m_cfg.m_uniqueOutputFileNames);
713 ui->cbMAutoClearOutputFileName->setChecked(m_cfg.m_autoClearOutputFileName);
714
715 enableOutputFileNameControls();
716 }
717
718 void
setupRecentDestinationDirectoryList()719 PreferencesDialog::setupRecentDestinationDirectoryList() {
720 ui->lwMRecentDestinationDirectories->setItems(m_cfg.m_mergeLastOutputDirs.items());
721 ui->lwMRecentDestinationDirectories->setMaximumNumItems(m_cfg.m_mergeLastOutputDirs.maximumNumItems());
722 ui->lwMRecentDestinationDirectories->setAddItemDialogTexts(QY("Select a directory"), {});
723 ui->lwMRecentDestinationDirectories->setItemType(Util::StringListConfigurationWidget::ItemType::Directory);
724 }
725
726 void
setupEnableMuxingTracksByType()727 PreferencesDialog::setupEnableMuxingTracksByType() {
728 auto allTypes = Util::SideBySideMultiSelect::ItemList{};
729 auto selectedTypes = QStringList{};
730
731 for (auto type = static_cast<int>(Merge::TrackType::Min); type <= static_cast<int>(Merge::TrackType::Max); ++type)
732 allTypes << std::make_pair(Merge::Track::nameForType(static_cast<Merge::TrackType>(type)), QString::number(type));
733
734 for (auto type : m_cfg.m_enableMuxingTracksByTheseTypes)
735 selectedTypes << QString::number(static_cast<int>(type));
736
737 ui->tbMEnableMuxingTracksByType->setItems(allTypes, selectedTypes);
738 }
739
740 void
setupEnableMuxingTracksByLanguage()741 PreferencesDialog::setupEnableMuxingTracksByLanguage() {
742 auto widgets = QList<QWidget *>{} << ui->lMEnableMuxingAllTracksOfType << ui->gbMEnableMuxingExceptions << ui->cbMEnableMuxingAllVideoTracks << ui->cbMEnableMuxingAllAudioTracks << ui->cbMEnableMuxingAllSubtitleTracks << ui->tbMEnableMuxingTracksByLanguage;
743 for (auto const &widget : widgets)
744 widget->setEnabled(m_cfg.m_enableMuxingTracksByLanguage);
745
746 ui->cbMEnableMuxingTracksByLanguage->setChecked(m_cfg.m_enableMuxingTracksByLanguage);
747 ui->cbMEnableMuxingAllVideoTracks->setChecked(m_cfg.m_enableMuxingAllVideoTracks);
748 ui->cbMEnableMuxingAllAudioTracks->setChecked(m_cfg.m_enableMuxingAllAudioTracks);
749 ui->cbMEnableMuxingAllSubtitleTracks->setChecked(m_cfg.m_enableMuxingAllSubtitleTracks);
750 }
751
752 void
setupMergeAddingAppendingFilesPolicy()753 PreferencesDialog::setupMergeAddingAppendingFilesPolicy() {
754 auto setup = [](QComboBox &cb, Util::Settings::MergeAddingAppendingFilesPolicy policy) {
755 cb.addItem(QY("Always ask the user"), static_cast<int>(Util::Settings::MergeAddingAppendingFilesPolicy::Ask));
756 cb.addItem(QY("Add all files to the current multiplex settings"), static_cast<int>(Util::Settings::MergeAddingAppendingFilesPolicy::Add));
757 cb.addItem(QY("Create one new multiplex settings tab and add all files there"), static_cast<int>(Util::Settings::MergeAddingAppendingFilesPolicy::AddToNew));
758 cb.addItem(QY("Create one new multiplex settings tab for each file"), static_cast<int>(Util::Settings::MergeAddingAppendingFilesPolicy::AddEachToNew));
759
760 Util::setComboBoxIndexIf(&cb, [policy](QString const &, QVariant const &data) {
761 return data.isValid() && (static_cast<Util::Settings::MergeAddingAppendingFilesPolicy>(data.toInt()) == policy);
762 });
763
764 Util::fixComboBoxViewWidth(cb);
765 };
766
767 setup(*ui->cbMAddingAppendingFilesPolicy, m_cfg.m_mergeAddingAppendingFilesPolicy);
768 setup(*ui->cbMDragAndDropFilesPolicy, m_cfg.m_mergeDragAndDropFilesPolicy);
769
770 ui->cbMAlwaysCreateSettingsForVideoFiles->setChecked(m_cfg.m_mergeAlwaysCreateNewSettingsForVideoFiles);
771 }
772
773 void
setupMergeWarnMissingAudioTrack()774 PreferencesDialog::setupMergeWarnMissingAudioTrack() {
775 ui->cbMWarnMissingAudioTrack->addItem(QY("Never"), static_cast<int>(Util::Settings::MergeMissingAudioTrackPolicy::Never));
776 ui->cbMWarnMissingAudioTrack->addItem(QY("If audio tracks are present but none is enabled"), static_cast<int>(Util::Settings::MergeMissingAudioTrackPolicy::IfAudioTrackPresent));
777 ui->cbMWarnMissingAudioTrack->addItem(QY("Even if no audio tracks are present"), static_cast<int>(Util::Settings::MergeMissingAudioTrackPolicy::Always));
778
779 Util::setComboBoxIndexIf(ui->cbMWarnMissingAudioTrack, [this](QString const &, QVariant const &data) {
780 return data.isValid() && (static_cast<Util::Settings::MergeMissingAudioTrackPolicy>(data.toInt()) == m_cfg.m_mergeWarnMissingAudioTrack);
781 });
782
783 Util::fixComboBoxViewWidth(*ui->cbMWarnMissingAudioTrack);
784 }
785
786 void
setupMergePredefinedItems()787 PreferencesDialog::setupMergePredefinedItems() {
788 auto &cfg = Util::Settings::get();
789
790 ui->lwMPredefinedVideoTrackNames->setItems(cfg.m_mergePredefinedVideoTrackNames);
791 ui->lwMPredefinedAudioTrackNames->setItems(cfg.m_mergePredefinedAudioTrackNames);
792 ui->lwMPredefinedSubtitleTrackNames->setItems(cfg.m_mergePredefinedSubtitleTrackNames);
793 ui->lwMPredefinedSplitSizes->setItems(cfg.m_mergePredefinedSplitSizes);
794 ui->lwMPredefinedSplitDurations->setItems(cfg.m_mergePredefinedSplitDurations);
795
796 auto widgets = QList<mtx::gui::Util::StringListConfigurationWidget *>{} << ui->lwMPredefinedVideoTrackNames << ui->lwMPredefinedAudioTrackNames << ui->lwMPredefinedSubtitleTrackNames;
797 for (auto const &widget : widgets)
798 widget->setAddItemDialogTexts(QY("Enter predefined track name"), QY("Please enter the new predefined track name."));
799
800 ui->lwMPredefinedSplitSizes->setAddItemDialogTexts(QY("Enter predefined split size"), QY("Please enter the new predefined split size."));
801 ui->lwMPredefinedSplitDurations->setAddItemDialogTexts(QY("Enter predefined split duration"), QY("Please enter the new predefined split duration."));
802 }
803
804 void
setupHeaderEditorDroppedFilesPolicy()805 PreferencesDialog::setupHeaderEditorDroppedFilesPolicy() {
806 ui->cbHEDroppedFilesPolicy->addItem(QY("Always ask the user"), static_cast<int>(Util::Settings::HeaderEditorDroppedFilesPolicy::Ask));
807 ui->cbHEDroppedFilesPolicy->addItem(QY("Open all files as tabs in the header editor"), static_cast<int>(Util::Settings::HeaderEditorDroppedFilesPolicy::Open));
808 ui->cbHEDroppedFilesPolicy->addItem(QY("Add all files as new attachments to the current tab"), static_cast<int>(Util::Settings::HeaderEditorDroppedFilesPolicy::AddAttachments));
809
810 Util::setComboBoxIndexIf(ui->cbHEDroppedFilesPolicy, [this](QString const &, QVariant const &data) {
811 return data.isValid() && (static_cast<Util::Settings::HeaderEditorDroppedFilesPolicy>(data.toInt()) == m_cfg.m_headerEditorDroppedFilesPolicy);
812 });
813
814 Util::fixComboBoxViewWidth(*ui->cbHEDroppedFilesPolicy);
815 }
816
817 void
setupTrackPropertiesLayout()818 PreferencesDialog::setupTrackPropertiesLayout() {
819 auto rbToCheck = Util::Settings::TrackPropertiesLayout::HorizontalScrollArea == m_cfg.m_mergeTrackPropertiesLayout ? ui->rbMTrackPropertiesLayoutHorizontalScrollArea
820 : Util::Settings::TrackPropertiesLayout::HorizontalTwoColumns == m_cfg.m_mergeTrackPropertiesLayout ? ui->rbMTrackPropertiesLayoutHorizontalTwoColumns
821 : ui->rbMTrackPropertiesLayoutVerticalTabWidget;
822
823 rbToCheck->setChecked(true);
824 }
825
826 void
setupBCP47LanguageEditMode()827 PreferencesDialog::setupBCP47LanguageEditMode() {
828 ui->cbGuiBCP47LanguageEditingMode->clear();
829 ui->cbGuiBCP47LanguageEditingMode->addItem(QY("Free-form input"), static_cast<int>(Util::Settings::BCP47LanguageEditingMode::FreeForm));
830 ui->cbGuiBCP47LanguageEditingMode->addItem(QY("Individually selected components"), static_cast<int>(Util::Settings::BCP47LanguageEditingMode::Components));
831
832 Util::setComboBoxIndexIf(ui->cbGuiBCP47LanguageEditingMode, [this](auto const &, auto const &data) {
833 return data.toInt() == static_cast<int>(m_cfg.m_bcp47LanguageEditingMode);
834 });
835 }
836
837 void
setupTabPositions()838 PreferencesDialog::setupTabPositions() {
839 ui->cbGuiTabPositions->clear();
840 ui->cbGuiTabPositions->addItem(QY("Top"), static_cast<int>(QTabWidget::North));
841 ui->cbGuiTabPositions->addItem(QY("Bottom"), static_cast<int>(QTabWidget::South));
842 ui->cbGuiTabPositions->addItem(QY("Left"), static_cast<int>(QTabWidget::West));
843 ui->cbGuiTabPositions->addItem(QY("Right"), static_cast<int>(QTabWidget::East));
844
845 Util::setComboBoxIndexIf(ui->cbGuiTabPositions, [this](QString const &, QVariant const &data) {
846 return data.toInt() == static_cast<int>(m_cfg.m_tabPosition);
847 });
848 }
849
850 void
setupDerivingTrackLanguagesFromFileName()851 PreferencesDialog::setupDerivingTrackLanguagesFromFileName() {
852 auto setupComboBox = [](QComboBox &cb, Util::Settings::DeriveLanguageFromFileNamePolicy policy) {
853 cb.clear();
854 cb.addItem(QY("Never"), static_cast<int>(Util::Settings::DeriveLanguageFromFileNamePolicy::Never));
855 cb.addItem(QY("Only if the source doesn't contain a language"), static_cast<int>(Util::Settings::DeriveLanguageFromFileNamePolicy::OnlyIfAbsent));
856 cb.addItem(QY("Also if the language is 'undetermined' ('und')"), static_cast<int>(Util::Settings::DeriveLanguageFromFileNamePolicy::IfAbsentOrUndetermined));
857
858 Util::fixComboBoxViewWidth(cb);
859
860 Util::setComboBoxIndexIf(&cb, [policy](QString const &, QVariant const &data) {
861 return data.toInt() == static_cast<int>(policy);
862 });
863 };
864
865 setupComboBox(*ui->cbMDeriveAudioTrackLanguageFromFileName, m_cfg.m_deriveAudioTrackLanguageFromFileNamePolicy);
866 setupComboBox(*ui->cbMDeriveVideoTrackLanguageFromFileName, m_cfg.m_deriveVideoTrackLanguageFromFileNamePolicy);
867 setupComboBox(*ui->cbMDeriveSubtitleTrackLanguageFromFileName, m_cfg.m_deriveSubtitleTrackLanguageFromFileNamePolicy);
868
869 ui->leMDeriveTrackLanguageBoundaryChars->setText(m_cfg.m_boundaryCharsForDerivingTrackLanguagesFromFileNames);
870 }
871
872 void
setupWhenToSetDefaultLanguage()873 PreferencesDialog::setupWhenToSetDefaultLanguage() {
874 ui->cbMWhenToSetDefaultLanguage->clear();
875 ui->cbMWhenToSetDefaultLanguage->addItem(QY("Only if the source doesn't contain a language"), static_cast<int>(Util::Settings::SetDefaultLanguagePolicy::OnlyIfAbsent));
876 ui->cbMWhenToSetDefaultLanguage->addItem(QY("Also if the language is 'undetermined' ('und')"), static_cast<int>(Util::Settings::SetDefaultLanguagePolicy::IfAbsentOrUndetermined));
877
878 Util::setComboBoxIndexIf(ui->cbMWhenToSetDefaultLanguage, [this](QString const &, QVariant const &data) {
879 return data.toInt() == static_cast<int>(m_cfg.m_whenToSetDefaultLanguage);
880 });
881 }
882
883 void
setupJobsRunPrograms()884 PreferencesDialog::setupJobsRunPrograms() {
885 ui->twJobsPrograms->setTabPosition(m_cfg.m_tabPosition);
886
887 for (auto const &runProgramConfig : m_cfg.m_runProgramConfigurations) {
888 auto widget = new PrefsRunProgramWidget{ui->twJobsPrograms, *runProgramConfig};
889 ui->twJobsPrograms->addTab(widget, {});
890
891 setTabTitleForRunProgramWidget(ui->twJobsPrograms->count() - 1, runProgramConfig->name());
892
893 connect(widget, &PrefsRunProgramWidget::titleChanged, this, &PreferencesDialog::setSendersTabTitleForRunProgramWidget);
894 }
895
896 if (!m_cfg.m_runProgramConfigurations.isEmpty())
897 ui->swJobsPrograms->setCurrentIndex(1);
898
899 connect(ui->pbJobsAddProgram, &QPushButton::clicked, this, &PreferencesDialog::addProgramToExecute);
900 connect(ui->twJobsPrograms, &QTabWidget::tabCloseRequested, this, &PreferencesDialog::removeProgramToExecute);
901 }
902
903 void
setupFontAndScaling()904 PreferencesDialog::setupFontAndScaling() {
905 auto font = App::font();
906 ui->fcbGuiFontFamily->setCurrentFont(font);
907 ui->sbGuiFontPointSize->setValue(font.pointSize());
908
909 ui->cbGuiDisableHighDPIScaling->setChecked(m_cfg.m_uiDisableHighDPIScaling);
910 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
911 ui->cbGuiDisableHighDPIScaling->setVisible(false);
912 #endif
913
914 ui->cbGuiDisableDarkStyleSheet->setChecked(m_cfg.m_uiDisableDarkStyleSheet);
915 #if !defined(SYS_WINDOWS)
916 ui->cbGuiDisableDarkStyleSheet->setVisible(false);
917 #endif
918
919 ui->cbGuiStayOnTop->setChecked(m_cfg.m_uiStayOnTop);
920 }
921
922 void
setupFileColorsControls()923 PreferencesDialog::setupFileColorsControls() {
924 ui->cbMUseFileAndTrackColors->setChecked(m_cfg.m_mergeUseFileAndTrackColors);
925 setupFileColors(m_cfg.m_mergeFileColors);
926
927 enableFileColorsControls();
928
929 connect(ui->cbMUseFileAndTrackColors, &QCheckBox::toggled, this, &PreferencesDialog::enableFileColorsControls);
930 connect(ui->lwMFileColors, &QListWidget::itemSelectionChanged, this, &PreferencesDialog::enableFileColorsControls);
931 connect(ui->lwMFileColors, &QListWidget::itemDoubleClicked, this, &PreferencesDialog::editFileColor);
932 connect(ui->pbMAddFileColor, &QPushButton::clicked, this, &PreferencesDialog::addFileColor);
933 connect(ui->pbMRemoveFileColors, &QPushButton::clicked, this, &PreferencesDialog::removeFileColors);
934 connect(ui->pbMEditFileColor, &QPushButton::clicked, this, &PreferencesDialog::editSelectedFileColor);
935 connect(ui->pbMRevertFileColorsToDefault, &QPushButton::clicked, this, &PreferencesDialog::revertFileColorsToDefault);
936 }
937
938 QListWidgetItem &
setupFileColorItem(QListWidgetItem & item,QColor const & color)939 PreferencesDialog::setupFileColorItem(QListWidgetItem &item,
940 QColor const &color) {
941 auto luminance = (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255.0;
942 auto foreground = luminance > 0.5 ? 0 : 255;
943
944 item.setForeground(QColor{foreground, foreground, foreground});
945 item.setBackground(color);
946 item.setText(Q(fmt::format("0x{0:02x}{1:02x}{2:02x}", color.red(), color.green(), color.blue())));
947
948 return item;
949 }
950
951 void
setupFileColors(QVector<QColor> const & colors)952 PreferencesDialog::setupFileColors(QVector<QColor> const &colors) {
953 ui->lwMFileColors->clear();
954
955 for (auto const &color : colors)
956 ui->lwMFileColors->addItem(&setupFileColorItem(*new QListWidgetItem, color));
957
958 enableFileColorsControls();
959 }
960
961 void
saveFileColors()962 PreferencesDialog::saveFileColors() {
963 auto &colors = m_cfg.m_mergeFileColors;
964 auto numRows = ui->lwMFileColors->count();
965
966 colors.clear();
967 colors.reserve(numRows);
968
969 for (int row = 0; row < numRows; ++row)
970 colors << ui->lwMFileColors->item(row)->background().color();
971
972 m_cfg.m_mergeUseFileAndTrackColors = ui->cbMUseFileAndTrackColors->isChecked();
973 }
974
975 void
enableFileColorsControls()976 PreferencesDialog::enableFileColorsControls() {
977 auto useColors = ui->cbMUseFileAndTrackColors->isChecked();
978 auto items = ui->lwMFileColors->selectedItems();
979
980 ui->lwMFileColors->setEnabled(useColors);
981 ui->pbMAddFileColor->setEnabled(useColors);
982 ui->pbMRevertFileColorsToDefault->setEnabled(useColors);
983 ui->pbMRemoveFileColors->setEnabled(useColors && !items.isEmpty());
984 ui->pbMEditFileColor->setEnabled(useColors && (items.size() == 1));
985 }
986
987 void
addFileColor()988 PreferencesDialog::addFileColor() {
989 QColorDialog dlg{this};
990
991 if (dlg.exec())
992 ui->lwMFileColors->addItem(&setupFileColorItem(*new QListWidgetItem, dlg.currentColor()));
993 }
994
995 void
removeFileColors()996 PreferencesDialog::removeFileColors() {
997 for (auto const &item : ui->lwMFileColors->selectedItems())
998 delete item;
999
1000 enableFileColorsControls();
1001 }
1002
1003 void
editFileColor(QListWidgetItem * item)1004 PreferencesDialog::editFileColor(QListWidgetItem *item) {
1005 if (!item)
1006 return;
1007
1008 QColorDialog dlg{item->background().color(), this};
1009
1010 if (dlg.exec())
1011 setupFileColorItem(*item, dlg.currentColor());
1012 }
1013
1014 void
editSelectedFileColor()1015 PreferencesDialog::editSelectedFileColor() {
1016 auto items = ui->lwMFileColors->selectedItems();
1017 if (!items.isEmpty())
1018 editFileColor(items.first());
1019 }
1020
1021 void
revertFileColorsToDefault()1022 PreferencesDialog::revertFileColorsToDefault() {
1023 setupFileColors(Util::Settings::defaultFileColors());
1024 }
1025
1026 void
save()1027 PreferencesDialog::save() {
1028 // GUI page:
1029 m_cfg.m_uiLocale = ui->cbGuiInterfaceLanguage->currentData().toString();
1030 m_cfg.m_tabPosition = static_cast<QTabWidget::TabPosition>(ui->cbGuiTabPositions->currentData().toInt());
1031 m_cfg.m_bcp47LanguageEditingMode = static_cast<Util::Settings::BCP47LanguageEditingMode>(ui->cbGuiBCP47LanguageEditingMode->currentData().toInt());
1032 m_cfg.m_uiFontFamily = ui->fcbGuiFontFamily->currentFont().family();
1033 m_cfg.m_uiFontPointSize = ui->sbGuiFontPointSize->value();
1034 m_cfg.m_uiStayOnTop = ui->cbGuiStayOnTop->isChecked();
1035 m_cfg.m_uiDisableHighDPIScaling = ui->cbGuiDisableHighDPIScaling->isChecked();
1036 m_cfg.m_uiDisableDarkStyleSheet = ui->cbGuiDisableDarkStyleSheet->isChecked();
1037 m_cfg.m_uiDisableToolTips = ui->cbGuiDisableToolTips->isChecked();
1038 m_cfg.m_checkForUpdates = ui->cbGuiCheckForUpdates->isChecked();
1039 m_cfg.m_showDebuggingMenu = ui->cbGuiShowDebuggingMenu->isChecked();
1040 m_cfg.m_showToolSelector = ui->cbGuiShowToolSelector->isChecked();
1041 m_cfg.m_showMoveUpDownButtons = ui->cbGuiShowMoveUpDownButtons->isChecked();
1042 m_cfg.m_elideTabHeaderLabels = ui->cbGuiElideTabHeaderLabels->isChecked();
1043 m_cfg.m_useLegacyFontMIMETypes = ui->cbGuiUseLegacyFontMIMETypes->isChecked();
1044 m_cfg.m_warnBeforeClosingModifiedTabs = ui->cbGuiWarnBeforeClosingModifiedTabs->isChecked();
1045 m_cfg.m_warnBeforeAbortingJobs = ui->cbGuiWarnBeforeAbortingJobs->isChecked();
1046 m_cfg.m_warnBeforeOverwriting = ui->cbGuiWarnBeforeOverwriting->isChecked();
1047 m_cfg.m_useDefaultJobDescription = ui->cbGuiUseDefaultJobDescription->isChecked();
1048 m_cfg.m_showOutputOfAllJobs = ui->cbGuiShowOutputOfAllJobs->isChecked();
1049 m_cfg.m_switchToJobOutputAfterStarting = ui->cbGuiSwitchToJobOutputAfterStarting->isChecked();
1050 m_cfg.m_resetJobWarningErrorCountersOnExit = ui->cbGuiResetJobWarningErrorCountersOnExit->isChecked();
1051 m_cfg.m_removeOutputFileOnJobFailure = ui->cbGuiRemoveOutputFileOnJobFailure->isChecked();
1052 auto idx = !ui->cbGuiRemoveJobs ->isChecked() ? 0 : ui->cbGuiJobRemovalPolicy ->currentIndex() + 1;
1053 auto idxOnExit = !ui->cbGuiRemoveJobsOnExit->isChecked() ? 0 : ui->cbGuiJobRemovalOnExitPolicy->currentIndex() + 1;
1054 m_cfg.m_jobRemovalPolicy = static_cast<Util::Settings::JobRemovalPolicy>(idx);
1055 m_cfg.m_jobRemovalOnExitPolicy = static_cast<Util::Settings::JobRemovalPolicy>(idxOnExit);
1056 m_cfg.m_removeOldJobs = ui->cbGuiRemoveOldJobs->isChecked();
1057 m_cfg.m_removeOldJobsDays = ui->sbGuiRemoveOldJobsDays->value();
1058 m_cfg.m_maximumConcurrentJobs = ui->sbGuiMaximumConcurrentJobs->value();
1059
1060 m_cfg.m_chapterNameTemplate = ui->leCENameTemplate->text();
1061 m_cfg.m_ceTextFileCharacterSet = ui->cbCETextFileCharacterSet->currentData().toString();
1062 m_cfg.m_defaultChapterLanguage = ui->ldwCEDefaultLanguage->language();
1063 m_cfg.m_dropLastChapterFromBlurayPlaylist = ui->cbCEDropLastFromBlurayPlaylist->isChecked();
1064
1065 // Merge page:
1066 m_cfg.m_mediaInfoExe = ui->leMMediaInfoExe->text();
1067 m_cfg.m_autoSetFileTitle = ui->cbMAutoSetFileTitle->isChecked();
1068 m_cfg.m_autoClearFileTitle = ui->cbMAutoClearFileTitle->isChecked();
1069 m_cfg.m_setAudioDelayFromFileName = ui->cbMSetAudioDelayFromFileName->isChecked();
1070 m_cfg.m_disableCompressionForAllTrackTypes = ui->cbMDisableCompressionForAllTrackTypes->isChecked();
1071 m_cfg.m_disableDefaultTrackForSubtitles = ui->cbMDisableDefaultTrackForSubtitles->isChecked();
1072 m_cfg.m_mergeEnableDialogNormGainRemoval = ui->cbMEnableDialogNormGainRemoval->isChecked();
1073 m_cfg.m_mergeAddingAppendingFilesPolicy = static_cast<Util::Settings::MergeAddingAppendingFilesPolicy>(ui->cbMAddingAppendingFilesPolicy->currentData().toInt());
1074 m_cfg.m_mergeDragAndDropFilesPolicy = static_cast<Util::Settings::MergeAddingAppendingFilesPolicy>(ui->cbMDragAndDropFilesPolicy->currentData().toInt());
1075 m_cfg.m_mergeAlwaysCreateNewSettingsForVideoFiles = ui->cbMAlwaysCreateSettingsForVideoFiles->isChecked();
1076 m_cfg.m_mergeSortFilesTracksByTypeWhenAdding = ui->cbMSortFilesTracksByTypeWhenAdding->isChecked();
1077 m_cfg.m_mergeReconstructSequencesWhenAdding = ui->cbMReconstructSequencesWhenAdding->isChecked();
1078 m_cfg.m_mergeAlwaysShowOutputFileControls = ui->cbMAlwaysShowOutputFileControls->isChecked();
1079 m_cfg.m_mergeWarnMissingAudioTrack = static_cast<Util::Settings::MergeMissingAudioTrackPolicy>(ui->cbMWarnMissingAudioTrack->currentData().toInt());
1080 m_cfg.m_mergeTrackPropertiesLayout = ui->rbMTrackPropertiesLayoutHorizontalScrollArea->isChecked() ? Util::Settings::TrackPropertiesLayout::HorizontalScrollArea
1081 : ui->rbMTrackPropertiesLayoutHorizontalTwoColumns->isChecked() ? Util::Settings::TrackPropertiesLayout::HorizontalTwoColumns
1082 : Util::Settings::TrackPropertiesLayout::VerticalTabWidget;
1083 m_cfg.m_clearMergeSettings = static_cast<Util::Settings::ClearMergeSettingsAction>(ui->cbMClearMergeSettings->currentIndex());
1084 m_cfg.m_defaultAudioTrackLanguage = ui->ldwMDefaultAudioTrackLanguage->language();
1085 m_cfg.m_defaultVideoTrackLanguage = ui->ldwMDefaultVideoTrackLanguage->language();
1086 m_cfg.m_defaultSubtitleTrackLanguage = ui->ldwMDefaultSubtitleTrackLanguage->language();
1087 m_cfg.m_whenToSetDefaultLanguage = static_cast<Util::Settings::SetDefaultLanguagePolicy>(ui->cbMWhenToSetDefaultLanguage->currentData().toInt());
1088 m_cfg.m_defaultSubtitleCharset = ui->cbMDefaultSubtitleCharset->currentData().toString();
1089 m_cfg.m_priority = static_cast<Util::Settings::ProcessPriority>(ui->cbMProcessPriority->currentData().toInt());
1090 m_cfg.m_defaultAdditionalMergeOptions = ui->leMDefaultAdditionalCommandLineOptions->text();
1091 m_cfg.m_probeRangePercentage = ui->cbMProbeRangePercentage->value();
1092
1093 m_cfg.m_deriveAudioTrackLanguageFromFileNamePolicy = static_cast<Util::Settings::DeriveLanguageFromFileNamePolicy>(ui->cbMDeriveAudioTrackLanguageFromFileName ->currentData().toInt());
1094 m_cfg.m_deriveVideoTrackLanguageFromFileNamePolicy = static_cast<Util::Settings::DeriveLanguageFromFileNamePolicy>(ui->cbMDeriveVideoTrackLanguageFromFileName ->currentData().toInt());
1095 m_cfg.m_deriveSubtitleTrackLanguageFromFileNamePolicy = static_cast<Util::Settings::DeriveLanguageFromFileNamePolicy>(ui->cbMDeriveSubtitleTrackLanguageFromFileName->currentData().toInt());
1096 m_cfg.m_boundaryCharsForDerivingTrackLanguagesFromFileNames = ui->leMDeriveTrackLanguageBoundaryChars->text();
1097 m_cfg.m_recognizedTrackLanguagesInFileNames = ui->tbMDeriveTrackLanguageRecognizedLanguages->selectedItemValues();
1098
1099 m_cfg.m_scanForPlaylistsPolicy = static_cast<Util::Settings::ScanForPlaylistsPolicy>(ui->cbMScanPlaylistsPolicy->currentIndex());
1100 m_cfg.m_minimumPlaylistDuration = ui->sbMMinPlaylistDuration->value();
1101 m_cfg.m_mergeAddBlurayCovers = ui->cbMAddBlurayCovers->isChecked();
1102 m_cfg.m_mergeAttachmentsAlwaysSkipForExistingName = ui->cbMAttachmentAlwaysSkipForExistingName->isChecked();
1103
1104 m_cfg.m_outputFileNamePolicy = !ui->cbMAutoSetOutputFileName->isChecked() ? Util::Settings::DontSetOutputFileName
1105 : ui->rbMAutoSetRelativeDirectory->isChecked() ? Util::Settings::ToRelativeOfFirstInputFile
1106 : ui->rbMAutoSetFixedDirectory->isChecked() ? Util::Settings::ToFixedDirectory
1107 : ui->rbMAutoSetPreviousDirectory->isChecked() ? Util::Settings::ToPreviousDirectory
1108 : Util::Settings::ToSameAsFirstInputFile;
1109 m_cfg.m_autoDestinationOnlyForVideoFiles = ui->cbMAutoDestinationOnlyForVideoFiles->isChecked();
1110 m_cfg.m_mergeSetDestinationFromTitle = ui->cbMSetDestinationFromTitle->isChecked();
1111 m_cfg.m_relativeOutputDir.setPath(ui->cbMAutoSetRelativeDirectory->currentText());
1112 m_cfg.m_fixedOutputDir.setPath(ui->cbMAutoSetFixedDirectory->currentText());
1113 m_cfg.m_uniqueOutputFileNames = ui->cbMUniqueOutputFileNames->isChecked();
1114 m_cfg.m_autoClearOutputFileName = ui->cbMAutoClearOutputFileName->isChecked();
1115
1116 m_cfg.m_mergeLastFixedOutputDirs.add(QDir::toNativeSeparators(m_cfg.m_fixedOutputDir.path()));
1117 m_cfg.m_mergeLastRelativeOutputDirs.add(QDir::toNativeSeparators(m_cfg.m_relativeOutputDir.path()));
1118 m_cfg.m_mergeLastOutputDirs.setItems(ui->lwMRecentDestinationDirectories->items());
1119
1120 m_cfg.m_enableMuxingTracksByLanguage = ui->cbMEnableMuxingTracksByLanguage->isChecked();
1121 m_cfg.m_enableMuxingAllVideoTracks = ui->cbMEnableMuxingAllVideoTracks->isChecked();
1122 m_cfg.m_enableMuxingAllAudioTracks = ui->cbMEnableMuxingAllAudioTracks->isChecked();
1123 m_cfg.m_enableMuxingAllSubtitleTracks = ui->cbMEnableMuxingAllSubtitleTracks->isChecked();
1124 m_cfg.m_enableMuxingTracksByTheseLanguages = ui->tbMEnableMuxingTracksByLanguage->selectedItemValues();
1125
1126 // Often used selections page:
1127 m_cfg.m_oftenUsedLanguages = ui->tbOftenUsedLanguages->selectedItemValues();
1128 m_cfg.m_oftenUsedRegions = ui->tbOftenUsedRegions->selectedItemValues();
1129 m_cfg.m_oftenUsedCharacterSets = ui->tbOftenUsedCharacterSets->selectedItemValues();
1130
1131 m_cfg.m_oftenUsedLanguagesOnly = ui->cbOftenUsedLanguagesOnly ->isChecked() && !m_cfg.m_oftenUsedLanguages .isEmpty();
1132 m_cfg.m_oftenUsedRegionsOnly = ui->cbOftenUsedRegionsOnly ->isChecked() && !m_cfg.m_oftenUsedRegions .isEmpty();
1133 m_cfg.m_oftenUsedCharacterSetsOnly = ui->cbOftenUsedCharacterSetsOnly->isChecked() && !m_cfg.m_oftenUsedCharacterSets.isEmpty();
1134 m_cfg.m_useISO639_3Languages = ui->cbUseISO639_3Languages ->isChecked();
1135
1136 // Info tool page
1137 m_cfg.m_defaultInfoJobSettings = ui->wIDefaultJobSettings->settings();
1138
1139 // Header editor page
1140 m_cfg.m_headerEditorDroppedFilesPolicy = static_cast<Util::Settings::HeaderEditorDroppedFilesPolicy>(ui->cbHEDroppedFilesPolicy->currentData().toInt());
1141 m_cfg.m_headerEditorDateTimeInUTC = ui->cbHEDateTimeInUTC->isChecked();
1142
1143 // Run programs page:
1144 m_cfg.m_runProgramConfigurations.clear();
1145 for (auto tabIdx = 0, numTabs = ui->twJobsPrograms->count(); tabIdx < numTabs; ++tabIdx) {
1146 auto widget = static_cast<PrefsRunProgramWidget *>(ui->twJobsPrograms->widget(tabIdx));
1147 auto cfg = widget->config();
1148
1149 if (!cfg->m_active || cfg->isValid())
1150 m_cfg.m_runProgramConfigurations << cfg;
1151 }
1152
1153 m_cfg.m_enableMuxingTracksByTheseTypes.clear();
1154 for (auto const &type : ui->tbMEnableMuxingTracksByType->selectedItemValues())
1155 m_cfg.m_enableMuxingTracksByTheseTypes << static_cast<Merge::TrackType>(type.toInt());
1156
1157 m_cfg.m_mergePredefinedVideoTrackNames = ui->lwMPredefinedVideoTrackNames->items();
1158 m_cfg.m_mergePredefinedAudioTrackNames = ui->lwMPredefinedAudioTrackNames->items();
1159 m_cfg.m_mergePredefinedSubtitleTrackNames = ui->lwMPredefinedSubtitleTrackNames->items();
1160 m_cfg.m_mergePredefinedSplitSizes = ui->lwMPredefinedSplitSizes->items();
1161 m_cfg.m_mergePredefinedSplitDurations = ui->lwMPredefinedSplitDurations->items();
1162
1163 saveLanguageShortcuts();
1164 saveFileColors();
1165
1166 m_cfg.save();
1167
1168 mtx::chapters::g_chapter_generation_name_template.override(to_utf8(m_cfg.m_chapterNameTemplate));
1169 }
1170
1171 void
rememberCurrentlySelectedPage()1172 PreferencesDialog::rememberCurrentlySelectedPage() {
1173 auto pageIndex = ui->pages->currentIndex();
1174 auto pageTypes = m_pageIndexes.keys(pageIndex);
1175
1176 if (!pageTypes.isEmpty())
1177 ms_previouslySelectedPage = pageTypes[0];
1178 }
1179
1180 void
enableOftendUsedLanguagesOnly()1181 PreferencesDialog::enableOftendUsedLanguagesOnly() {
1182 ui->cbOftenUsedLanguagesOnly->setEnabled(!ui->tbOftenUsedLanguages->selectedItemValues().isEmpty());
1183 }
1184
1185 void
enableOftendUsedRegionsOnly()1186 PreferencesDialog::enableOftendUsedRegionsOnly() {
1187 ui->cbOftenUsedRegionsOnly->setEnabled(!ui->tbOftenUsedRegions->selectedItemValues().isEmpty());
1188 }
1189
1190 void
enableOftendUsedCharacterSetsOnly()1191 PreferencesDialog::enableOftendUsedCharacterSetsOnly() {
1192 ui->cbOftenUsedCharacterSetsOnly->setEnabled(!ui->tbOftenUsedCharacterSets->selectedItemValues().isEmpty());
1193 }
1194
1195 void
enableOutputFileNameControls()1196 PreferencesDialog::enableOutputFileNameControls() {
1197 bool isChecked = ui->cbMAutoSetOutputFileName->isChecked();
1198 bool relativeSelected = ui->rbMAutoSetRelativeDirectory->isChecked();
1199 bool fixedSelected = ui->rbMAutoSetFixedDirectory->isChecked();
1200
1201 Util::enableWidgets(QList<QWidget *>{} << ui->gbDestinationDirectory << ui->cbMUniqueOutputFileNames << ui->cbMAutoDestinationOnlyForVideoFiles << ui->cbMSetDestinationFromTitle, isChecked);
1202 Util::enableWidgets(QList<QWidget *>{} << ui->cbMAutoSetFixedDirectory << ui->pbMBrowseAutoSetFixedDirectory, isChecked && fixedSelected);
1203 ui->cbMAutoSetRelativeDirectory->setEnabled(isChecked && relativeSelected);
1204 }
1205
1206 void
browseFixedOutputDirectory()1207 PreferencesDialog::browseFixedOutputDirectory() {
1208 auto dir = Util::getExistingDirectory(this, QY("Select destination directory"), ui->cbMAutoSetFixedDirectory->currentText());
1209 if (!dir.isEmpty())
1210 ui->cbMAutoSetFixedDirectory->setCurrentText(dir);
1211 }
1212
1213 void
browseMediaInfoExe()1214 PreferencesDialog::browseMediaInfoExe() {
1215 auto filters = QStringList{};
1216
1217 #if defined(SYS_WINDOWS)
1218 filters << QY("Executable files") + Q(" (*.exe)");
1219 #endif
1220 filters << QY("All files") + Q(" (*)");
1221
1222 auto fileName = Util::getOpenFileName(this, QY("Select executable"), ui->leMMediaInfoExe->text(), filters.join(Q(";;")));
1223 if (!fileName.isEmpty())
1224 ui->leMMediaInfoExe->setText(fileName);
1225 }
1226
1227 void
editDefaultAdditionalCommandLineOptions()1228 PreferencesDialog::editDefaultAdditionalCommandLineOptions() {
1229 Merge::AdditionalCommandLineOptionsDialog dlg{this, ui->leMDefaultAdditionalCommandLineOptions->text()};
1230 dlg.hideSaveAsDefaultCheckbox();
1231 if (dlg.exec())
1232 ui->leMDefaultAdditionalCommandLineOptions->setText(dlg.additionalOptions());
1233 }
1234
1235 void
addProgramToExecute()1236 PreferencesDialog::addProgramToExecute() {
1237 auto programWidget = new PrefsRunProgramWidget{this, {}};
1238 ui->twJobsPrograms->addTab(programWidget, programWidget->config()->name());
1239 ui->twJobsPrograms->setCurrentIndex(ui->twJobsPrograms->count() - 1);
1240
1241 ui->swJobsPrograms->setCurrentIndex(1);
1242
1243 connect(programWidget, &PrefsRunProgramWidget::titleChanged, this, &PreferencesDialog::setSendersTabTitleForRunProgramWidget);
1244 }
1245
1246 void
removeProgramToExecute(int index)1247 PreferencesDialog::removeProgramToExecute(int index) {
1248 if ((0 > index) || (ui->twJobsPrograms->count() <= index))
1249 return;
1250
1251 auto tab = qobject_cast<QWidget *>(ui->twJobsPrograms->widget(index));
1252 if (!tab)
1253 return;
1254
1255 ui->twJobsPrograms->removeTab(index);
1256 delete tab;
1257
1258 if (!ui->twJobsPrograms->count())
1259 ui->swJobsPrograms->setCurrentIndex(0);
1260 }
1261
1262 void
setSendersTabTitleForRunProgramWidget()1263 PreferencesDialog::setSendersTabTitleForRunProgramWidget() {
1264 auto widget = qobject_cast<PrefsRunProgramWidget *>(sender());
1265 auto title = widget->config()->name();
1266
1267 setTabTitleForRunProgramWidget(ui->twJobsPrograms->indexOf(widget), title);
1268 }
1269
1270 void
setTabTitleForRunProgramWidget(int tabIdx,QString const & title)1271 PreferencesDialog::setTabTitleForRunProgramWidget(int tabIdx,
1272 QString const &title) {
1273 if ((tabIdx < 0) || (tabIdx >= ui->twJobsPrograms->count()))
1274 return;
1275
1276 ui->twJobsPrograms->setTabText(tabIdx, title);
1277 }
1278
1279 void
adjustPlaylistControls()1280 PreferencesDialog::adjustPlaylistControls() {
1281 ui->sbMMinPlaylistDuration->setSuffix(QNY(" second", " seconds", ui->sbMMinPlaylistDuration->value()));
1282 }
1283
1284 void
adjustRemoveOldJobsControls()1285 PreferencesDialog::adjustRemoveOldJobsControls() {
1286 ui->sbGuiRemoveOldJobsDays->setEnabled(ui->cbGuiRemoveOldJobs->isChecked());
1287 ui->sbGuiRemoveOldJobsDays->setSuffix(QNY(" day", " days", ui->sbGuiRemoveOldJobsDays->value()));
1288 }
1289
1290 void
showPage(Page page)1291 PreferencesDialog::showPage(Page page) {
1292 auto pageIndex = m_pageIndexes[page];
1293 auto pageModelIndex = modelIndexForPage(pageIndex);
1294
1295 if (!pageModelIndex.isValid())
1296 return;
1297
1298 m_ignoreNextCurrentChange = true;
1299
1300 ui->pageSelector->selectionModel()->select(pageModelIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
1301 ui->pages->setCurrentIndex(pageIndex);
1302 }
1303
1304 void
revertDeriveTrackLanguageFromFileNameChars()1305 PreferencesDialog::revertDeriveTrackLanguageFromFileNameChars() {
1306 ui->leMDeriveTrackLanguageBoundaryChars->setText(Util::Settings::defaultBoundaryCharsForDerivingLanguageFromFileName());
1307 }
1308
1309 bool
verifyDeriveTrackLanguageSettings()1310 PreferencesDialog::verifyDeriveTrackLanguageSettings() {
1311 if ( (static_cast<Util::Settings::DeriveLanguageFromFileNamePolicy>(ui->cbMDeriveAudioTrackLanguageFromFileName ->currentData().toInt()) == Util::Settings::DeriveLanguageFromFileNamePolicy::Never)
1312 && (static_cast<Util::Settings::DeriveLanguageFromFileNamePolicy>(ui->cbMDeriveVideoTrackLanguageFromFileName ->currentData().toInt()) == Util::Settings::DeriveLanguageFromFileNamePolicy::Never)
1313 && (static_cast<Util::Settings::DeriveLanguageFromFileNamePolicy>(ui->cbMDeriveSubtitleTrackLanguageFromFileName->currentData().toInt()) == Util::Settings::DeriveLanguageFromFileNamePolicy::Never))
1314 return true;
1315
1316 if (ui->leMDeriveTrackLanguageBoundaryChars->text().isEmpty()) {
1317 Util::MessageBox::critical(this)
1318 ->title(QY("Invalid settings"))
1319 .text(QY("The list of boundary characters for deriving the track language from file names must not be empty."))
1320 .exec();
1321
1322 return false;
1323 }
1324
1325 if (ui->tbMDeriveTrackLanguageRecognizedLanguages->selectedItemValues().isEmpty()) {
1326 showPage(Page::DeriveTrackLanguage);
1327 ui->tbMDeriveTrackLanguageRecognizedLanguages->setFocus();
1328
1329 Util::MessageBox::critical(this)
1330 ->title(QY("Invalid settings"))
1331 .text(QY("The list of recognized track languages in file names must not be empty."))
1332 .exec();
1333
1334 return false;
1335 }
1336
1337 return true;
1338 }
1339
1340 bool
verifyRunProgramConfigurations()1341 PreferencesDialog::verifyRunProgramConfigurations() {
1342 for (auto tabIdx = 0, numTabs = ui->twJobsPrograms->count(); tabIdx < numTabs; ++tabIdx) {
1343 auto tab = qobject_cast<PrefsRunProgramWidget *>(ui->twJobsPrograms->widget(tabIdx));
1344 auto error = tab->validate();
1345
1346 if (error.isEmpty())
1347 continue;
1348
1349 showPage(Page::RunPrograms);
1350 ui->twJobsPrograms->setCurrentIndex(tabIdx);
1351
1352 Util::MessageBox::critical(this)
1353 ->title(QY("Invalid settings"))
1354 .text(Q("<p>%1 %2</p>"
1355 "<p>%3</p>")
1356 .arg(QY("This configuration is currently invalid.").toHtmlEscaped())
1357 .arg(error.toHtmlEscaped())
1358 .arg(QY("Either fix the error or remove the configuration before closing the preferences dialog.").toHtmlEscaped()))
1359 .exec();
1360
1361 return false;
1362 }
1363
1364 return true;
1365 }
1366
1367 void
accept()1368 PreferencesDialog::accept() {
1369 if ( verifyDeriveTrackLanguageSettings()
1370 && verifyRunProgramConfigurations()) {
1371 rememberCurrentlySelectedPage();
1372 QDialog::accept();
1373 }
1374 }
1375
1376 void
reject()1377 PreferencesDialog::reject() {
1378 rememberCurrentlySelectedPage();
1379 QDialog::reject();
1380 }
1381
1382 void
setupLanguageShortcuts()1383 PreferencesDialog::setupLanguageShortcuts() {
1384 for (auto const &formattedLanguage : m_cfg.m_languageShortcuts) {
1385 auto language = mtx::bcp47::language_c::parse(to_utf8(formattedLanguage));
1386 if (!language.is_valid())
1387 continue;
1388
1389 auto item = new QListWidgetItem{Q(language.format_long())};
1390 item->setData(Qt::UserRole, Q(language.format()));
1391
1392 ui->lwGuiLanguageShortcuts->addItem(item);
1393 }
1394
1395 enableLanguageShortcutControls();
1396 }
1397
1398 void
saveLanguageShortcuts()1399 PreferencesDialog::saveLanguageShortcuts() {
1400 m_cfg.m_languageShortcuts.clear();
1401
1402 for (auto row = 0, numItems = ui->lwGuiLanguageShortcuts->count(); row < numItems; ++row)
1403 m_cfg.m_languageShortcuts << ui->lwGuiLanguageShortcuts->item(row)->data(Qt::UserRole).toString();
1404 }
1405
1406 void
enableLanguageShortcutControls()1407 PreferencesDialog::enableLanguageShortcutControls() {
1408 auto items = ui->lwGuiLanguageShortcuts->selectedItems();
1409
1410 ui->pbGuiEditLanguageShortcut->setEnabled(items.size() == 1);
1411 ui->pbGuiRemoveLanguageShortcuts->setEnabled(items.size() > 0);
1412 }
1413
1414 void
addLanguageShortcut()1415 PreferencesDialog::addLanguageShortcut() {
1416 mtx::gui::Util::LanguageDialog dlg{this};
1417
1418 if (!dlg.exec())
1419 return;
1420
1421 auto language = dlg.language();
1422 auto item = new QListWidgetItem{Q(language.format_long())};
1423
1424 item->setData(Qt::UserRole, Q(language.format()));
1425
1426 ui->lwGuiLanguageShortcuts->addItem(item);
1427 }
1428
1429 void
editLanguageShortcut()1430 PreferencesDialog::editLanguageShortcut() {
1431 auto items = ui->lwGuiLanguageShortcuts->selectedItems();
1432 if (items.isEmpty())
1433 return;
1434
1435 auto item = items.first();
1436 auto language = mtx::bcp47::language_c::parse(to_utf8(item->data(Qt::UserRole).toString()));
1437
1438 mtx::gui::Util::LanguageDialog dlg{this};
1439
1440 if (language.is_valid())
1441 dlg.setLanguage(language);
1442
1443 if (!dlg.exec())
1444 return;
1445
1446 language = dlg.language();
1447 item->setText(Q(language.format_long()));
1448 item->setData(Qt::UserRole, Q(language.format()));
1449 }
1450
1451 void
removeLanguageShortcuts()1452 PreferencesDialog::removeLanguageShortcuts() {
1453 for (auto const &item : ui->lwGuiLanguageShortcuts->selectedItems())
1454 delete item;
1455
1456 enableLanguageShortcutControls();
1457 }
1458
1459 }
1460