1 /**
2  * \file kdemainwindow.cpp
3  * KDE Kid3 main window.
4  *
5  * \b Project: Kid3
6  * \author Urs Fleisch
7  * \date 9 Jan 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 "kdemainwindow.h"
28 #include <kconfigwidgets_version.h>
29 #include <KConfig>
30 #include <KToggleAction>
31 #include <KStandardAction>
32 #include <KShortcutsDialog>
33 #include <KRecentFilesAction>
34 #include <KActionCollection>
35 #include <KEditToolBar>
36 #include <KConfigSkeleton>
37 #include <QApplication>
38 #include <QUrl>
39 #include <QAction>
40 #include "config.h"
41 #include "kid3form.h"
42 #include "filelist.h"
43 #include "sectionactions.h"
44 #include "kid3application.h"
45 #include "kdeconfigdialog.h"
46 #include "guiconfig.h"
47 #include "tagconfig.h"
48 #include "useractionsconfig.h"
49 #include "serverimporter.h"
50 #include "servertrackimporter.h"
51 
52 /**
53  * Constructor.
54  *
55  * @param platformTools platform specific tools
56  * @param app application context
57  * @param parent parent widget
58  */
KdeMainWindow(IPlatformTools * platformTools,Kid3Application * app,QWidget * parent)59 KdeMainWindow::KdeMainWindow(IPlatformTools* platformTools,
60                              Kid3Application* app, QWidget* parent)
61   : KXmlGuiWindow(parent),
62     BaseMainWindow(this, platformTools, app),
63     m_platformTools(platformTools), m_fileOpenRecent(nullptr),
64     m_settingsShowStatusbar(nullptr),
65     m_settingsAutoHideTags(nullptr), m_settingsShowHidePicture(nullptr)
66 {
67   init();
68 }
69 
70 /** Only defined for generation of translation files */
71 #define MAIN_TOOLBAR_FOR_PO QT_TRANSLATE_NOOP("@default", "Main Toolbar")
72 
73 /**
74  * Init menu and toolbar actions.
75  */
initActions()76 void KdeMainWindow::initActions()
77 {
78   KActionCollection* collection = actionCollection();
79 #if KCONFIGWIDGETS_VERSION >= 0x051700
80   QAction* action = KStandardAction::open(
81       impl(), &BaseMainWindowImpl::slotFileOpen, collection);
82 #else
83   QAction* action = KStandardAction::open(
84       impl(), SLOT(slotFileOpen()), collection);
85 #endif
86   action->setStatusTip(tr("Open files"));
87 #if KCONFIGWIDGETS_VERSION >= 0x051700
88   m_fileOpenRecent = KStandardAction::openRecent(
89       this, &KdeMainWindow::slotFileOpenRecentUrl, collection);
90 #else
91   m_fileOpenRecent = KStandardAction::openRecent(
92       this, SLOT(slotFileOpenRecentUrl(QUrl)), collection);
93 #endif
94   m_fileOpenRecent->setStatusTip(tr("Opens a recently used folder"));
95 #if KCONFIGWIDGETS_VERSION >= 0x051700
96   action = KStandardAction::revert(
97       app(), &Kid3Application::revertFileModifications, collection);
98 #else
99   action = KStandardAction::revert(
100       app(), SLOT(revertFileModifications()), collection);
101 #endif
102   action->setStatusTip(
103       tr("Reverts the changes of all or the selected files"));
104   collection->setDefaultShortcuts(action,
105                           KStandardShortcut::shortcut(KStandardShortcut::Undo));
106 #if KCONFIGWIDGETS_VERSION >= 0x051700
107   action = KStandardAction::save(
108       impl(), &BaseMainWindowImpl::slotFileSave, collection);
109 #else
110   action = KStandardAction::save(
111       impl(), SLOT(slotFileSave()), collection);
112 #endif
113   action->setStatusTip(tr("Saves the changed files"));
114 #if KCONFIGWIDGETS_VERSION >= 0x051700
115   action = KStandardAction::quit(
116       impl(), &BaseMainWindowImpl::slotFileQuit, collection);
117 #else
118   action = KStandardAction::quit(
119       impl(), SLOT(slotFileQuit()), collection);
120 #endif
121   action->setStatusTip(tr("Quits the application"));
122 #if KCONFIGWIDGETS_VERSION >= 0x051700
123   action = KStandardAction::selectAll(
124       form(), &Kid3Form::selectAllFiles, collection);
125 #else
126   action = KStandardAction::selectAll(
127       form(), SLOT(selectAllFiles()), collection);
128 #endif
129   action->setStatusTip(tr("Select all files"));
130   action->setShortcut(QKeySequence(QLatin1String("Alt+Shift+A")));
131 #if KCONFIGWIDGETS_VERSION >= 0x051700
132   action = KStandardAction::deselect(
133       form(), &Kid3Form::deselectAllFiles, collection);
134 #else
135   action = KStandardAction::deselect(
136       form(), SLOT(deselectAllFiles()), collection);
137 #endif
138   action->setStatusTip(tr("Deselect all files"));
139 #if KCONFIGWIDGETS_VERSION >= 0x051700
140   action = KStandardAction::find(
141       impl(), &BaseMainWindowImpl::find, collection);
142 #else
143   action = KStandardAction::find(
144       impl(), SLOT(find()), collection);
145 #endif
146   action->setStatusTip(tr("Find"));
147 #if KCONFIGWIDGETS_VERSION >= 0x051700
148   action = KStandardAction::replace(
149       impl(), &BaseMainWindowImpl::findReplace, collection);
150 #else
151   action = KStandardAction::replace(
152       impl(), SLOT(findReplace()), collection);
153 #endif
154   action->setStatusTip(tr("Find and replace"));
155   setStandardToolBarMenuEnabled(true);
156   createStandardStatusBarAction();
157 #if KCONFIGWIDGETS_VERSION >= 0x051700
158   action = KStandardAction::keyBindings(
159     this, &KdeMainWindow::slotSettingsShortcuts, collection);
160 #else
161   action = KStandardAction::keyBindings(
162     this, SLOT(slotSettingsShortcuts()), collection);
163 #endif
164   action->setStatusTip(tr("Configure Shortcuts"));
165 #if KCONFIGWIDGETS_VERSION >= 0x051700
166   action = KStandardAction::configureToolbars(
167     this, &KdeMainWindow::slotSettingsToolbars, collection);
168 #else
169   action = KStandardAction::configureToolbars(
170     this, SLOT(slotSettingsToolbars()), collection);
171 #endif
172   action->setStatusTip(tr("Configure Toolbars"));
173 #if KCONFIGWIDGETS_VERSION >= 0x051700
174   m_settingsShowStatusbar = KStandardAction::showStatusbar(
175     this, &KdeMainWindow::slotSettingsShowStatusbar, collection);
176 #else
177   m_settingsShowStatusbar = KStandardAction::showStatusbar(
178     this, SLOT(slotSettingsShowStatusbar()), collection);
179 #endif
180   m_settingsShowStatusbar->setStatusTip(tr("Enables/disables the statusbar"));
181 #if KCONFIGWIDGETS_VERSION >= 0x051700
182   action = KStandardAction::preferences(
183       this, &KdeMainWindow::slotSettingsConfigure, collection);
184 #else
185   action = KStandardAction::preferences(
186       this, SLOT(slotSettingsConfigure()), collection);
187 #endif
188   action->setStatusTip(tr("Preferences dialog"));
189 
190   action = new QAction(QIcon::fromTheme(QLatin1String("document-open")),
191                        tr("O&pen Folder..."), this);
192   action->setStatusTip(tr("Opens a folder"));
193   collection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_D));
194   collection->addAction(QLatin1String("open_directory"), action);
195   connect(action, &QAction::triggered,
196           impl(), &BaseMainWindowImpl::slotFileOpenDirectory);
197 
198   action = new QAction(QIcon::fromTheme(QLatin1String("view-refresh")),
199                        tr("Re&load"), this);
200   action->setStatusTip(tr("Reload folder"));
201   // When using the KDE version on GNOME, a dialog appears "There are two
202   // actions (Replace..., Reload) that want to use the same shortcut (Ctrl+R)".
203   // Avoid this by assigning Qt::Key_F5 instead of QKeySequence::Refresh.
204   // The section "Standard Shortcuts" in the QKeySequence documentation lists
205   // F5 as a key for "Refresh" on all platforms.
206   collection->setDefaultShortcut(action, QKeySequence(Qt::Key_F5));
207   collection->addAction(QLatin1String("reload"), action);
208   connect(action, &QAction::triggered,
209           impl(), &BaseMainWindowImpl::slotFileReload);
210 
211   action = new QAction(tr("Unload"), this);
212   collection->addAction(QLatin1String("unload"), action);
213   connect(action, &QAction::triggered, app(), &Kid3Application::unloadAllTags);
214 
215   action = new QAction(QIcon::fromTheme(QLatin1String("document-import")),
216                        tr("&Import..."), this);
217   action->setStatusTip(tr("Import from file or clipboard"));
218   action->setData(-1);
219   collection->addAction(QLatin1String("import"), action);
220   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotImport);
221 
222   int importerIdx = 0;
223   const auto sis = app()->getServerImporters();
224   for (const ServerImporter* si : sis) {
225     QString serverName(QCoreApplication::translate("@default", si->name()));
226     QString actionName = QString::fromLatin1(si->name()).toLower()
227         .remove(QLatin1Char(' '));
228     int dotPos = actionName.indexOf(QLatin1Char('.'));
229     if (dotPos != -1)
230       actionName.truncate(dotPos);
231     actionName = QLatin1String("import_") + actionName;
232     action = new QAction(tr("Import from %1...").arg(serverName), this);
233     action->setData(importerIdx);
234     action->setStatusTip(tr("Import from %1").arg(serverName));
235     collection->addAction(actionName, action);
236     connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotImport);
237     ++importerIdx;
238   }
239 
240   const auto stis = app()->getServerTrackImporters();
241   for (const ServerTrackImporter* si : stis) {
242     QString serverName(QCoreApplication::translate("@default", si->name()));
243     QString actionName = QString::fromLatin1(si->name()).toLower()
244         .remove(QLatin1Char(' '));
245     int dotPos = actionName.indexOf(QLatin1Char('.'));
246     if (dotPos != -1)
247       actionName.truncate(dotPos);
248     actionName = QLatin1String("import_") + actionName;
249     action = new QAction(tr("Import from %1...").arg(serverName), this);
250     action->setStatusTip(tr("Import from %1").arg(serverName));
251     action->setData(importerIdx);
252     collection->addAction(actionName, action);
253     connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotImport);
254     ++importerIdx;
255   }
256 
257   action = new QAction(tr("Import from Tags..."), this);
258   action->setStatusTip(tr("Import from Tags"));
259   collection->addAction(QLatin1String("import_tags"), action);
260   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotTagImport);
261 
262   action = new QAction(tr("Automatic I&mport..."), this);
263   action->setStatusTip(tr("Automatic import"));
264   collection->addAction(QLatin1String("batch_import"), action);
265   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotBatchImport);
266 
267   action = new QAction(tr("&Browse Cover Art..."), this);
268   action->setStatusTip(tr("Browse album cover artwork"));
269   collection->addAction(QLatin1String("browse_cover_art"), action);
270   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotBrowseCoverArt);
271   action = new QAction(QIcon::fromTheme(QLatin1String("document-export")),
272                        tr("&Export..."), this);
273   action->setStatusTip(tr("Export to file or clipboard"));
274   collection->addAction(QLatin1String("export"), action);
275   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotExport);
276   action = new QAction(QIcon::fromTheme(QLatin1String("view-media-playlist")),
277                        tr("&Create Playlist..."), this);
278   action->setStatusTip(tr("Create M3U Playlist"));
279   collection->addAction(QLatin1String("create_playlist"), action);
280   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotPlaylistDialog);
281   action = new QAction(tr("Apply &Filename Format"), this);
282   action->setStatusTip(tr("Apply Filename Format"));
283   collection->addAction(QLatin1String("apply_filename_format"), action);
284   connect(action, &QAction::triggered, app(), &Kid3Application::applyFilenameFormat);
285   action = new QAction(tr("Apply &Tag Format"), this);
286   action->setStatusTip(tr("Apply Tag Format"));
287   collection->addAction(QLatin1String("apply_id3_format"), action);
288   connect(action, &QAction::triggered, app(), &Kid3Application::applyTagFormat);
289   action = new QAction(tr("Apply Text &Encoding"), this);
290   action->setStatusTip(tr("Apply Text Encoding"));
291   collection->addAction(QLatin1String("apply_text_encoding"), action);
292   connect(action, &QAction::triggered, app(), &Kid3Application::applyTextEncoding);
293   action = new QAction(tr("&Rename Folder..."), this);
294   action->setStatusTip(tr("Rename Folder"));
295   collection->addAction(QLatin1String("rename_directory"), action);
296   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotRenameDirectory);
297   action = new QAction(tr("&Number Tracks..."), this);
298   action->setStatusTip(tr("Number Tracks"));
299   collection->addAction(QLatin1String("number_tracks"), action);
300   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotNumberTracks);
301   action = new QAction(tr("F&ilter..."), this);
302   action->setStatusTip(tr("Filter"));
303   collection->addAction(QLatin1String("filter"), action);
304   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::slotFilter);
305   const TagConfig& tagCfg = TagConfig::instance();
306   if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v24) {
307     action = new QAction(tr("Convert ID3v2.3 to ID3v2.&4"), this);
308     action->setStatusTip(tr("Convert ID3v2.3 to ID3v2.4"));
309     collection->addAction(QLatin1String("convert_to_id3v24"), action);
310     connect(action, &QAction::triggered, app(), &Kid3Application::convertToId3v24);
311     if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v23) {
312       action = new QAction(tr("Convert ID3v2.4 to ID3v2.&3"), this);
313       action->setStatusTip(tr("Convert ID3v2.4 to ID3v2.3"));
314       collection->addAction(QLatin1String("convert_to_id3v23"), action);
315       connect(action, &QAction::triggered, app(), &Kid3Application::convertToId3v23);
316     }
317   }
318 #ifdef HAVE_QTMULTIMEDIA
319   action = new QAction(QIcon::fromTheme(QLatin1String("media-playback-start")),
320                        tr("&Play"), this);
321   action->setStatusTip(tr("Play"));
322   collection->addAction(QLatin1String("play"), action);
323   connect(action, &QAction::triggered, app(), &Kid3Application::playAudio);
324 #endif
325   m_settingsShowHidePicture = new KToggleAction(tr("Show &Picture"), this);
326   m_settingsShowHidePicture->setStatusTip(tr("Show Picture"));
327   m_settingsShowHidePicture->setCheckable(true);
328   collection->addAction(QLatin1String("hide_picture"), m_settingsShowHidePicture);
329   connect(m_settingsShowHidePicture, &QAction::triggered,
330           impl(), &BaseMainWindowImpl::slotSettingsShowHidePicture);
331   m_settingsAutoHideTags = new KToggleAction(tr("Auto &Hide Tags"), this);
332   m_settingsAutoHideTags->setStatusTip(tr("Auto Hide Tags"));
333   m_settingsAutoHideTags->setCheckable(true);
334   collection->addAction(QLatin1String("auto_hide_tags"), m_settingsAutoHideTags);
335   connect(m_settingsAutoHideTags, &QAction::triggered,
336           impl(), &BaseMainWindowImpl::slotSettingsAutoHideTags);
337   action = new QAction(tr("Select All in &Folder"), this);
338   action->setStatusTip(tr("Select all files in the current folder"));
339   collection->addAction(QLatin1String("select_all_in_directory"), action);
340   connect(action, &QAction::triggered, app(), &Kid3Application::selectAllInDirectory);
341   action = new QAction(tr("&Invert Selection"), this);
342   collection->addAction(QLatin1String("invert_selection"), action);
343   connect(action, &QAction::triggered, app(), &Kid3Application::invertSelection);
344   action = new QAction(QIcon::fromTheme(QLatin1String("go-previous")),
345                        tr("&Previous File"), this);
346   action->setStatusTip(tr("Select previous file"));
347   collection->setDefaultShortcuts(action,
348                          KStandardShortcut::shortcut(KStandardShortcut::Prior));
349   collection->addAction(QLatin1String("previous_file"), action);
350   connect(action, &QAction::triggered, form(), &Kid3Form::selectPreviousTaggedFile);
351   action = new QAction(QIcon::fromTheme(QLatin1String("go-next")),
352                        tr("&Next File"), this);
353   action->setStatusTip(tr("Select next file"));
354   collection->setDefaultShortcuts(action,
355                          KStandardShortcut::shortcut(KStandardShortcut::Next));
356   collection->addAction(QLatin1String("next_file"), action);
357   connect(action, &QAction::triggered, form(), &Kid3Form::selectNextTaggedFile);
358   FOR_ALL_TAGS(tagNr) {
359     Frame::TagNumber otherTagNr = tagNr == Frame::Tag_1
360         ? Frame::Tag_2
361         : tagNr == Frame::Tag_2 ? Frame::Tag_1 : Frame::Tag_NumValues;
362     QString tagStr = Frame::tagNumberToString(tagNr);
363     Kid3ApplicationTagContext* appTag = app()->tag(tagNr);
364     Kid3FormTagContext* formTag = form()->tag(tagNr);
365     QString actionPrefix = tr("Tag %1").arg(tagStr) +
366         QLatin1String(": ");
367     action = new QAction(tr("Filename") + QLatin1String(": ") +
368                          tr("From Tag %1").arg(tagStr), this);
369     collection->addAction(QLatin1String("filename_from_v") + tagStr, action);
370     connect(action, &QAction::triggered,
371             appTag, &Kid3ApplicationTagContext::getFilenameFromTags);
372     tagStr = QLatin1Char('v') + tagStr + QLatin1Char('_');
373     action = new QAction(actionPrefix + tr("From Filename"), this);
374     collection->addAction(tagStr + QLatin1String("from_filename"), action);
375     connect(action, &QAction::triggered,
376             appTag, &Kid3ApplicationTagContext::getTagsFromFilename);
377     if (otherTagNr < Frame::Tag_NumValues) {
378       QString otherTagStr = Frame::tagNumberToString(otherTagNr);
379       action = new QAction(actionPrefix + tr("From Tag %1").arg(otherTagStr),
380                            this);
381       collection->addAction(tagStr + QLatin1String("from_v") + otherTagStr,
382                             action);
383       connect(action, &QAction::triggered,
384               appTag, &Kid3ApplicationTagContext::copyToOtherTag);
385     }
386     action = new QAction(actionPrefix + tr("Copy"), this);
387     collection->addAction(tagStr + QLatin1String("copy"), action);
388     connect(action, &QAction::triggered,
389             appTag, &Kid3ApplicationTagContext::copyTags);
390     action = new QAction(actionPrefix + tr("Paste"), this);
391     collection->addAction(tagStr + QLatin1String("paste"), action);
392     connect(action, &QAction::triggered,
393             appTag, &Kid3ApplicationTagContext::pasteTags);
394     action = new QAction(actionPrefix + tr("Remove"), this);
395     collection->addAction(tagStr + QLatin1String("remove"), action);
396     connect(action, &QAction::triggered,
397             appTag, &Kid3ApplicationTagContext::removeTags);
398     action = new QAction(actionPrefix + tr("Focus"), this);
399     collection->addAction(tagStr + QLatin1String("focus"), action);
400     connect(action, &QAction::triggered,
401             formTag, &Kid3FormTagContext::setFocusTag);
402     if (tagNr != Frame::Tag_Id3v1) {
403       actionPrefix += tr("Frames:") + QLatin1Char(' ');
404       action = new QAction(actionPrefix + tr("Edit"), this);
405       collection->addAction(tagStr + QLatin1String("frames_edit"), action);
406       connect(action, &QAction::triggered,
407               appTag, &Kid3ApplicationTagContext::editFrame);
408       action = new QAction(actionPrefix + tr("Add"), this);
409       collection->addAction(tagStr + QLatin1String("frames_add"), action);
410       connect(action, &QAction::triggered,
411               appTag, &Kid3ApplicationTagContext::addFrame);
412       action = new QAction(actionPrefix + tr("Delete"), this);
413       collection->addAction(tagStr + QLatin1String("frames_delete"), action);
414       connect(action, &QAction::triggered,
415               appTag, &Kid3ApplicationTagContext::deleteFrame);
416     }
417   }
418 
419   action = new QAction(tr("Filename") + QLatin1String(": ") + tr("Focus"),
420                        this);
421   collection->addAction(QLatin1String("filename_focus"), action);
422   connect(action, &QAction::triggered, form(), &Kid3Form::setFocusFilename);
423 
424   action = new QAction(tr("File List") + QLatin1String(": ") + tr("Focus"),
425                        this);
426   collection->addAction(QLatin1String("filelist_focus"), action);
427   connect(action, &QAction::triggered, form(), &Kid3Form::setFocusFileList);
428   action = new QAction(tr("&Rename"), this);
429   collection->setDefaultShortcut(action, QKeySequence(Qt::Key_F2));
430   action->setShortcutContext(Qt::WidgetShortcut);
431   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::renameFile);
432   // This action is not made configurable because its shortcut F2 conflicts
433   // with a section shortcut and there seems to be no way to avoid it with
434   // KShortcutsDialog. The same applies to the shortcut with the Delete key.
435   // collection->addAction(QLatin1String("filelist_rename"), action);
436   form()->getFileList()->setRenameAction(action);
437   action = new QAction(tr("&Move to Trash"), this);
438   collection->setDefaultShortcut(action, QKeySequence::Delete);
439   action->setShortcutContext(Qt::WidgetShortcut);
440   connect(action, &QAction::triggered, impl(), &BaseMainWindowImpl::deleteFile);
441   // collection->addAction(QLatin1String("filelist_delete"), action);
442   form()->getFileList()->setDeleteAction(action);
443   action = new QAction(tr("Folder List") + QLatin1String(": ") + tr("Focus"),
444                        this);
445   collection->addAction(QLatin1String("dirlist_focus"), action);
446   connect(action, &QAction::triggered, form(), &Kid3Form::setFocusDirList);
447 
448   FileList* fileList = form()->getFileList();
449   // Do not support user action keyboard shortcuts with KDE 4, it would only
450   // print "Attempt to use QAction (..) with KXMLGUIFactory!" warnings.
451   connect(fileList, &FileList::userActionAdded,
452           this, &KdeMainWindow::onUserActionAdded);
453   connect(fileList, &FileList::userActionRemoved,
454           this, &KdeMainWindow::onUserActionRemoved);
455   fileList->initUserActions();
456   const UserActionsConfig& userActionsCfg = UserActionsConfig::instance();
457   connect(&userActionsCfg, &UserActionsConfig::contextMenuCommandsChanged,
458           fileList, &FileList::initUserActions);
459 
460   const auto sectionShortcuts = SectionActions::defaultShortcuts();
461   QString actionPrefix = tr("Section") + QLatin1String(": ");
462   for (auto it = sectionShortcuts.constBegin();
463        it != sectionShortcuts.constEnd();
464        ++it) {
465     const auto& tpl = *it;
466     action = new QAction(actionPrefix + std::get<1>(tpl), this);
467     action->setShortcutContext(Qt::WidgetShortcut);
468     // The action is only used to configure the shortcuts. Disabling it will
469     // also avoid "that want to use the same shortcut" error dialogs.
470     action->setEnabled(false);
471     collection->setDefaultShortcut(action, std::get<2>(tpl));
472     collection->addAction(std::get<0>(tpl), action);
473   }
474 
475   createGUI();
476 }
477 
478 /**
479  * Get keyboard shortcuts.
480  * @return mapping of action names to key sequences.
481  */
shortcutsMap() const482 QMap<QString, QKeySequence> KdeMainWindow::shortcutsMap() const
483 {
484   QMap<QString, QKeySequence> map;
485   if (KActionCollection* collection = actionCollection()) {
486     const auto actions = collection->actions();
487     for (QAction* action : actions) {
488       if (action) {
489         QString name = action->objectName();
490         if (!name.isEmpty()) {
491           map.insert(name, action->shortcut());
492         }
493       }
494     }
495   }
496   return map;
497 }
498 
499 /**
500  * Add directory to recent files list.
501  *
502  * @param dirName path to directory
503  */
addDirectoryToRecentFiles(const QString & dirName)504 void KdeMainWindow::addDirectoryToRecentFiles(const QString& dirName)
505 {
506   QUrl url;
507   url.setPath(dirName);
508   m_fileOpenRecent->addUrl(url);
509 }
510 
511 /**
512  * Read settings from the configuration.
513  */
readConfig()514 void KdeMainWindow::readConfig()
515 {
516   setAutoSaveSettings();
517   m_settingsShowHidePicture->setChecked(!GuiConfig::instance().hidePicture());
518   m_settingsAutoHideTags->setChecked(GuiConfig::instance().autoHideTags());
519   auto cfg = KSharedConfig::openConfig();
520   m_fileOpenRecent->loadEntries(cfg->group("Recent Files"));
521 
522   QString entry = cfg->group("MainWindow").readEntry("StatusBar", "Enabled");
523   bool statusBarVisible = entry != QLatin1String("Disabled");
524   if (m_settingsShowStatusbar) {
525     m_settingsShowStatusbar->setChecked(statusBarVisible);
526   }
527   setStatusBarVisible(statusBarVisible);
528 }
529 
530 /**
531  * Store geometry and recent files in settings.
532  */
saveConfig()533 void KdeMainWindow::saveConfig()
534 {
535   m_fileOpenRecent->saveEntries(KSharedConfig::openConfig()->group("Recent Files"));
536 }
537 
538 /**
539  * Set main window caption.
540  *
541  * @param caption caption without application name
542  * @param modified true if any file is modified
543  */
setWindowCaption(const QString & caption,bool modified)544 void KdeMainWindow::setWindowCaption(const QString& caption, bool modified)
545 {
546   setCaption(caption, modified);
547 }
548 
549 /**
550  * Get action for Settings/Auto Hide Tags.
551  * @return action.
552  */
autoHideTagsAction()553 QAction* KdeMainWindow::autoHideTagsAction()
554 {
555   return m_settingsAutoHideTags;
556 }
557 
558 /**
559  * Get action for Settings/Hide Picture.
560  * @return action.
561  */
showHidePictureAction()562 QAction* KdeMainWindow::showHidePictureAction()
563 {
564  return m_settingsShowHidePicture;
565 }
566 
567 /**
568  * Update modification state before closing.
569  * Called on closeEvent() of window.
570  * If anything was modified, save after asking user.
571  * Save options before closing.
572  * This method is called by closeEvent(), which occurs when the
573  * window is closed or slotFileQuit() (Quit menu) is selected.
574  *
575  * @return false if user canceled,
576  *         true will quit the application.
577  */
queryClose()578 bool KdeMainWindow::queryClose()
579 {
580   return queryBeforeClosing();
581 }
582 
583 /**
584  * Saves the window properties to the session config file.
585  *
586  * @param cfg application configuration
587  */
saveProperties(KConfigGroup & cfg)588 void KdeMainWindow::saveProperties(KConfigGroup& cfg)
589 {
590   cfg.writeEntry("dirname", app()->getDirName());
591 }
592 
593 /**
594  * Reads the session config file and restores the application's state.
595  *
596  * @param cfg application configuration
597  */
readProperties(const KConfigGroup & cfg)598 void KdeMainWindow::readProperties(const KConfigGroup& cfg)
599 {
600   app()->openDirectory({cfg.readEntry("dirname", "")});
601 }
602 
603 /**
604  * Open recent directory.
605  *
606  * @param dir directory to open
607  */
slotFileOpenRecentUrl(const QUrl & url)608 void KdeMainWindow::slotFileOpenRecentUrl(const QUrl& url)
609 {
610   openRecentDirectory(url.path());
611 }
612 
613 /**
614  * Shortcuts configuration.
615  */
slotSettingsShortcuts()616 void KdeMainWindow::slotSettingsShortcuts()
617 {
618   if (KShortcutsDialog::configure(
619         actionCollection(),
620         KShortcutsEditor::LetterShortcutsAllowed, this) ==
621       QDialog::Accepted) {
622     impl()->applyChangedShortcuts();
623   }
624 }
625 
626 /**
627  * Toolbars configuration.
628  */
slotSettingsToolbars()629 void KdeMainWindow::slotSettingsToolbars()
630 {
631   KEditToolBar dlg(actionCollection());
632   if (dlg.exec()) {
633     createGUI();
634   }
635 }
636 
637 /**
638  * Statusbar configuration.
639  */
slotSettingsShowStatusbar()640 void KdeMainWindow::slotSettingsShowStatusbar()
641 {
642   setStatusBarVisible(m_settingsShowStatusbar->isChecked());
643   setSettingsDirty();
644 }
645 
646 /**
647  * Preferences.
648  */
slotSettingsConfigure()649 void KdeMainWindow::slotSettingsConfigure()
650 {
651   QString caption(tr("Configure - Kid3"));
652   KConfigSkeleton* configSkeleton = new KConfigSkeleton;
653   auto dialog = new KdeConfigDialog(m_platformTools, this, caption,
654                                                 configSkeleton);
655   dialog->setConfig();
656   if (dialog->exec() == QDialog::Accepted) {
657     dialog->getConfig();
658     impl()->applyChangedConfiguration();
659   }
660   delete configSkeleton;
661 }
662 
663 /**
664  * Add user action to collection.
665  * @param name name of action
666  * @param action action to add
667  */
onUserActionAdded(const QString & name,QAction * action)668 void KdeMainWindow::onUserActionAdded(const QString& name, QAction* action)
669 {
670   KActionCollection* collection = actionCollection();
671   collection->addAction(name, action);
672 }
673 
674 /**
675  * Remove user action from collection.
676  * @param name name of action
677  * @param action action to remove
678  */
onUserActionRemoved(const QString & name,QAction * action)679 void KdeMainWindow::onUserActionRemoved(const QString& name, QAction* action)
680 {
681   Q_UNUSED(name)
682   KActionCollection* collection = actionCollection();
683   collection->takeAction(action);
684 }
685