1 /**
2 * \file kid3form.cpp
3 * GUI for kid3.
4 *
5 * \b Project: Kid3
6 * \author Urs Fleisch
7 * \date 8 Apr 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 "kid3form.h"
28 #include <QCheckBox>
29 #include <QPushButton>
30 #include <QToolButton>
31 #include <QSpinBox>
32 #include <QLayout>
33 #include <QToolTip>
34 #include <QStackedWidget>
35 #include <QSplitter>
36 #include <QDir>
37 #include <QFrame>
38 #include <QPixmap>
39 #include <QComboBox>
40 #include <QVBoxLayout>
41 #include <QDropEvent>
42 #include <QDragEnterEvent>
43 #include <QLabel>
44 #include <QGridLayout>
45 #include <QScrollArea>
46 #include <QUrl>
47 #include <QApplication>
48 #include <QMimeData>
49 #include <QMenu>
50 #include <QFileIconProvider>
51 #include <QStyle>
52 #include <QHeaderView>
53 #include <QBuffer>
54 #include "filesystemmodel.h"
55 #include "frametable.h"
56 #include "frametablemodel.h"
57 #include "trackdata.h"
58 #include "genres.h"
59 #include "basemainwindow.h"
60 #include "filelist.h"
61 #include "framelist.h"
62 #include "pictureframe.h"
63 #include "configurabletreeview.h"
64 #include "picturelabel.h"
65 #include "fileconfig.h"
66 #include "guiconfig.h"
67 #include "formatconfig.h"
68 #include "dirproxymodel.h"
69 #include "fileproxymodel.h"
70 #include "coretaggedfileiconprovider.h"
71 #include "icoreplatformtools.h"
72 #include "kid3application.h"
73 #include "sectionactions.h"
74 #ifdef Q_OS_MAC
75 #include <CoreFoundation/CFURL.h>
76 #endif
77
78 /** Collapse pixmap, will be allocated in constructor */
79 QPixmap* Kid3Form::s_collapsePixmap = nullptr;
80 /** Expand pixmap, will be allocated in constructor */
81 QPixmap* Kid3Form::s_expandPixmap = nullptr;
82
83 namespace {
84
85 /** picture for collapse pixmap */
86 const char* const collapse_xpm[] = {
87 "7 7 3 1",
88 " \tc None",
89 ".\tc #FFFFFF",
90 "+\tc #000000",
91 ".......",
92 ".......",
93 ".......",
94 ".+++++.",
95 ".......",
96 ".......",
97 "......."
98 };
99
100 /** picture for expand pixmap */
101 const char* const expand_xpm[] = {
102 "7 7 3 1",
103 " \tc None",
104 ".\tc #FFFFFF",
105 "+\tc #000000",
106 ".......",
107 "...+...",
108 "...+...",
109 ".+++++.",
110 "...+...",
111 "...+...",
112 "......."
113 };
114
115
116 /**
117 * Event filter for double click on picture label.
118 */
119 class PictureDblClickHandler : public QObject {
120 public:
121 /**
122 * Constructor.
123 * @param app application
124 */
PictureDblClickHandler(Kid3Application * app)125 explicit PictureDblClickHandler(Kid3Application* app)
126 : QObject(app), m_app(app) {}
127 virtual ~PictureDblClickHandler() override = default;
128
129 protected:
130 /**
131 * Event filter function, calls Kid3Application::editOrAddPicture().
132 *
133 * @param obj watched object
134 * @param event event for object
135 *
136 * @return true if event is filtered.
137 */
138 virtual bool eventFilter(QObject* obj, QEvent* event) override;
139
140 private:
141 Q_DISABLE_COPY(PictureDblClickHandler)
142
143 Kid3Application* m_app;
144 };
145
146 /**
147 * Event filter function, calls Kid3Application::editOrAddPicture() on double click.
148 *
149 * @param obj watched object
150 * @param event event for object
151 *
152 * @return true if event is filtered.
153 */
eventFilter(QObject * obj,QEvent * event)154 bool PictureDblClickHandler::eventFilter(QObject* obj, QEvent* event)
155 {
156 if (event->type() == QEvent::MouseButtonDblClick) {
157 m_app->editOrAddPicture();
158 return true;
159 } else {
160 // standard event processing
161 return QObject::eventFilter(obj, event);
162 }
163 }
164
165
166 /**
167 * File decoration provider for FileSystemModel delegating to QFileIconProvider.
168 */
169 class WidgetFileDecorationProvider : public AbstractFileDecorationProvider {
170 public:
headerDecoration() const171 virtual QVariant headerDecoration() const override {
172 // From QFileSystemModel:
173 // ### TODO oh man this is ugly and doesn't even work all the way!
174 // it is still 2 pixels off
175 QImage pixmap(16, 1, QImage::Format_Mono);
176 pixmap.fill(0);
177 pixmap.setAlphaChannel(pixmap.createAlphaMask());
178 return pixmap;
179 }
180
computerDecoration() const181 virtual QVariant computerDecoration() const override {
182 return m_provider.icon(QFileIconProvider::Computer);
183 }
184
folderDecoration() const185 virtual QVariant folderDecoration() const override {
186 return m_provider.icon(QFileIconProvider::Folder);
187 }
188
fileDecoration() const189 virtual QVariant fileDecoration() const override {
190 return m_provider.icon(QFileIconProvider::File);
191 }
192
decoration(const QFileInfo & info) const193 virtual QVariant decoration(const QFileInfo& info) const override {
194 return m_provider.icon(info);
195 }
196
type(const QFileInfo & info) const197 virtual QString type(const QFileInfo& info) const override {
198 return m_provider.type(info);
199 }
200
201 private:
202 QFileIconProvider m_provider;
203 };
204
205
206 /**
207 * Get the items from a combo box.
208 *
209 * @param comboBox combo box
210 *
211 * @return item texts from combo box.
212 */
getItemsFromComboBox(const QComboBox * comboBox)213 QStringList getItemsFromComboBox(const QComboBox* comboBox)
214 {
215 QStringList lst;
216 const int numItems = comboBox->count();
217 lst.reserve(numItems);
218 for (int i = 0; i < numItems; ++i) {
219 lst += comboBox->itemText(i);
220 }
221 return lst;
222 }
223
224 /**
225 * Set items in combo box and add current item if not already existing.
226 * @param items combo box item
227 * @param currentItem current item
228 * @param comboBox combo box to set
229 */
setItemsInComboBox(const QStringList & items,const QString & currentItem,QComboBox * comboBox)230 void setItemsInComboBox(const QStringList& items, const QString& currentItem,
231 QComboBox* comboBox)
232 {
233 QStringList allItems = items;
234 int idx = allItems.indexOf(currentItem);
235 if (idx == -1) {
236 allItems.append(currentItem);
237 idx = allItems.size() - 1;
238 }
239 // Block signals on combo box while setting contents to avoid
240 // editTextChanged() signals causing configuration changes.
241 comboBox->blockSignals(true);
242 if (!allItems.isEmpty()) {
243 comboBox->clear();
244 comboBox->addItems(allItems);
245 }
246 comboBox->setCurrentIndex(idx);
247 comboBox->blockSignals(false);
248 }
249
250 }
251
252 /**
253 * Constructs an Id3Form as a child of 'parent', with the
254 * name 'name' and widget flags set to 'f'.
255 * @param app application
256 * @param parent parent widget
257 */
Kid3Form(Kid3Application * app,BaseMainWindowImpl * mainWin,QWidget * parent)258 Kid3Form::Kid3Form(Kid3Application* app, BaseMainWindowImpl* mainWin,
259 QWidget* parent)
260 : QSplitter(parent), m_pictureLabel(nullptr), m_app(app), m_mainWin(mainWin),
261 m_iconProvider(new WidgetFileDecorationProvider)
262 {
263 setObjectName(QLatin1String("Kid3Form"));
264
265 FOR_ALL_TAGS(tagNr) {
266 m_tagContext[tagNr] = new Kid3FormTagContext(this, tagNr);
267 if (tagNr != Frame::Tag_Id3v1) {
268 m_app->getFrameList(tagNr)->setFrameEditor(m_mainWin);
269 }
270 }
271
272 if (!s_collapsePixmap) {
273 s_collapsePixmap = new QPixmap(const_cast<const char**>(collapse_xpm));
274 }
275 if (!s_expandPixmap) {
276 s_expandPixmap = new QPixmap(const_cast<const char**>(expand_xpm));
277 }
278
279 setAcceptDrops(true);
280 setWindowTitle(tr("Kid3"));
281
282 m_leftSideWidget = new QStackedWidget(this);
283 m_vSplitter = new QSplitter(Qt::Vertical);
284 m_leftSideWidget->addWidget(m_vSplitter);
285 m_fileListBox = new FileList(m_vSplitter, m_mainWin);
286 FileProxyModel* fileProxyModel = m_app->getFileProxyModel();
287 if (FileSystemModel* fsModel =
288 qobject_cast<FileSystemModel*>(fileProxyModel->sourceModel())) {
289 fsModel->setDecorationProvider(m_iconProvider.data());
290 }
291 CoreTaggedFileIconProvider* tagIconProvider = fileProxyModel->getIconProvider();
292 tagIconProvider->setModifiedIcon(
293 QApplication::style()->standardIcon(QStyle::SP_DriveFDIcon));
294 int iconHeight = (((fontMetrics().height() - 1) / 16) + 1) * 16;
295 tagIconProvider->setRequestedSize(QSize(iconHeight, iconHeight));
296 m_fileListBox->setModel(fileProxyModel);
297 m_fileListBox->setSelectionModel(m_app->getFileSelectionModel());
298 m_dirListBox = new ConfigurableTreeView(m_vSplitter);
299 m_dirListBox->setObjectName(QLatin1String("DirList"));
300 m_dirListBox->setItemsExpandable(false);
301 m_dirListBox->setRootIsDecorated(false);
302 m_dirListBox->setModel(m_app->getDirProxyModel());
303 m_dirListBox->setSelectionModel(m_app->getDirSelectionModel());
304 connect(m_fileListBox, &QAbstractItemView::activated,
305 this, &Kid3Form::fileActivated);
306 connect(m_dirListBox, &QAbstractItemView::activated, this,
307 &Kid3Form::dirSelected);
308 connect(m_fileListBox, &ConfigurableTreeView::parentActivated,
309 this, &Kid3Form::openParentDirectory);
310 connect(m_dirListBox, &ConfigurableTreeView::parentActivated,
311 this, &Kid3Form::openParentDirectory);
312
313 connect(m_app, &Kid3Application::fileRootIndexChanged,
314 this, &Kid3Form::setFileRootIndex);
315 connect(m_app, &Kid3Application::dirRootIndexChanged,
316 this, &Kid3Form::setDirRootIndex);
317 connect(m_app, &Kid3Application::directoryOpened,
318 this, &Kid3Form::onFirstDirectoryOpened);
319
320 m_rightHalfVBox = new QWidget;
321 auto scrollView = new QScrollArea(this);
322 scrollView->setWidget(m_rightHalfVBox);
323 scrollView->setWidgetResizable(true);
324 auto rightHalfLayout = new QVBoxLayout(m_rightHalfVBox);
325 rightHalfLayout->setSpacing(0);
326
327 m_fileButton = new QToolButton(m_rightHalfVBox);
328 m_fileButton->setIcon(*s_collapsePixmap);
329 m_fileButton->setAutoRaise(true);
330 #ifdef Q_OS_MAC
331 m_fileButton->setStyleSheet(QLatin1String("border: 0;"));
332 #endif
333 connect(m_fileButton, &QAbstractButton::clicked, this, &Kid3Form::showHideFile);
334 m_fileLabel = new QLabel(tr("F&ile"), m_rightHalfVBox);
335 auto fileButtonLayout = new QHBoxLayout;
336 fileButtonLayout->addWidget(m_fileButton);
337 fileButtonLayout->addWidget(m_fileLabel);
338 rightHalfLayout->addLayout(fileButtonLayout);
339
340 m_fileWidget = new QWidget(m_rightHalfVBox);
341 m_fileWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
342 rightHalfLayout->addWidget(m_fileWidget);
343 auto fileLayout = new QGridLayout(m_fileWidget);
344
345 m_nameLabel = new QLabel(tr("Name:"), m_fileWidget);
346 fileLayout->addWidget(m_nameLabel, 0, 0);
347
348 m_nameLineEdit = new QLineEdit(m_fileWidget);
349 connect(m_nameLineEdit, &QLineEdit::textEdited, this,
350 &Kid3Form::nameLineEditChanged);
351 fileLayout->addWidget(m_nameLineEdit, 0, 1, 1, 4);
352 m_fileLabel->setBuddy(m_nameLineEdit);
353
354 QLabel* formatLabel = new QLabel(tr("Format:") + QChar(0x2191),
355 m_fileWidget);
356 fileLayout->addWidget(formatLabel, 1, 0);
357
358 m_formatComboBox = new QComboBox(m_fileWidget);
359 m_formatComboBox->setEditable(true);
360 m_formatComboBox->setSizeAdjustPolicy(
361 QComboBox::AdjustToMinimumContentsLengthWithIcon);
362 m_formatComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
363 m_formatComboBox->setToolTip(TrackDataFormatReplacer::getToolTip());
364 connect(m_formatComboBox, &QComboBox::editTextChanged,
365 this, &Kid3Form::onFormatEditTextChanged);
366 m_formatFromFilenameComboBox = new QComboBox(m_fileWidget);
367 m_formatFromFilenameComboBox->setEditable(true);
368 m_formatFromFilenameComboBox->setSizeAdjustPolicy(
369 QComboBox::AdjustToMinimumContentsLengthWithIcon);
370 m_formatFromFilenameComboBox->setSizePolicy(
371 QSizePolicy::Expanding, QSizePolicy::Fixed);
372 m_formatFromFilenameComboBox->setToolTip(FrameFormatReplacer::getToolTip());
373 connect(m_formatFromFilenameComboBox, &QComboBox::editTextChanged,
374 this, &Kid3Form::onFormatFromFilenameEditTextChanged);
375
376 fileLayout->addWidget(m_formatComboBox, 1, 1);
377
378 setTabOrder(m_fileListBox, m_dirListBox);
379 setTabOrder(m_dirListBox, m_nameLineEdit);
380 setTabOrder(m_nameLineEdit, m_formatComboBox);
381 setTabOrder(m_formatComboBox, m_formatFromFilenameComboBox);
382
383 QWidget* tabWidget = m_formatFromFilenameComboBox;
384
385 QLabel* fromTagLabel = new QLabel(tr("From:"), m_fileWidget);
386 fileLayout->addWidget(fromTagLabel, 1, 2);
387 int column = 3;
388 FOR_ALL_TAGS(tagNr) {
389 if (tagNr <= Frame::Tag_2) {
390 QString tagStr = Frame::tagNumberToString(tagNr);
391 m_fnButton[tagNr] = new QPushButton(tr("Tag %1").arg(tagStr),
392 m_fileWidget);
393 m_fnButton[tagNr]->setToolTip(tr("Filename from Tag %1").arg(tagStr));
394 connect(m_fnButton[tagNr], &QAbstractButton::clicked,
395 m_app->tag(tagNr), &Kid3ApplicationTagContext::getFilenameFromTags);
396 fileLayout->addWidget(m_fnButton[tagNr], 1, column++);
397 setTabOrder(tabWidget, m_fnButton[tagNr]);
398 tabWidget = m_fnButton[tagNr];
399 } else {
400 m_fnButton[tagNr] = nullptr;
401 }
402 }
403
404 QLabel* formatFromFilenameLabel = new QLabel(tr("Format:") + QChar(0x2193),
405 m_fileWidget);
406 fileLayout->addWidget(formatFromFilenameLabel, 2, 0);
407
408 fileLayout->addWidget(m_formatFromFilenameComboBox, 2, 1);
409
410 QLabel* toTagLabel = new QLabel(tr("To:"), m_fileWidget);
411 fileLayout->addWidget(toTagLabel, 2, 2);
412 column = 3;
413 FOR_ALL_TAGS(tagNr) {
414 if (tagNr <= Frame::Tag_2) {
415 QString tagStr = Frame::tagNumberToString(tagNr);
416 m_toTagButton[tagNr] =
417 new QPushButton(tr("Tag %1").arg(tagStr), m_fileWidget);
418 m_toTagButton[tagNr]->setToolTip(tr("Tag %1 from Filename").arg(tagStr));
419 connect(m_toTagButton[tagNr], &QAbstractButton::clicked,
420 m_app->tag(tagNr), &Kid3ApplicationTagContext::getTagsFromFilename);
421 fileLayout->addWidget(m_toTagButton[tagNr], 2, column++);
422 setTabOrder(tabWidget, m_toTagButton[tagNr]);
423 tabWidget = m_toTagButton[tagNr];
424 } else {
425 m_toTagButton[tagNr] = nullptr;
426 }
427 }
428
429 FOR_ALL_TAGS(tagNr) {
430 m_tagButton[tagNr] = new QToolButton(m_rightHalfVBox);
431 m_tagButton[tagNr]->setIcon(*s_collapsePixmap);
432 m_tagButton[tagNr]->setAutoRaise(true);
433 #ifdef Q_OS_MAC
434 m_tagButton[tagNr]->setStyleSheet(QLatin1String("border: 0;"));
435 #endif
436 connect(m_tagButton[tagNr], &QAbstractButton::clicked,
437 tag(tagNr), &Kid3FormTagContext::showHideTag);
438 m_tagLabel[tagNr] = new QLabel(
439 tr("Tag &%1").arg(Frame::tagNumberToString(tagNr)), m_rightHalfVBox);
440 auto tagButtonLayout = new QHBoxLayout;
441 tagButtonLayout->addWidget(m_tagButton[tagNr]);
442 tagButtonLayout->addWidget(m_tagLabel[tagNr]);
443 rightHalfLayout->addLayout(tagButtonLayout);
444
445 m_tagWidget[tagNr] = new QWidget(m_rightHalfVBox);
446 if (tagNr == Frame::Tag_Id3v1) {
447 m_tagWidget[tagNr]->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
448 }
449 rightHalfLayout->addWidget(m_tagWidget[tagNr], 100);
450
451 auto idHBoxLayout = new QHBoxLayout(m_tagWidget[tagNr]);
452 FrameTableModel* frameModel = m_app->frameModel(tagNr);
453 frameModel->setHeadersEmpty(true);
454 m_frameTable[tagNr] = new FrameTable(
455 frameModel, m_app->genreModel(tagNr), m_tagWidget[tagNr]);
456 m_frameTable[tagNr]->setSelectionModel(m_app->getFramesSelectionModel(tagNr));
457 idHBoxLayout->addWidget(m_frameTable[tagNr], tagNr == Frame::Tag_Id3v1 ? 100 : 0);
458 m_tagLabel[tagNr]->setBuddy(m_frameTable[tagNr]);
459
460 auto buttonsVBoxLayout = new QVBoxLayout;
461 idHBoxLayout->addLayout(buttonsVBoxLayout);
462
463 if (tagNr <= Frame::Tag_2) {
464 Frame::TagNumber otherTagNr = tagNr == Frame::Tag_1
465 ? Frame::Tag_2
466 : tagNr == Frame::Tag_2 ? Frame::Tag_1 : Frame::Tag_NumValues;
467 m_id3PushButton[tagNr] =
468 new QPushButton(tr("From Tag %1")
469 .arg(Frame::tagNumberToString(otherTagNr)),
470 m_tagWidget[tagNr]);
471 connect(m_id3PushButton[tagNr], &QAbstractButton::clicked,
472 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyToOtherTag);
473 buttonsVBoxLayout->addWidget(m_id3PushButton[tagNr]);
474 setTabOrder(tabWidget, m_id3PushButton[tagNr]);
475
476 QPushButton* copyPushButton = new QPushButton(tr("Copy"),
477 m_tagWidget[tagNr]);
478 connect(copyPushButton, &QAbstractButton::clicked,
479 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyTags);
480 buttonsVBoxLayout->addWidget(copyPushButton);
481 setTabOrder(m_id3PushButton[tagNr], copyPushButton);
482
483 QPushButton* pastePushButton =
484 new QPushButton(tr("Paste"), m_tagWidget[tagNr]);
485 connect(pastePushButton, &QAbstractButton::clicked,
486 m_app->tag(tagNr), &Kid3ApplicationTagContext::pasteTags);
487 buttonsVBoxLayout->addWidget(pastePushButton);
488 setTabOrder(copyPushButton, pastePushButton);
489 tabWidget = pastePushButton;
490 } else {
491 m_id3PushButton[tagNr] = new QPushButton(tr("From"));
492 auto menu = new QMenu(this);
493 QAction* action = menu->addAction(tr("Filename"));
494 connect(action, &QAction::triggered,
495 m_app->tag(tagNr), &Kid3ApplicationTagContext::getTagsFromFilename);
496 FOR_ALL_TAGS(fromTagNr) {
497 if (fromTagNr != tagNr) {
498 action = menu->addAction(
499 tr("Tag %1").arg(Frame::tagNumberToString(fromTagNr)));
500 QByteArray ba;
501 ba.append(static_cast<char>(fromTagNr));
502 ba.append(static_cast<char>(tagNr));
503 action->setData(ba);
504 connect(action, &QAction::triggered,
505 this, &Kid3Form::copyTagsActionData);
506 }
507 }
508 action = menu->addAction(tr("Paste"));
509 connect(action, &QAction::triggered,
510 m_app->tag(tagNr), &Kid3ApplicationTagContext::pasteTags);
511 m_id3PushButton[tagNr]->setMenu(menu);
512 buttonsVBoxLayout->addWidget(m_id3PushButton[tagNr]);
513 setTabOrder(tabWidget, m_id3PushButton[tagNr]);
514
515 QPushButton* toButton = new QPushButton(tr("To"));
516 menu = new QMenu(this);
517 action = menu->addAction(tr("Filename"));
518 connect(action, &QAction::triggered,
519 m_app->tag(tagNr), &Kid3ApplicationTagContext::getFilenameFromTags);
520 FOR_ALL_TAGS(fromTagNr) {
521 if (fromTagNr != tagNr) {
522 action = menu->addAction(
523 tr("Tag %1").arg(Frame::tagNumberToString(fromTagNr)));
524 QByteArray ba;
525 ba.append(static_cast<char>(tagNr));
526 ba.append(static_cast<char>(fromTagNr));
527 action->setData(ba);
528 connect(action, &QAction::triggered,
529 this, &Kid3Form::copyTagsActionData);
530 }
531 }
532 action = menu->addAction(tr("Copy"));
533 connect(action, &QAction::triggered,
534 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyTags);
535 toButton->setMenu(menu);
536 buttonsVBoxLayout->addWidget(toButton);
537 setTabOrder(m_id3PushButton[tagNr], toButton);
538 tabWidget = toButton;
539 }
540
541 QPushButton* removePushButton =
542 new QPushButton(tr("Remove"), m_tagWidget[tagNr]);
543 connect(removePushButton, &QAbstractButton::clicked,
544 m_app->tag(tagNr), &Kid3ApplicationTagContext::removeTags);
545 buttonsVBoxLayout->addWidget(removePushButton);
546 setTabOrder(tabWidget, removePushButton);
547 tabWidget = removePushButton;
548
549 if (tagNr != Frame::Tag_Id3v1) {
550 QFrame* frameLine = new QFrame;
551 frameLine->setFrameShape(QFrame::HLine);
552 frameLine->setFrameShadow(QFrame::Sunken);
553 buttonsVBoxLayout->addWidget(frameLine);
554
555 QPushButton* editFramesPushButton =
556 new QPushButton(tr("Edit..."), m_tagWidget[tagNr]);
557 connect(editFramesPushButton, &QAbstractButton::clicked,
558 m_app->tag(tagNr), &Kid3ApplicationTagContext::editFrame);
559 buttonsVBoxLayout->addWidget(editFramesPushButton);
560 setTabOrder(tabWidget, editFramesPushButton);
561 QPushButton* framesAddPushButton =
562 new QPushButton(tr("Add..."), m_tagWidget[tagNr]);
563 connect(framesAddPushButton, &QAbstractButton::clicked,
564 m_app->tag(tagNr), &Kid3ApplicationTagContext::addFrame);
565 buttonsVBoxLayout->addWidget(framesAddPushButton);
566 setTabOrder(editFramesPushButton, framesAddPushButton);
567 QPushButton* deleteFramesPushButton =
568 new QPushButton(tr("Delete"), m_tagWidget[tagNr]);
569 connect(deleteFramesPushButton, &QAbstractButton::clicked,
570 m_app->tag(tagNr), &Kid3ApplicationTagContext::deleteFrame);
571 buttonsVBoxLayout->addWidget(deleteFramesPushButton);
572 setTabOrder(framesAddPushButton, deleteFramesPushButton);
573 tabWidget = deleteFramesPushButton;
574 }
575 if (tagNr == Frame::Tag_Picture) {
576 m_pictureLabel = new PictureLabel(this);
577 m_pictureLabel->installEventFilter(new PictureDblClickHandler(m_app));
578 buttonsVBoxLayout->addWidget(m_pictureLabel);
579 }
580
581 buttonsVBoxLayout->addItem(
582 new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
583 }
584
585 rightHalfLayout->insertStretch(-1);
586
587 FOR_ALL_TAGS(tagNr) {
588 setTabOrder(tabWidget, m_frameTable[tagNr]);
589 tabWidget = m_frameTable[tagNr];
590 }
591
592 auto sectionActions = new SectionActions(SectionActions::Navigation,
593 m_fileListBox);
594 m_sectionActions.append(sectionActions);
595 connect(sectionActions->previousSectionAction(), &QAction::triggered,
596 this, [this]() { setFocusPreviousTag(Frame::Tag_NumValues); });
597 connect(sectionActions->nextSectionAction(), &QAction::triggered,
598 this, &Kid3Form::setFocusDirList);
599
600 sectionActions = new SectionActions(SectionActions::Navigation, m_dirListBox);
601 m_sectionActions.append(sectionActions);
602 connect(sectionActions->previousSectionAction(), &QAction::triggered,
603 this, &Kid3Form::setFocusFileList);
604 connect(sectionActions->nextSectionAction(), &QAction::triggered,
605 this, &Kid3Form::setFocusFilename);
606
607 sectionActions = new SectionActions(SectionActions::Navigation |
608 SectionActions::Transfer,
609 m_fileWidget);
610 m_sectionActions.append(sectionActions);
611 connect(sectionActions->previousSectionAction(), &QAction::triggered,
612 this, &Kid3Form::setFocusDirList);
613 connect(sectionActions->nextSectionAction(), &QAction::triggered,
614 this, [this]() { setFocusNextTag(Frame::Tag_NumValues); });
615 connect(sectionActions->transferAction(), &QAction::triggered,
616 m_app->tag(Frame::Tag_2), &Kid3ApplicationTagContext::getFilenameFromTags);
617
618 FOR_ALL_TAGS(tagNr) {
619 sectionActions = new SectionActions(
620 tagNr == Frame::Tag_1 ? SectionActions::Navigation |
621 SectionActions::Transfer |
622 SectionActions::EditSection
623 : SectionActions::Navigation |
624 SectionActions::Transfer |
625 SectionActions::EditSection |
626 SectionActions::EditElement,
627 m_frameTable[tagNr]);
628 m_sectionActions.append(sectionActions);
629 connect(sectionActions->previousSectionAction(), &QAction::triggered,
630 this, [this, tagNr]() {
631 setFocusPreviousTag(tagNr);
632 });
633 connect(sectionActions->nextSectionAction(), &QAction::triggered,
634 this, [this, tagNr]() {
635 setFocusNextTag(tagNr);
636 });
637
638 connect(sectionActions->copyAction(), &QAction::triggered,
639 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyTags);
640 connect(sectionActions->pasteAction(), &QAction::triggered,
641 m_app->tag(tagNr), &Kid3ApplicationTagContext::pasteTags);
642 connect(sectionActions->removeAction(), &QAction::triggered,
643 this, [this, tagNr]() {
644 m_app->tag(tagNr)->removeTags();
645 setFocusTag(tagNr);
646 });
647
648 QByteArray ba;
649 ba.append(static_cast<char>(tagNr == Frame::Tag_2 ? Frame::Tag_1
650 : Frame::Tag_2));
651 ba.append(static_cast<char>(tagNr));
652 sectionActions->transferAction()->setData(ba);
653 connect(sectionActions->transferAction(), &QAction::triggered,
654 this, &Kid3Form::copyTagsActionData);
655
656 if (tagNr != Frame::Tag_1) {
657 connect(sectionActions->editAction(), &QAction::triggered,
658 m_app->tag(tagNr), &Kid3ApplicationTagContext::editFrame);
659 connect(sectionActions->addAction(), &QAction::triggered,
660 m_app->tag(tagNr), &Kid3ApplicationTagContext::addFrame);
661 connect(sectionActions->deleteAction(), &QAction::triggered,
662 m_app->tag(tagNr), &Kid3ApplicationTagContext::deleteFrame);
663 }
664 }
665 }
666
667 /**
668 * Destructor.
669 */
~Kid3Form()670 Kid3Form::~Kid3Form()
671 {
672 m_app->removeFrameEditor(m_mainWin);
673 }
674
675 /**
676 * Set keyboard shortcuts for section actions.
677 * @param map map of action names to key sequences
678 */
setSectionActionShortcuts(const QMap<QString,QKeySequence> & map)679 void Kid3Form::setSectionActionShortcuts(const QMap<QString, QKeySequence>& map)
680 {
681 for (SectionActions* sectionActions : m_sectionActions) {
682 sectionActions->setShortcuts(map);
683 }
684 m_fileListBox->setShortcuts(map);
685 m_dirListBox->setShortcuts(map);
686 }
687
688 /**
689 * Handle event when mouse is moved while dragging.
690 *
691 * @param ev drag event.
692 */
dragMoveEvent(QDragMoveEvent * ev)693 void Kid3Form::dragMoveEvent(QDragMoveEvent* ev)
694 {
695 if (ev->mimeData()->hasFormat(QLatin1String("text/uri-list")) ||
696 ev->mimeData()->hasImage()) {
697 ev->acceptProposedAction();
698 } else {
699 ev->ignore();
700 }
701 }
702
703 /**
704 * Accept drag.
705 *
706 * @param ev drag event.
707 */
dragEnterEvent(QDragEnterEvent * ev)708 void Kid3Form::dragEnterEvent(QDragEnterEvent* ev)
709 {
710 Kid3Form::dragMoveEvent(ev);
711 }
712
713 /**
714 * Handle event when mouse leaves widget while dragging.
715 *
716 * @param ev drag event.
717 */
dragLeaveEvent(QDragLeaveEvent * ev)718 void Kid3Form::dragLeaveEvent(QDragLeaveEvent* ev)
719 {
720 ev->accept();
721 }
722
723 /**
724 * Handle drop event.
725 *
726 * @param ev drop event.
727 */
dropEvent(QDropEvent * ev)728 void Kid3Form::dropEvent(QDropEvent* ev)
729 {
730 if (ev->mimeData()->hasImage()) {
731 QImage image = qvariant_cast<QImage>(ev->mimeData()->imageData());
732 ev->acceptProposedAction();
733 if (!image.isNull()) {
734 QByteArray ba;
735 QBuffer buffer(&ba);
736 buffer.open(QIODevice::WriteOnly);
737 image.save(&buffer, "JPG");
738 PictureFrame frame;
739 if (PictureFrame::setData(frame, ba)) {
740 m_app->dropImage(&frame);
741 }
742 }
743 } else if (ev->mimeData()->hasFormat(QLatin1String("text/uri-list"))) {
744 QList<QUrl> urls = ev->mimeData()->urls();
745 ev->acceptProposedAction();
746 m_app->dropUrls(urls, ev->source() != nullptr);
747 } else {
748 ev->ignore();
749 }
750 }
751
752 /**
753 * Filename line edit is changed.
754 * @param txt contents of line edit
755 */
nameLineEditChanged(const QString & txt)756 void Kid3Form::nameLineEditChanged(const QString& txt)
757 {
758 formatLineEdit(m_nameLineEdit, txt, &FilenameFormatConfig::instance());
759 }
760
761 /**
762 * Mark the filename as changed.
763 * @param en true to mark as changed
764 */
markChangedFilename(bool en)765 void Kid3Form::markChangedFilename(bool en)
766 {
767 CoreTaggedFileIconProvider* colorProvider;
768 if (en &&
769 (colorProvider = m_app->getPlatformTools()->iconProvider()) != nullptr) {
770 QPalette changedPalette(m_nameLabel->palette());
771 changedPalette.setBrush(QPalette::Active, QPalette::Window,
772 colorProvider->colorForContext(ColorContext::Marked)
773 .value<QBrush>());
774 m_nameLabel->setPalette(changedPalette);
775 } else {
776 m_nameLabel->setPalette(QPalette());
777 }
778 m_nameLabel->setAutoFillBackground(en);
779 }
780
781 /**
782 * Format string within line edit.
783 *
784 * @param le line edit
785 * @param txt text in line edit
786 * @param fcfg format configuration
787 */
formatLineEdit(QLineEdit * le,const QString & txt,const FormatConfig * fcfg)788 void Kid3Form::formatLineEdit(QLineEdit* le, const QString& txt,
789 const FormatConfig* fcfg)
790 {
791 if (fcfg->formatWhileEditing()) {
792 QString str(txt);
793 fcfg->formatString(str);
794 if (str != txt) {
795 int curPos = le->cursorPosition();
796 le->setText(str);
797 le->setCursorPosition(curPos + str.length() - txt.length());
798 }
799 }
800 }
801
802 /**
803 * Directory list box directory selected.
804 *
805 * @param index selected item
806 */
dirSelected(const QModelIndex & index)807 void Kid3Form::dirSelected(const QModelIndex& index)
808 {
809 QString dirPath = index.data(FileSystemModel::FilePathRole).toString();
810 if (!dirPath.isEmpty()) {
811 m_app->setDirUpIndex(
812 dirPath.endsWith(QLatin1String("..")) ? index.parent() : QModelIndex());
813 m_mainWin->updateCurrentSelection();
814 m_mainWin->confirmedOpenDirectory({dirPath});
815 }
816 }
817
818 /**
819 * File list box item activated.
820 *
821 * @param index selected item
822 */
fileActivated(const QModelIndex & index)823 void Kid3Form::fileActivated(const QModelIndex& index)
824 {
825 if (const FileProxyModel* fileProxyModel =
826 qobject_cast<const FileProxyModel*>(index.model())) {
827 if (fileProxyModel->isDir(index)) {
828 QString dirPath = fileProxyModel->filePath(index);
829 if (!dirPath.isEmpty()) {
830 m_mainWin->updateCurrentSelection();
831 m_mainWin->confirmedOpenDirectory({dirPath});
832 }
833 }
834 }
835 }
836
837 /**
838 * Open the parent directory of a model index.
839 *
840 * @param index current root index of item view
841 */
openParentDirectory(const QModelIndex & index)842 void Kid3Form::openParentDirectory(const QModelIndex& index)
843 {
844 if (index.isValid()) {
845 QDir dir(index.data(FileSystemModel::FilePathRole).toString());
846 if (dir.cdUp()) {
847 QString dirPath = dir.absolutePath();
848 if (m_dirListBox && index.model() == m_dirListBox->model()) {
849 m_app->setDirUpIndex(index);
850 }
851 m_mainWin->updateCurrentSelection();
852 m_mainWin->confirmedOpenDirectory({dirPath});
853 }
854 }
855 }
856
857 /**
858 * Enable or disable controls requiring tags.
859 * @param tagNr tag number
860 * @param enable true to enable
861 */
enableControls(Frame::TagNumber tagNr,bool enable)862 void Kid3Form::enableControls(Frame::TagNumber tagNr, bool enable)
863 {
864 if (m_fnButton[tagNr]) {
865 m_fnButton[tagNr]->setEnabled(enable);
866 }
867 if (m_toTagButton[tagNr]) {
868 m_toTagButton[tagNr]->setEnabled(enable);
869 }
870 Frame::TagNumber otherTagNr = tagNr == Frame::Tag_1
871 ? Frame::Tag_2
872 : tagNr == Frame::Tag_2 ? Frame::Tag_1 : Frame::Tag_NumValues;
873 if (otherTagNr < Frame::Tag_NumValues) {
874 m_id3PushButton[otherTagNr]->setEnabled(enable);
875 }
876 m_tagWidget[tagNr]->setEnabled(enable);
877 if (tagNr > Frame::Tag_2) {
878 m_tagButton[tagNr]->setVisible(enable);
879 m_tagLabel[tagNr]->setVisible(enable);
880 }
881 }
882
883 /**
884 * Display the tag format.
885 * @param tagNr tag number
886 * @param str string describing format, e.g. "ID3v1.1"
887 */
setTagFormat(Frame::TagNumber tagNr,const QString & str)888 void Kid3Form::setTagFormat(Frame::TagNumber tagNr, const QString& str)
889 {
890 QString txt = tr("Tag &%1").arg(Frame::tagNumberToString(tagNr));
891 if (!str.isEmpty()) {
892 txt += QLatin1String(": ");
893 txt += str;
894 }
895 m_tagLabel[tagNr]->setText(txt);
896 }
897
898 /**
899 * Adjust the size of the right half box.
900 */
adjustRightHalfBoxSize()901 void Kid3Form::adjustRightHalfBoxSize()
902 {
903 m_rightHalfVBox->adjustSize();
904 }
905
906 /**
907 * Hide or show file controls.
908 *
909 * @param hide true to hide, false to show
910 */
hideFile(bool hide)911 void Kid3Form::hideFile(bool hide)
912 {
913 if (hide) {
914 m_fileWidget->hide();
915 m_fileButton->setIcon(*s_expandPixmap);
916 } else {
917 m_fileWidget->show();
918 m_fileButton->setIcon(*s_collapsePixmap);
919 }
920 }
921
922 /**
923 * Hide or show tag controls.
924 * @param tagNr tag number
925 * @param hide true to hide, false to show
926 */
hideTag(Frame::TagNumber tagNr,bool hide)927 void Kid3Form::hideTag(Frame::TagNumber tagNr, bool hide)
928 {
929 if (hide) {
930 m_tagWidget[tagNr]->hide();
931 m_tagButton[tagNr]->setIcon(*s_expandPixmap);
932 } else {
933 m_tagWidget[tagNr]->show();
934 m_tagButton[tagNr]->setIcon(*s_collapsePixmap);
935 }
936 }
937
938 /**
939 * Toggle visibility of file controls.
940 */
showHideFile()941 void Kid3Form::showHideFile()
942 {
943 hideFile(!m_fileWidget->isHidden());
944 }
945
946 /**
947 * Toggle visibility of tag controls.
948 * @param tagNr tag number
949 */
showHideTag(Frame::TagNumber tagNr)950 void Kid3Form::showHideTag(Frame::TagNumber tagNr)
951 {
952 hideTag(tagNr, !m_tagWidget[tagNr]->isHidden());
953 }
954
955 /**
956 * Set format text configuration when format edit text is changed.
957 * @param text format text
958 */
onFormatEditTextChanged(const QString & text)959 void Kid3Form::onFormatEditTextChanged(const QString& text)
960 {
961 FileConfig::instance().setToFilenameFormat(text);
962 }
963
964 /**
965 * Set format from filename text configuration when edit text is changed.
966 * @param text format text
967 */
onFormatFromFilenameEditTextChanged(const QString & text)968 void Kid3Form::onFormatFromFilenameEditTextChanged(const QString& text)
969 {
970 FileConfig::instance().setFromFilenameFormat(text);
971 }
972
973 /**
974 * Update sorting after directory is opened for the first time.
975 * The sort order of the file list is not correct if it is not explicitly
976 * sorted the first time.
977 */
onFirstDirectoryOpened()978 void Kid3Form::onFirstDirectoryOpened()
979 {
980 // Only call this once.
981 disconnect(m_app, &Kid3Application::directoryOpened,
982 this, &Kid3Form::onFirstDirectoryOpened);
983 const GuiConfig& guiCfg = GuiConfig::instance();
984 m_app->getFileProxyModel()->sort(guiCfg.fileListSortColumn(),
985 guiCfg.fileListSortOrder());
986 int firstFileSectionSize =
987 m_fileListBox->initializeColumnWidthsFromContents(-1);
988 m_fileListBox->scrollTo(m_fileListBox->currentIndex());
989 // The directory column often only contains "." and "..", which results
990 // in a small size. Make it at least as wide as the corresponding
991 // file list column.
992 m_dirListBox->initializeColumnWidthsFromContents(firstFileSectionSize);
993 }
994
995 /**
996 * Hide or show picture.
997 *
998 * @param hide true to hide, false to show
999 */
hidePicture(bool hide)1000 void Kid3Form::hidePicture(bool hide)
1001 {
1002 if (!m_pictureLabel)
1003 return;
1004
1005 if (hide) {
1006 m_pictureLabel->hide();
1007 } else {
1008 m_pictureLabel->show();
1009 }
1010 }
1011
1012 /**
1013 * Set focus on filename controls.
1014 */
setFocusFilename()1015 void Kid3Form::setFocusFilename()
1016 {
1017 if (m_fileWidget->isHidden()) {
1018 hideFile(false);
1019 }
1020 if (isFilenameEditEnabled()) {
1021 m_nameLineEdit->setFocus();
1022 } else {
1023 m_formatComboBox->setFocus();
1024 }
1025 }
1026
1027 /**
1028 * Set focus on tag controls.
1029 * @param tagNr tag number
1030 */
setFocusTag(Frame::TagNumber tagNr)1031 void Kid3Form::setFocusTag(Frame::TagNumber tagNr)
1032 {
1033 if (m_tagWidget[tagNr]->isHidden()) {
1034 hideTag(tagNr, false);
1035 }
1036 m_frameTable[tagNr]->setFocus();
1037 }
1038
1039 /**
1040 * Set focus on next tag controls.
1041 * @param tagNr current tag, Frame::Tag_NumValues if not on tag
1042 */
setFocusNextTag(Frame::TagNumber tagNr)1043 void Kid3Form::setFocusNextTag(Frame::TagNumber tagNr)
1044 {
1045 for (int i = tagNr == Frame::Tag_NumValues ? Frame::Tag_1
1046 : tagNr + 1; ; ++i) {
1047 if (i >= Frame::Tag_NumValues) {
1048 setFocusFileList();
1049 break;
1050 } else if (i >= Frame::Tag_1) {
1051 if (m_tagWidget[i]->isEnabled()) {
1052 setFocusTag(static_cast<Frame::TagNumber>(i));
1053 break;
1054 }
1055 } else {
1056 break;
1057 }
1058 }
1059 }
1060
1061 /**
1062 * Set focus on previous tag controls.
1063 * @param tagNr current tag, Frame::Tag_NumValues if not on tag
1064 */
setFocusPreviousTag(Frame::TagNumber tagNr)1065 void Kid3Form::setFocusPreviousTag(Frame::TagNumber tagNr)
1066 {
1067 for (int i = tagNr - 1; ; --i) {
1068 if (i < Frame::Tag_1) {
1069 setFocusFilename();
1070 break;
1071 } else if (i < Frame::Tag_NumValues) {
1072 if (m_tagWidget[i]->isEnabled()) {
1073 setFocusTag(static_cast<Frame::TagNumber>(i));
1074 break;
1075 }
1076 } else {
1077 break;
1078 }
1079 }
1080 }
1081
1082 /**
1083 * Set focus on file list.
1084 */
setFocusFileList()1085 void Kid3Form::setFocusFileList()
1086 {
1087 m_fileListBox->setFocus();
1088 }
1089
1090 /**
1091 * Set focus on directory list.
1092 */
setFocusDirList()1093 void Kid3Form::setFocusDirList()
1094 {
1095 m_dirListBox->setFocus();
1096 }
1097
1098 /**
1099 * Save the local settings to the configuration.
1100 */
saveConfig()1101 void Kid3Form::saveConfig()
1102 {
1103 GuiConfig& guiCfg = GuiConfig::instance();
1104 FileConfig& fileCfg = FileConfig::instance();
1105 guiCfg.setSplitterSizes(sizes());
1106 guiCfg.setVSplitterSizes(m_vSplitter->sizes());
1107 fileCfg.setToFilenameFormat(m_formatComboBox->currentText());
1108 fileCfg.setToFilenameFormats(getItemsFromComboBox(m_formatComboBox));
1109 fileCfg.setFromFilenameFormat(m_formatFromFilenameComboBox->currentText());
1110 fileCfg.setFromFilenameFormats(getItemsFromComboBox(m_formatFromFilenameComboBox));
1111 if (!guiCfg.autoHideTags()) {
1112 guiCfg.setHideFile(m_fileWidget->isHidden());
1113 FOR_ALL_TAGS(tagNr) {
1114 guiCfg.setHideTag(tagNr, m_tagWidget[tagNr]->isHidden());
1115 }
1116 }
1117 int column;
1118 Qt::SortOrder order;
1119 m_fileListBox->getSortByColumn(column, order);
1120 guiCfg.setFileListSortColumn(column);
1121 guiCfg.setFileListSortOrder(order);
1122 guiCfg.setFileListVisibleColumns(m_fileListBox->getVisibleColumns());
1123 bool customColumWidthsEnabled = m_fileListBox->areCustomColumnWidthsEnabled();
1124 guiCfg.setFileListCustomColumnWidthsEnabled(customColumWidthsEnabled);
1125 if (customColumWidthsEnabled) {
1126 guiCfg.setFileListColumnWidths(m_fileListBox->getColumnWidths());
1127 }
1128 m_dirListBox->getSortByColumn(column, order);
1129 guiCfg.setDirListSortColumn(column);
1130 guiCfg.setDirListSortOrder(order);
1131 guiCfg.setDirListVisibleColumns(m_dirListBox->getVisibleColumns());
1132 customColumWidthsEnabled = m_dirListBox->areCustomColumnWidthsEnabled();
1133 guiCfg.setDirListCustomColumnWidthsEnabled(customColumWidthsEnabled);
1134 if (customColumWidthsEnabled) {
1135 guiCfg.setDirListColumnWidths(m_dirListBox->getColumnWidths());
1136 }
1137 }
1138
1139 /**
1140 * Read the local settings from the configuration.
1141 */
readConfig()1142 void Kid3Form::readConfig()
1143 {
1144 const GuiConfig& guiCfg = GuiConfig::instance();
1145 const FileConfig& fileCfg = FileConfig::instance();
1146 if (!guiCfg.splitterSizes().isEmpty()) {
1147 setSizes(guiCfg.splitterSizes());
1148 } else {
1149 setSizes({307, 601});
1150 }
1151 if (!guiCfg.vSplitterSizes().isEmpty()) {
1152 m_vSplitter->setSizes(guiCfg.vSplitterSizes());
1153 } else {
1154 m_vSplitter->setSizes({451, 109});
1155 }
1156
1157 setToFilenameFormats();
1158 setFromFilenameFormats();
1159 connect(&fileCfg, &FileConfig::toFilenameFormatsChanged,
1160 this, &Kid3Form::setToFilenameFormats, Qt::UniqueConnection);
1161 connect(&fileCfg, &FileConfig::fromFilenameFormatsChanged,
1162 this, &Kid3Form::setFromFilenameFormats, Qt::UniqueConnection);
1163
1164 if (!guiCfg.autoHideTags()) {
1165 hideFile(guiCfg.hideFile());
1166 FOR_ALL_TAGS(tagNr) {
1167 hideTag(tagNr, guiCfg.hideTag(tagNr));
1168 }
1169 }
1170 hidePicture(guiCfg.hidePicture());
1171 m_fileListBox->sortByColumn(guiCfg.fileListSortColumn(),
1172 guiCfg.fileListSortOrder());
1173 m_fileListBox->setVisibleColumns(guiCfg.fileListVisibleColumns());
1174 m_fileListBox->setColumnWidths(guiCfg.fileListColumnWidths());
1175 m_fileListBox->setCustomColumnWidthsEnabled(
1176 guiCfg.fileListCustomColumnWidthsEnabled());
1177 m_dirListBox->sortByColumn(guiCfg.dirListSortColumn(),
1178 guiCfg.dirListSortOrder());
1179 m_dirListBox->setVisibleColumns(guiCfg.dirListVisibleColumns());
1180 m_dirListBox->setColumnWidths(guiCfg.dirListColumnWidths());
1181 m_dirListBox->setCustomColumnWidthsEnabled(
1182 guiCfg.dirListCustomColumnWidthsEnabled());
1183 }
1184
1185 /**
1186 * Set items of "Format <arrow up>" combo box from file configuration.
1187 */
setToFilenameFormats()1188 void Kid3Form::setToFilenameFormats()
1189 {
1190 const FileConfig& fileCfg = FileConfig::instance();
1191 setItemsInComboBox(fileCfg.toFilenameFormats(), fileCfg.toFilenameFormat(),
1192 m_formatComboBox);
1193 }
1194
1195 /**
1196 * Set items of "Format <arrow down>" combo box from file configuration.
1197 */
setFromFilenameFormats()1198 void Kid3Form::setFromFilenameFormats()
1199 {
1200 const FileConfig& fileCfg = FileConfig::instance();
1201 setItemsInComboBox(fileCfg.fromFilenameFormats(), fileCfg.fromFilenameFormat(),
1202 m_formatFromFilenameComboBox);
1203 }
1204
1205 /**
1206 * Set preview picture data.
1207 * @param data picture data, empty if no picture is available
1208 */
setPictureData(const QByteArray & data)1209 void Kid3Form::setPictureData(const QByteArray& data)
1210 {
1211 if (m_pictureLabel) {
1212 m_pictureLabel->setData(data);
1213 }
1214 }
1215
1216 /**
1217 * Set details info text.
1218 *
1219 * @param str detail information summary as string
1220 */
setDetailInfo(const QString & str)1221 void Kid3Form::setDetailInfo(const QString& str)
1222 {
1223 m_fileLabel->setText(!str.isEmpty()
1224 ? tr("F&ile") + QLatin1String(": ") + str
1225 : tr("F&ile"));
1226 }
1227
1228 /**
1229 * Select all files.
1230 */
selectAllFiles()1231 void Kid3Form::selectAllFiles()
1232 {
1233 m_fileListBox->selectAll();
1234 }
1235
1236 /**
1237 * Deselect all files.
1238 */
deselectAllFiles()1239 void Kid3Form::deselectAllFiles()
1240 {
1241 m_fileListBox->clearSelection();
1242 }
1243
1244 /**
1245 * Set the next file as the current file.
1246 *
1247 * @param select true to select the file
1248 * @param onlyTaggedFiles only consider tagged files
1249 *
1250 * @return true if a next file exists.
1251 */
nextFile(bool select,bool onlyTaggedFiles)1252 bool Kid3Form::nextFile(bool select, bool onlyTaggedFiles)
1253 {
1254 FrameTable* editingFrameTable = getEditingFrameTable();
1255 bool ok = m_app->nextFile(select, onlyTaggedFiles);
1256 if (ok && editingFrameTable) {
1257 editingFrameTable->edit(editingFrameTable->currentIndex());
1258 }
1259 return ok;
1260 }
1261
1262 /**
1263 * Set the previous file as the current file.
1264 *
1265 * @param select true to select the file
1266 * @param onlyTaggedFiles only consider tagged files
1267 *
1268 * @return true if a previous file exists.
1269 */
previousFile(bool select,bool onlyTaggedFiles)1270 bool Kid3Form::previousFile(bool select, bool onlyTaggedFiles)
1271 {
1272 FrameTable* editingFrameTable = getEditingFrameTable();
1273 bool ok = m_app->previousFile(select, onlyTaggedFiles);
1274 if (ok && editingFrameTable) {
1275 editingFrameTable->edit(editingFrameTable->currentIndex());
1276 }
1277 return ok;
1278 }
1279
1280 /**
1281 * Select the next tagged file as the current file.
1282 * Same as nextFile() with default arguments, provided for functor-based
1283 * connections.
1284 * @return true if a next file exists.
1285 */
selectNextTaggedFile()1286 bool Kid3Form::selectNextTaggedFile()
1287 {
1288 return nextFile(true, true);
1289 }
1290
1291 /**
1292 * Select the previous tagged file as the current file.
1293 * Same as previousFile() with default arguments, provided for functor-based
1294 * connections.
1295 * @return true if a previous file exists.
1296 */
selectPreviousTaggedFile()1297 bool Kid3Form::selectPreviousTaggedFile()
1298 {
1299 return previousFile(true, true);
1300 }
1301
1302 /**
1303 * Get frame table which is currently in editing state.
1304 * The returned frame table can be used to restore the editing state after
1305 * changing the current file.
1306 * @return frame table which is in editing state, 0 if none.
1307 */
getEditingFrameTable() const1308 FrameTable* Kid3Form::getEditingFrameTable() const
1309 {
1310 if (QWidget* focusWidget = QApplication::focusWidget()) {
1311 FOR_ALL_TAGS(tagNr) {
1312 if (m_frameTable[tagNr]->getCurrentEditor() == focusWidget) {
1313 return m_frameTable[tagNr];
1314 }
1315 }
1316 }
1317 return nullptr;
1318 }
1319
1320 /**
1321 * Set the root index of the file list.
1322 *
1323 * @param index root index of directory in file proxy model
1324 */
setFileRootIndex(const QModelIndex & index)1325 void Kid3Form::setFileRootIndex(const QModelIndex& index)
1326 {
1327 if (index.isValid()) {
1328 m_fileListBox->setRootIndex(index);
1329 m_fileListBox->scrollTo(m_fileListBox->currentIndex());
1330 }
1331 }
1332
1333 /**
1334 * Set the root index of the directory list.
1335 *
1336 * @param index root index of directory in directory model
1337 */
setDirRootIndex(const QModelIndex & index)1338 void Kid3Form::setDirRootIndex(const QModelIndex& index)
1339 {
1340 if (index.isValid()) {
1341 m_dirListBox->setRootIndex(index);
1342 }
1343 }
1344
1345 /**
1346 * Set a widget to be displayed at the left side instead of the file lists.
1347 * @param widget widget to be shown at the left side
1348 */
setLeftSideWidget(QWidget * widget)1349 void Kid3Form::setLeftSideWidget(QWidget* widget)
1350 {
1351 int idx = m_leftSideWidget->addWidget(widget);
1352 m_leftSideWidget->setCurrentIndex(idx);
1353 }
1354
1355 /**
1356 * Remove widget set with setLeftSideWidget().
1357 *
1358 * The widget will not be deleted.
1359 *
1360 * @param widget widget to be removed
1361 */
removeLeftSideWidget(QWidget * widget)1362 void Kid3Form::removeLeftSideWidget(QWidget* widget)
1363 {
1364 m_leftSideWidget->removeWidget(widget);
1365 }
1366
1367 /**
1368 * Copy tags using QAction::data().
1369 * The source and destination tag numbers are taken from the first two bytes
1370 * in QAction::data().toByteArray() if the sender() is a QAction.
1371 */
copyTagsActionData()1372 void Kid3Form::copyTagsActionData()
1373 {
1374 if (auto action = qobject_cast<QAction*>(sender())) {
1375 QByteArray ba = action->data().toByteArray();
1376 if (ba.size() == 2) {
1377 Frame::TagNumber srcTagNr = Frame::tagNumberCast(ba.at(0));
1378 Frame::TagNumber dstTagNr = Frame::tagNumberCast(ba.at(1));
1379 if (srcTagNr != Frame::Tag_NumValues &&
1380 dstTagNr != Frame::Tag_NumValues) {
1381 m_app->copyTag(srcTagNr, dstTagNr);
1382 }
1383 }
1384 }
1385 }
1386