File indexing completed on 2025-02-23 04:34:21
0001 /** 0002 * \file kid3form.cpp 0003 * GUI for kid3. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 8 Apr 2003 0008 * 0009 * Copyright (C) 2003-2024 Urs Fleisch 0010 * 0011 * This file is part of Kid3. 0012 * 0013 * Kid3 is free software; you can redistribute it and/or modify 0014 * it under the terms of the GNU General Public License as published by 0015 * the Free Software Foundation; either version 2 of the License, or 0016 * (at your option) any later version. 0017 * 0018 * Kid3 is distributed in the hope that it will be useful, 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0021 * GNU General Public License for more details. 0022 * 0023 * You should have received a copy of the GNU General Public License 0024 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0025 */ 0026 0027 #include "kid3form.h" 0028 #include <QCheckBox> 0029 #include <QPushButton> 0030 #include <QToolButton> 0031 #include <QSpinBox> 0032 #include <QLayout> 0033 #include <QToolTip> 0034 #include <QStackedWidget> 0035 #include <QSplitter> 0036 #include <QDir> 0037 #include <QFrame> 0038 #include <QPixmap> 0039 #include <QComboBox> 0040 #include <QVBoxLayout> 0041 #include <QDropEvent> 0042 #include <QDragEnterEvent> 0043 #include <QLabel> 0044 #include <QGridLayout> 0045 #include <QScrollArea> 0046 #include <QUrl> 0047 #include <QApplication> 0048 #include <QMimeData> 0049 #include <QMenu> 0050 #include <QFileIconProvider> 0051 #include <QStyle> 0052 #include <QHeaderView> 0053 #include <QBuffer> 0054 #include "filesystemmodel.h" 0055 #include "frametable.h" 0056 #include "frametablemodel.h" 0057 #include "trackdata.h" 0058 #include "genres.h" 0059 #include "basemainwindow.h" 0060 #include "filelist.h" 0061 #include "framelist.h" 0062 #include "pictureframe.h" 0063 #include "configurabletreeview.h" 0064 #include "picturelabel.h" 0065 #include "fileconfig.h" 0066 #include "guiconfig.h" 0067 #include "formatconfig.h" 0068 #include "dirproxymodel.h" 0069 #include "fileproxymodel.h" 0070 #include "coretaggedfileiconprovider.h" 0071 #include "icoreplatformtools.h" 0072 #include "kid3application.h" 0073 #include "sectionactions.h" 0074 #ifdef Q_OS_MAC 0075 #include <CoreFoundation/CFURL.h> 0076 #endif 0077 0078 /** Collapse pixmap, will be allocated in constructor */ 0079 QPixmap* Kid3Form::s_collapsePixmap = nullptr; 0080 /** Expand pixmap, will be allocated in constructor */ 0081 QPixmap* Kid3Form::s_expandPixmap = nullptr; 0082 0083 namespace { 0084 0085 /** picture for collapse pixmap */ 0086 const char* const collapse_xpm[] = { 0087 "7 7 3 1", 0088 " \tc None", 0089 ".\tc #FFFFFF", 0090 "+\tc #000000", 0091 ".......", 0092 ".......", 0093 ".......", 0094 ".+++++.", 0095 ".......", 0096 ".......", 0097 "......." 0098 }; 0099 0100 /** picture for expand pixmap */ 0101 const char* const expand_xpm[] = { 0102 "7 7 3 1", 0103 " \tc None", 0104 ".\tc #FFFFFF", 0105 "+\tc #000000", 0106 ".......", 0107 "...+...", 0108 "...+...", 0109 ".+++++.", 0110 "...+...", 0111 "...+...", 0112 "......." 0113 }; 0114 0115 0116 /** 0117 * Event filter for double click on picture label. 0118 */ 0119 class PictureDblClickHandler : public QObject { 0120 public: 0121 /** 0122 * Constructor. 0123 * @param app application 0124 */ 0125 explicit PictureDblClickHandler(Kid3Application* app) 0126 : QObject(app), m_app(app) {} 0127 0128 ~PictureDblClickHandler() override = default; 0129 0130 protected: 0131 /** 0132 * Event filter function, calls Kid3Application::editOrAddPicture(). 0133 * 0134 * @param obj watched object 0135 * @param event event for object 0136 * 0137 * @return true if event is filtered. 0138 */ 0139 bool eventFilter(QObject* obj, QEvent* event) override; 0140 0141 private: 0142 Q_DISABLE_COPY(PictureDblClickHandler) 0143 0144 Kid3Application* m_app; 0145 }; 0146 0147 /** 0148 * Event filter function, calls Kid3Application::editOrAddPicture() on double click. 0149 * 0150 * @param obj watched object 0151 * @param event event for object 0152 * 0153 * @return true if event is filtered. 0154 */ 0155 bool PictureDblClickHandler::eventFilter(QObject* obj, QEvent* event) 0156 { 0157 if (event->type() == QEvent::MouseButtonDblClick) { 0158 m_app->editOrAddPicture(); 0159 return true; 0160 } 0161 // standard event processing 0162 return QObject::eventFilter(obj, event); 0163 } 0164 0165 0166 /** 0167 * File decoration provider for FileSystemModel delegating to QFileIconProvider. 0168 */ 0169 class WidgetFileDecorationProvider : public AbstractFileDecorationProvider { 0170 public: 0171 QVariant headerDecoration() const override { 0172 // From QFileSystemModel: 0173 // ### TODO oh man this is ugly and doesn't even work all the way! 0174 // it is still 2 pixels off 0175 QImage pixmap(16, 1, QImage::Format_Mono); 0176 pixmap.fill(0); 0177 pixmap.setAlphaChannel(pixmap.createAlphaMask()); 0178 return pixmap; 0179 } 0180 0181 QVariant computerDecoration() const override { 0182 return m_provider.icon(QFileIconProvider::Computer); 0183 } 0184 0185 QVariant folderDecoration() const override { 0186 return m_provider.icon(QFileIconProvider::Folder); 0187 } 0188 0189 QVariant fileDecoration() const override { 0190 return m_provider.icon(QFileIconProvider::File); 0191 } 0192 0193 QVariant decoration(const QFileInfo& info) const override { 0194 return m_provider.icon(info); 0195 } 0196 0197 QString type(const QFileInfo& info) const override { 0198 return m_provider.type(info); 0199 } 0200 0201 private: 0202 QFileIconProvider m_provider; 0203 }; 0204 0205 0206 /** 0207 * Get the items from a combo box. 0208 * 0209 * @param comboBox combo box 0210 * 0211 * @return item texts from combo box. 0212 */ 0213 QStringList getItemsFromComboBox(const QComboBox* comboBox) 0214 { 0215 QStringList lst; 0216 const int numItems = comboBox->count(); 0217 lst.reserve(numItems); 0218 for (int i = 0; i < numItems; ++i) { 0219 lst += comboBox->itemText(i); 0220 } 0221 return lst; 0222 } 0223 0224 /** 0225 * Set items in combo box and add current item if not already existing. 0226 * @param items combo box item 0227 * @param currentItem current item 0228 * @param comboBox combo box to set 0229 */ 0230 void setItemsInComboBox(const QStringList& items, const QString& currentItem, 0231 QComboBox* comboBox) 0232 { 0233 QStringList allItems = items; 0234 int idx = allItems.indexOf(currentItem); 0235 if (idx == -1) { 0236 allItems.append(currentItem); 0237 idx = allItems.size() - 1; 0238 } 0239 // Block signals on combo box while setting contents to avoid 0240 // editTextChanged() signals causing configuration changes. 0241 comboBox->blockSignals(true); 0242 if (!allItems.isEmpty()) { 0243 comboBox->clear(); 0244 comboBox->addItems(allItems); 0245 } 0246 comboBox->setCurrentIndex(idx); 0247 comboBox->blockSignals(false); 0248 } 0249 0250 } 0251 0252 /** 0253 * Constructs an Id3Form as a child of 'parent', with the 0254 * name 'name' and widget flags set to 'f'. 0255 * @param app application 0256 * @param mainWin main window 0257 * @param parent parent widget 0258 */ 0259 Kid3Form::Kid3Form(Kid3Application* app, BaseMainWindowImpl* mainWin, 0260 QWidget* parent) 0261 : QSplitter(parent), m_pictureLabel(nullptr), m_app(app), m_mainWin(mainWin), 0262 m_iconProvider(new WidgetFileDecorationProvider) 0263 { 0264 setObjectName(QLatin1String("Kid3Form")); 0265 0266 FOR_ALL_TAGS(tagNr) { 0267 m_tagContext[tagNr] = new Kid3FormTagContext(this, tagNr); 0268 if (tagNr != Frame::Tag_Id3v1) { 0269 m_app->getFrameList(tagNr)->setFrameEditor(m_mainWin); 0270 } 0271 } 0272 0273 if (!s_collapsePixmap) { 0274 s_collapsePixmap = new QPixmap(const_cast<const char**>(collapse_xpm)); 0275 } 0276 if (!s_expandPixmap) { 0277 s_expandPixmap = new QPixmap(const_cast<const char**>(expand_xpm)); 0278 } 0279 0280 setAcceptDrops(true); 0281 setWindowTitle(tr("Kid3")); 0282 0283 m_leftSideWidget = new QStackedWidget(this); 0284 m_vSplitter = new QSplitter(Qt::Vertical); 0285 m_leftSideWidget->addWidget(m_vSplitter); 0286 m_fileListBox = new FileList(m_vSplitter, m_mainWin); 0287 FileProxyModel* fileProxyModel = m_app->getFileProxyModel(); 0288 if (auto fsModel = 0289 qobject_cast<FileSystemModel*>(fileProxyModel->sourceModel())) { 0290 fsModel->setDecorationProvider(m_iconProvider.data()); 0291 } 0292 CoreTaggedFileIconProvider* tagIconProvider = fileProxyModel->getIconProvider(); 0293 tagIconProvider->setModifiedIcon( 0294 QApplication::style()->standardIcon(QStyle::SP_DriveFDIcon)); 0295 int iconHeight = (((fontMetrics().height() - 1) / 16) + 1) * 16; 0296 tagIconProvider->setRequestedSize(QSize(iconHeight, iconHeight)); 0297 m_fileListBox->setModel(fileProxyModel); 0298 m_fileListBox->setSelectionModel(m_app->getFileSelectionModel()); 0299 m_dirListBox = new ConfigurableTreeView(m_vSplitter); 0300 m_dirListBox->setObjectName(QLatin1String("DirList")); 0301 m_dirListBox->setItemsExpandable(false); 0302 m_dirListBox->setRootIsDecorated(false); 0303 m_dirListBox->setModel(m_app->getDirProxyModel()); 0304 m_dirListBox->setSelectionModel(m_app->getDirSelectionModel()); 0305 m_dirListBox->setMaxNumColumns(TaggedFileSystemModel::NUM_FILESYSTEM_COLUMNS); 0306 connect(m_fileListBox, &QAbstractItemView::activated, 0307 this, &Kid3Form::fileActivated); 0308 connect(m_dirListBox, &QAbstractItemView::activated, this, 0309 &Kid3Form::dirSelected); 0310 connect(m_fileListBox, &ConfigurableTreeView::parentActivated, 0311 this, &Kid3Form::openParentDirectory); 0312 connect(m_dirListBox, &ConfigurableTreeView::parentActivated, 0313 this, &Kid3Form::openParentDirectory); 0314 0315 connect(m_app, &Kid3Application::fileRootIndexChanged, 0316 this, &Kid3Form::setFileRootIndex); 0317 connect(m_app, &Kid3Application::dirRootIndexChanged, 0318 this, &Kid3Form::setDirRootIndex); 0319 connect(m_app, &Kid3Application::directoryOpened, 0320 this, &Kid3Form::onFirstDirectoryOpened); 0321 0322 m_rightHalfVBox = new QWidget; 0323 auto scrollView = new QScrollArea(this); 0324 scrollView->setWidget(m_rightHalfVBox); 0325 scrollView->setWidgetResizable(true); 0326 auto rightHalfLayout = new QVBoxLayout(m_rightHalfVBox); 0327 rightHalfLayout->setSpacing(0); 0328 0329 m_fileButton = new QToolButton(m_rightHalfVBox); 0330 m_fileButton->setIcon(*s_collapsePixmap); 0331 m_fileButton->setAutoRaise(true); 0332 #ifdef Q_OS_MAC 0333 m_fileButton->setStyleSheet(QLatin1String("border: 0;")); 0334 #endif 0335 connect(m_fileButton, &QAbstractButton::clicked, this, &Kid3Form::showHideFile); 0336 m_fileLabel = new QLabel(tr("F&ile"), m_rightHalfVBox); 0337 auto fileButtonLayout = new QHBoxLayout; 0338 fileButtonLayout->addWidget(m_fileButton); 0339 fileButtonLayout->addWidget(m_fileLabel); 0340 rightHalfLayout->addLayout(fileButtonLayout); 0341 0342 m_fileWidget = new QWidget(m_rightHalfVBox); 0343 m_fileWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 0344 rightHalfLayout->addWidget(m_fileWidget); 0345 auto fileLayout = new QGridLayout(m_fileWidget); 0346 0347 m_nameLabel = new QLabel(tr("Name:"), m_fileWidget); 0348 fileLayout->addWidget(m_nameLabel, 0, 0); 0349 0350 m_nameLineEdit = new QLineEdit(m_fileWidget); 0351 connect(m_nameLineEdit, &QLineEdit::textEdited, this, 0352 &Kid3Form::nameLineEditChanged); 0353 fileLayout->addWidget(m_nameLineEdit, 0, 1, 1, 4); 0354 m_fileLabel->setBuddy(m_nameLineEdit); 0355 0356 auto formatLabel = new QLabel(tr("Format:") + QChar(0x2191), 0357 m_fileWidget); 0358 fileLayout->addWidget(formatLabel, 1, 0); 0359 0360 m_formatComboBox = new QComboBox(m_fileWidget); 0361 m_formatComboBox->setEditable(true); 0362 m_formatComboBox->setSizeAdjustPolicy( 0363 QComboBox::AdjustToMinimumContentsLengthWithIcon); 0364 m_formatComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 0365 m_formatComboBox->setToolTip(TrackDataFormatReplacer::getToolTip()); 0366 connect(m_formatComboBox, &QComboBox::editTextChanged, 0367 this, &Kid3Form::onFormatEditTextChanged); 0368 m_formatFromFilenameComboBox = new QComboBox(m_fileWidget); 0369 m_formatFromFilenameComboBox->setEditable(true); 0370 m_formatFromFilenameComboBox->setSizeAdjustPolicy( 0371 QComboBox::AdjustToMinimumContentsLengthWithIcon); 0372 m_formatFromFilenameComboBox->setSizePolicy( 0373 QSizePolicy::Expanding, QSizePolicy::Fixed); 0374 m_formatFromFilenameComboBox->setToolTip(FrameFormatReplacer::getToolTip()); 0375 connect(m_formatFromFilenameComboBox, &QComboBox::editTextChanged, 0376 this, &Kid3Form::onFormatFromFilenameEditTextChanged); 0377 0378 fileLayout->addWidget(m_formatComboBox, 1, 1); 0379 0380 setTabOrder(m_fileListBox, m_dirListBox); 0381 setTabOrder(m_dirListBox, m_nameLineEdit); 0382 setTabOrder(m_nameLineEdit, m_formatComboBox); 0383 setTabOrder(m_formatComboBox, m_formatFromFilenameComboBox); 0384 0385 QWidget* tabWidget = m_formatFromFilenameComboBox; 0386 0387 auto fromTagLabel = new QLabel(tr("From:"), m_fileWidget); 0388 fileLayout->addWidget(fromTagLabel, 1, 2); 0389 int column = 3; 0390 FOR_ALL_TAGS(tagNr) { 0391 if (tagNr <= Frame::Tag_2) { 0392 QString tagStr = Frame::tagNumberToString(tagNr); 0393 m_fnButton[tagNr] = new QPushButton(tr("Tag %1").arg(tagStr), 0394 m_fileWidget); 0395 m_fnButton[tagNr]->setToolTip(tr("Filename from Tag %1").arg(tagStr)); 0396 connect(m_fnButton[tagNr], &QAbstractButton::clicked, 0397 m_app->tag(tagNr), &Kid3ApplicationTagContext::getFilenameFromTags); 0398 fileLayout->addWidget(m_fnButton[tagNr], 1, column++); 0399 setTabOrder(tabWidget, m_fnButton[tagNr]); 0400 tabWidget = m_fnButton[tagNr]; 0401 } else { 0402 m_fnButton[tagNr] = nullptr; 0403 } 0404 } 0405 0406 auto formatFromFilenameLabel = new QLabel(tr("Format:") + QChar(0x2193), 0407 m_fileWidget); 0408 fileLayout->addWidget(formatFromFilenameLabel, 2, 0); 0409 0410 fileLayout->addWidget(m_formatFromFilenameComboBox, 2, 1); 0411 0412 auto toTagLabel = new QLabel(tr("To:"), m_fileWidget); 0413 fileLayout->addWidget(toTagLabel, 2, 2); 0414 column = 3; 0415 FOR_ALL_TAGS(tagNr) { 0416 if (tagNr <= Frame::Tag_2) { 0417 QString tagStr = Frame::tagNumberToString(tagNr); 0418 m_toTagButton[tagNr] = 0419 new QPushButton(tr("Tag %1").arg(tagStr), m_fileWidget); 0420 m_toTagButton[tagNr]->setToolTip(tr("Tag %1 from Filename").arg(tagStr)); 0421 connect(m_toTagButton[tagNr], &QAbstractButton::clicked, 0422 m_app->tag(tagNr), &Kid3ApplicationTagContext::getTagsFromFilename); 0423 fileLayout->addWidget(m_toTagButton[tagNr], 2, column++); 0424 setTabOrder(tabWidget, m_toTagButton[tagNr]); 0425 tabWidget = m_toTagButton[tagNr]; 0426 } else { 0427 m_toTagButton[tagNr] = nullptr; 0428 } 0429 } 0430 0431 FOR_ALL_TAGS(tagNr) { 0432 m_tagButton[tagNr] = new QToolButton(m_rightHalfVBox); 0433 m_tagButton[tagNr]->setIcon(*s_collapsePixmap); 0434 m_tagButton[tagNr]->setAutoRaise(true); 0435 #ifdef Q_OS_MAC 0436 m_tagButton[tagNr]->setStyleSheet(QLatin1String("border: 0;")); 0437 #endif 0438 connect(m_tagButton[tagNr], &QAbstractButton::clicked, 0439 tag(tagNr), &Kid3FormTagContext::showHideTag); 0440 m_tagLabel[tagNr] = new QLabel( 0441 tr("Tag &%1").arg(Frame::tagNumberToString(tagNr)), m_rightHalfVBox); 0442 auto tagButtonLayout = new QHBoxLayout; 0443 tagButtonLayout->addWidget(m_tagButton[tagNr]); 0444 tagButtonLayout->addWidget(m_tagLabel[tagNr]); 0445 rightHalfLayout->addLayout(tagButtonLayout); 0446 0447 m_tagWidget[tagNr] = new QWidget(m_rightHalfVBox); 0448 if (tagNr == Frame::Tag_Id3v1) { 0449 m_tagWidget[tagNr]->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 0450 } 0451 rightHalfLayout->addWidget(m_tagWidget[tagNr], 100); 0452 0453 auto idHBoxLayout = new QHBoxLayout(m_tagWidget[tagNr]); 0454 FrameTableModel* frameModel = m_app->frameModel(tagNr); 0455 frameModel->setHeadersEmpty(true); 0456 m_frameTable[tagNr] = new FrameTable( 0457 frameModel, m_app->genreModel(tagNr), m_tagWidget[tagNr]); 0458 m_frameTable[tagNr]->setSelectionModel(m_app->getFramesSelectionModel(tagNr)); 0459 idHBoxLayout->addWidget(m_frameTable[tagNr], tagNr == Frame::Tag_Id3v1 ? 100 : 0); 0460 m_tagLabel[tagNr]->setBuddy(m_frameTable[tagNr]); 0461 0462 auto buttonsVBoxLayout = new QVBoxLayout; 0463 idHBoxLayout->addLayout(buttonsVBoxLayout); 0464 0465 if (tagNr <= Frame::Tag_2) { 0466 Frame::TagNumber otherTagNr = tagNr == Frame::Tag_1 0467 ? Frame::Tag_2 0468 : tagNr == Frame::Tag_2 ? Frame::Tag_1 : Frame::Tag_NumValues; 0469 m_id3PushButton[tagNr] = 0470 new QPushButton(tr("From Tag %1") 0471 .arg(Frame::tagNumberToString(otherTagNr)), 0472 m_tagWidget[tagNr]); 0473 connect(m_id3PushButton[tagNr], &QAbstractButton::clicked, 0474 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyToOtherTag); 0475 buttonsVBoxLayout->addWidget(m_id3PushButton[tagNr]); 0476 setTabOrder(tabWidget, m_id3PushButton[tagNr]); 0477 0478 auto copyPushButton = new QPushButton(tr("Copy"), 0479 m_tagWidget[tagNr]); 0480 connect(copyPushButton, &QAbstractButton::clicked, 0481 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyTags); 0482 buttonsVBoxLayout->addWidget(copyPushButton); 0483 setTabOrder(m_id3PushButton[tagNr], copyPushButton); 0484 0485 auto pastePushButton = 0486 new QPushButton(tr("Paste"), m_tagWidget[tagNr]); 0487 connect(pastePushButton, &QAbstractButton::clicked, 0488 m_app->tag(tagNr), &Kid3ApplicationTagContext::pasteTags); 0489 buttonsVBoxLayout->addWidget(pastePushButton); 0490 setTabOrder(copyPushButton, pastePushButton); 0491 tabWidget = pastePushButton; 0492 } else { 0493 m_id3PushButton[tagNr] = new QPushButton(tr("From")); 0494 auto menu = new QMenu(this); 0495 QAction* action = menu->addAction(tr("Filename")); 0496 connect(action, &QAction::triggered, 0497 m_app->tag(tagNr), &Kid3ApplicationTagContext::getTagsFromFilename); 0498 FOR_ALL_TAGS(fromTagNr) { 0499 if (fromTagNr != tagNr) { 0500 action = menu->addAction( 0501 tr("Tag %1").arg(Frame::tagNumberToString(fromTagNr))); 0502 QByteArray ba; 0503 ba.append(static_cast<char>(fromTagNr)); 0504 ba.append(static_cast<char>(tagNr)); 0505 action->setData(ba); 0506 connect(action, &QAction::triggered, 0507 this, &Kid3Form::copyTagsActionData); 0508 } 0509 } 0510 action = menu->addAction(tr("Paste")); 0511 connect(action, &QAction::triggered, 0512 m_app->tag(tagNr), &Kid3ApplicationTagContext::pasteTags); 0513 m_id3PushButton[tagNr]->setMenu(menu); 0514 buttonsVBoxLayout->addWidget(m_id3PushButton[tagNr]); 0515 setTabOrder(tabWidget, m_id3PushButton[tagNr]); 0516 0517 auto toButton = new QPushButton(tr("To")); 0518 menu = new QMenu(this); 0519 action = menu->addAction(tr("Filename")); 0520 connect(action, &QAction::triggered, 0521 m_app->tag(tagNr), &Kid3ApplicationTagContext::getFilenameFromTags); 0522 FOR_ALL_TAGS(fromTagNr) { 0523 if (fromTagNr != tagNr) { 0524 action = menu->addAction( 0525 tr("Tag %1").arg(Frame::tagNumberToString(fromTagNr))); 0526 QByteArray ba; 0527 ba.append(static_cast<char>(tagNr)); 0528 ba.append(static_cast<char>(fromTagNr)); 0529 action->setData(ba); 0530 connect(action, &QAction::triggered, 0531 this, &Kid3Form::copyTagsActionData); 0532 } 0533 } 0534 action = menu->addAction(tr("Copy")); 0535 connect(action, &QAction::triggered, 0536 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyTags); 0537 toButton->setMenu(menu); 0538 buttonsVBoxLayout->addWidget(toButton); 0539 setTabOrder(m_id3PushButton[tagNr], toButton); 0540 tabWidget = toButton; 0541 } 0542 0543 auto removePushButton = 0544 new QPushButton(tr("Remove"), m_tagWidget[tagNr]); 0545 connect(removePushButton, &QAbstractButton::clicked, 0546 m_app->tag(tagNr), &Kid3ApplicationTagContext::removeTags); 0547 buttonsVBoxLayout->addWidget(removePushButton); 0548 setTabOrder(tabWidget, removePushButton); 0549 tabWidget = removePushButton; 0550 0551 if (tagNr != Frame::Tag_Id3v1) { 0552 auto frameLine = new QFrame; 0553 frameLine->setFrameShape(QFrame::HLine); 0554 frameLine->setFrameShadow(QFrame::Sunken); 0555 buttonsVBoxLayout->addWidget(frameLine); 0556 0557 auto editFramesPushButton = 0558 new QPushButton(tr("Edit..."), m_tagWidget[tagNr]); 0559 connect(editFramesPushButton, &QAbstractButton::clicked, 0560 m_app->tag(tagNr), &Kid3ApplicationTagContext::editFrame); 0561 buttonsVBoxLayout->addWidget(editFramesPushButton); 0562 setTabOrder(tabWidget, editFramesPushButton); 0563 auto framesAddPushButton = 0564 new QPushButton(tr("Add..."), m_tagWidget[tagNr]); 0565 connect(framesAddPushButton, &QAbstractButton::clicked, 0566 m_app->tag(tagNr), &Kid3ApplicationTagContext::addFrame); 0567 buttonsVBoxLayout->addWidget(framesAddPushButton); 0568 setTabOrder(editFramesPushButton, framesAddPushButton); 0569 auto deleteFramesPushButton = 0570 new QPushButton(tr("Delete"), m_tagWidget[tagNr]); 0571 connect(deleteFramesPushButton, &QAbstractButton::clicked, 0572 m_app->tag(tagNr), &Kid3ApplicationTagContext::deleteFrame); 0573 buttonsVBoxLayout->addWidget(deleteFramesPushButton); 0574 setTabOrder(framesAddPushButton, deleteFramesPushButton); 0575 tabWidget = deleteFramesPushButton; 0576 } 0577 if (tagNr == Frame::Tag_Picture) { 0578 m_pictureLabel = new PictureLabel(this); 0579 m_pictureLabel->installEventFilter(new PictureDblClickHandler(m_app)); 0580 buttonsVBoxLayout->addWidget(m_pictureLabel); 0581 } 0582 0583 buttonsVBoxLayout->addItem( 0584 new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); 0585 } 0586 0587 rightHalfLayout->insertStretch(-1); 0588 0589 FOR_ALL_TAGS(tagNr) { 0590 setTabOrder(tabWidget, m_frameTable[tagNr]); 0591 tabWidget = m_frameTable[tagNr]; 0592 } 0593 0594 auto sectionActions = new SectionActions(SectionActions::Navigation, 0595 m_fileListBox); 0596 m_sectionActions.append(sectionActions); 0597 connect(sectionActions->previousSectionAction(), &QAction::triggered, 0598 this, [this] { setFocusPreviousTag(Frame::Tag_NumValues); }); 0599 connect(sectionActions->nextSectionAction(), &QAction::triggered, 0600 this, &Kid3Form::setFocusDirList); 0601 0602 sectionActions = new SectionActions(SectionActions::Navigation, m_dirListBox); 0603 m_sectionActions.append(sectionActions); 0604 connect(sectionActions->previousSectionAction(), &QAction::triggered, 0605 this, &Kid3Form::setFocusFileList); 0606 connect(sectionActions->nextSectionAction(), &QAction::triggered, 0607 this, &Kid3Form::setFocusFilename); 0608 0609 sectionActions = new SectionActions(SectionActions::Navigation | 0610 SectionActions::Transfer, 0611 m_fileWidget); 0612 m_sectionActions.append(sectionActions); 0613 connect(sectionActions->previousSectionAction(), &QAction::triggered, 0614 this, &Kid3Form::setFocusDirList); 0615 connect(sectionActions->nextSectionAction(), &QAction::triggered, 0616 this, [this] { setFocusNextTag(Frame::Tag_NumValues); }); 0617 connect(sectionActions->transferAction(), &QAction::triggered, 0618 m_app->tag(Frame::Tag_2), &Kid3ApplicationTagContext::getFilenameFromTags); 0619 0620 FOR_ALL_TAGS(tagNr) { 0621 sectionActions = new SectionActions( 0622 tagNr == Frame::Tag_1 ? SectionActions::Navigation | 0623 SectionActions::Transfer | 0624 SectionActions::EditSection 0625 : SectionActions::Navigation | 0626 SectionActions::Transfer | 0627 SectionActions::EditSection | 0628 SectionActions::EditElement, 0629 m_frameTable[tagNr]); 0630 m_sectionActions.append(sectionActions); 0631 connect(sectionActions->previousSectionAction(), &QAction::triggered, 0632 this, [this, tagNr] { 0633 setFocusPreviousTag(tagNr); 0634 }); 0635 connect(sectionActions->nextSectionAction(), &QAction::triggered, 0636 this, [this, tagNr] { 0637 setFocusNextTag(tagNr); 0638 }); 0639 0640 connect(sectionActions->copyAction(), &QAction::triggered, 0641 m_app->tag(tagNr), &Kid3ApplicationTagContext::copyTags); 0642 connect(sectionActions->pasteAction(), &QAction::triggered, 0643 m_app->tag(tagNr), &Kid3ApplicationTagContext::pasteTags); 0644 connect(sectionActions->removeAction(), &QAction::triggered, 0645 this, [this, tagNr] { 0646 m_app->tag(tagNr)->removeTags(); 0647 setFocusTag(tagNr); 0648 }); 0649 0650 QByteArray ba; 0651 ba.append(static_cast<char>(tagNr == Frame::Tag_2 ? Frame::Tag_1 0652 : Frame::Tag_2)); 0653 ba.append(static_cast<char>(tagNr)); 0654 sectionActions->transferAction()->setData(ba); 0655 connect(sectionActions->transferAction(), &QAction::triggered, 0656 this, &Kid3Form::copyTagsActionData); 0657 0658 if (tagNr != Frame::Tag_1) { 0659 connect(sectionActions->editAction(), &QAction::triggered, 0660 m_app->tag(tagNr), &Kid3ApplicationTagContext::editFrame); 0661 connect(sectionActions->addAction(), &QAction::triggered, 0662 m_app->tag(tagNr), &Kid3ApplicationTagContext::addFrame); 0663 connect(sectionActions->deleteAction(), &QAction::triggered, 0664 m_app->tag(tagNr), &Kid3ApplicationTagContext::deleteFrame); 0665 } 0666 } 0667 } 0668 0669 /** 0670 * Destructor. 0671 */ 0672 Kid3Form::~Kid3Form() 0673 { 0674 m_app->removeFrameEditor(m_mainWin); 0675 } 0676 0677 /** 0678 * Set keyboard shortcuts for section actions. 0679 * @param map map of action names to key sequences 0680 */ 0681 void Kid3Form::setSectionActionShortcuts(const QMap<QString, QKeySequence>& map) 0682 { 0683 for (SectionActions* sectionActions : m_sectionActions) { 0684 sectionActions->setShortcuts(map); 0685 } 0686 m_fileListBox->setShortcuts(map); 0687 m_dirListBox->setShortcuts(map); 0688 } 0689 0690 /** 0691 * Handle event when mouse is moved while dragging. 0692 * 0693 * @param ev drag event. 0694 */ 0695 void Kid3Form::dragMoveEvent(QDragMoveEvent* ev) 0696 { 0697 if (ev->mimeData()->hasFormat(QLatin1String("text/uri-list")) || 0698 ev->mimeData()->hasImage()) { 0699 ev->acceptProposedAction(); 0700 } else { 0701 ev->ignore(); 0702 } 0703 } 0704 0705 /** 0706 * Accept drag. 0707 * 0708 * @param ev drag event. 0709 */ 0710 void Kid3Form::dragEnterEvent(QDragEnterEvent* ev) 0711 { 0712 Kid3Form::dragMoveEvent(ev); 0713 } 0714 0715 /** 0716 * Handle event when mouse leaves widget while dragging. 0717 * 0718 * @param ev drag event. 0719 */ 0720 void Kid3Form::dragLeaveEvent(QDragLeaveEvent* ev) 0721 { 0722 ev->accept(); 0723 } 0724 0725 /** 0726 * Handle drop event. 0727 * 0728 * @param ev drop event. 0729 */ 0730 void Kid3Form::dropEvent(QDropEvent* ev) 0731 { 0732 if (ev->mimeData()->hasImage()) { 0733 auto image = qvariant_cast<QImage>(ev->mimeData()->imageData()); 0734 ev->acceptProposedAction(); 0735 if (!image.isNull()) { 0736 QByteArray ba; 0737 QBuffer buffer(&ba); 0738 buffer.open(QIODevice::WriteOnly); 0739 image.save(&buffer, "JPG"); 0740 PictureFrame frame; 0741 if (PictureFrame::setData(frame, ba)) { 0742 m_app->dropImage(&frame); 0743 } 0744 } 0745 } else if (ev->mimeData()->hasFormat(QLatin1String("text/uri-list"))) { 0746 QList<QUrl> urls = ev->mimeData()->urls(); 0747 ev->acceptProposedAction(); 0748 m_app->dropUrls(urls, ev->source() != nullptr); 0749 } else { 0750 ev->ignore(); 0751 } 0752 } 0753 0754 /** 0755 * Filename line edit is changed. 0756 * @param txt contents of line edit 0757 */ 0758 void Kid3Form::nameLineEditChanged(const QString& txt) 0759 { 0760 formatLineEdit(m_nameLineEdit, txt, &FilenameFormatConfig::instance()); 0761 } 0762 0763 /** 0764 * Mark the filename as changed. 0765 * @param en true to mark as changed 0766 */ 0767 void Kid3Form::markChangedFilename(bool en) 0768 { 0769 if (CoreTaggedFileIconProvider* colorProvider; 0770 en && 0771 (colorProvider = m_app->getPlatformTools()->iconProvider()) != nullptr) { 0772 QPalette changedPalette(m_nameLabel->palette()); 0773 changedPalette.setBrush(QPalette::Active, QPalette::Window, 0774 colorProvider->colorForContext(ColorContext::Marked) 0775 .value<QBrush>()); 0776 m_nameLabel->setPalette(changedPalette); 0777 } else { 0778 m_nameLabel->setPalette(QPalette()); 0779 } 0780 m_nameLabel->setAutoFillBackground(en); 0781 } 0782 0783 /** 0784 * Format string within line edit. 0785 * 0786 * @param le line edit 0787 * @param txt text in line edit 0788 * @param fcfg format configuration 0789 */ 0790 void Kid3Form::formatLineEdit(QLineEdit* le, const QString& txt, 0791 const FormatConfig* fcfg) 0792 { 0793 if (fcfg->formatWhileEditing()) { 0794 QString str(txt); 0795 fcfg->formatString(str); 0796 if (str != txt) { 0797 int curPos = le->cursorPosition(); 0798 le->setText(str); 0799 le->setCursorPosition(curPos + str.length() - txt.length()); 0800 } 0801 } 0802 } 0803 0804 /** 0805 * Directory list box directory selected. 0806 * 0807 * @param index selected item 0808 */ 0809 void Kid3Form::dirSelected(const QModelIndex& index) 0810 { 0811 if (QString dirPath = index.data(FileSystemModel::FilePathRole).toString(); 0812 !dirPath.isEmpty()) { 0813 m_app->setDirUpIndex( 0814 dirPath.endsWith(QLatin1String("..")) ? index.parent() : QModelIndex()); 0815 m_mainWin->updateCurrentSelection(); 0816 m_mainWin->confirmedOpenDirectory({dirPath}); 0817 } 0818 } 0819 0820 /** 0821 * File list box item activated. 0822 * 0823 * @param index selected item 0824 */ 0825 void Kid3Form::fileActivated(const QModelIndex& index) 0826 { 0827 if (auto fileProxyModel = 0828 qobject_cast<const FileProxyModel*>(index.model())) { 0829 if (fileProxyModel->isDir(index)) { 0830 if (QString dirPath = fileProxyModel->filePath(index); 0831 !dirPath.isEmpty()) { 0832 m_mainWin->updateCurrentSelection(); 0833 m_mainWin->confirmedOpenDirectory({dirPath}); 0834 } 0835 } 0836 } 0837 } 0838 0839 /** 0840 * Open the parent directory of a model index. 0841 * 0842 * @param index current root index of item view 0843 */ 0844 void Kid3Form::openParentDirectory(const QModelIndex& index) 0845 { 0846 if (index.isValid()) { 0847 if (QDir dir(index.data(FileSystemModel::FilePathRole).toString()); 0848 dir.cdUp()) { 0849 QString dirPath = dir.absolutePath(); 0850 if (m_dirListBox && index.model() == m_dirListBox->model()) { 0851 m_app->setDirUpIndex(index); 0852 } 0853 m_mainWin->updateCurrentSelection(); 0854 m_mainWin->confirmedOpenDirectory({dirPath}); 0855 } 0856 } 0857 } 0858 0859 /** 0860 * Enable or disable controls requiring tags. 0861 * @param tagNr tag number 0862 * @param enable true to enable 0863 */ 0864 void Kid3Form::enableControls(Frame::TagNumber tagNr, bool enable) 0865 { 0866 if (m_fnButton[tagNr]) { 0867 m_fnButton[tagNr]->setEnabled(enable); 0868 } 0869 if (m_toTagButton[tagNr]) { 0870 m_toTagButton[tagNr]->setEnabled(enable); 0871 } 0872 if (Frame::TagNumber otherTagNr = tagNr == Frame::Tag_1 0873 ? Frame::Tag_2 0874 : tagNr == Frame::Tag_2 ? Frame::Tag_1 : Frame::Tag_NumValues; 0875 otherTagNr < Frame::Tag_NumValues) { 0876 m_id3PushButton[otherTagNr]->setEnabled(enable); 0877 } 0878 m_tagWidget[tagNr]->setEnabled(enable); 0879 if (tagNr > Frame::Tag_2) { 0880 m_tagButton[tagNr]->setVisible(enable); 0881 m_tagLabel[tagNr]->setVisible(enable); 0882 } 0883 } 0884 0885 /** 0886 * Display the tag format. 0887 * @param tagNr tag number 0888 * @param str string describing format, e.g. "ID3v1.1" 0889 */ 0890 void Kid3Form::setTagFormat(Frame::TagNumber tagNr, const QString& str) 0891 { 0892 QString txt = tr("Tag &%1").arg(Frame::tagNumberToString(tagNr)); 0893 if (!str.isEmpty()) { 0894 txt += QLatin1String(": "); 0895 txt += str; 0896 } 0897 m_tagLabel[tagNr]->setText(txt); 0898 } 0899 0900 /** 0901 * Adjust the size of the right half box. 0902 */ 0903 void Kid3Form::adjustRightHalfBoxSize() 0904 { 0905 m_rightHalfVBox->adjustSize(); 0906 } 0907 0908 /** 0909 * Hide or show file controls. 0910 * 0911 * @param hide true to hide, false to show 0912 */ 0913 void Kid3Form::hideFile(bool hide) 0914 { 0915 if (hide) { 0916 m_fileWidget->hide(); 0917 m_fileButton->setIcon(*s_expandPixmap); 0918 } else { 0919 m_fileWidget->show(); 0920 m_fileButton->setIcon(*s_collapsePixmap); 0921 } 0922 } 0923 0924 /** 0925 * Hide or show tag controls. 0926 * @param tagNr tag number 0927 * @param hide true to hide, false to show 0928 */ 0929 void Kid3Form::hideTag(Frame::TagNumber tagNr, bool hide) 0930 { 0931 if (hide) { 0932 m_tagWidget[tagNr]->hide(); 0933 m_tagButton[tagNr]->setIcon(*s_expandPixmap); 0934 } else { 0935 m_tagWidget[tagNr]->show(); 0936 m_tagButton[tagNr]->setIcon(*s_collapsePixmap); 0937 } 0938 } 0939 0940 /** 0941 * Toggle visibility of file controls. 0942 */ 0943 void Kid3Form::showHideFile() 0944 { 0945 hideFile(!m_fileWidget->isHidden()); 0946 } 0947 0948 /** 0949 * Toggle visibility of tag controls. 0950 * @param tagNr tag number 0951 */ 0952 void Kid3Form::showHideTag(Frame::TagNumber tagNr) 0953 { 0954 hideTag(tagNr, !m_tagWidget[tagNr]->isHidden()); 0955 } 0956 0957 /** 0958 * Set format text configuration when format edit text is changed. 0959 * @param text format text 0960 */ 0961 void Kid3Form::onFormatEditTextChanged(const QString& text) 0962 { 0963 FileConfig::instance().setToFilenameFormat(text); 0964 } 0965 0966 /** 0967 * Set format from filename text configuration when edit text is changed. 0968 * @param text format text 0969 */ 0970 void Kid3Form::onFormatFromFilenameEditTextChanged(const QString& text) 0971 { 0972 FileConfig::instance().setFromFilenameFormat(text); 0973 } 0974 0975 /** 0976 * Update sorting after directory is opened for the first time. 0977 * The sort order of the file list is not correct if it is not explicitly 0978 * sorted the first time. 0979 */ 0980 void Kid3Form::onFirstDirectoryOpened() 0981 { 0982 // Only call this once. 0983 disconnect(m_app, &Kid3Application::directoryOpened, 0984 this, &Kid3Form::onFirstDirectoryOpened); 0985 const GuiConfig& guiCfg = GuiConfig::instance(); 0986 m_app->getFileProxyModel()->sort(guiCfg.fileListSortColumn(), 0987 guiCfg.fileListSortOrder()); 0988 int firstFileSectionSize = 0989 m_fileListBox->initializeColumnWidthsFromContents(-1); 0990 m_fileListBox->scrollTo(m_fileListBox->currentIndex()); 0991 // The directory column often only contains "." and "..", which results 0992 // in a small size. Make it at least as wide as the corresponding 0993 // file list column. 0994 m_dirListBox->initializeColumnWidthsFromContents(firstFileSectionSize); 0995 } 0996 0997 /** 0998 * Hide or show picture. 0999 * 1000 * @param hide true to hide, false to show 1001 */ 1002 void Kid3Form::hidePicture(bool hide) 1003 { 1004 if (!m_pictureLabel) 1005 return; 1006 1007 if (hide) { 1008 m_pictureLabel->hide(); 1009 } else { 1010 m_pictureLabel->show(); 1011 } 1012 } 1013 1014 /** 1015 * Set focus on filename controls. 1016 */ 1017 void Kid3Form::setFocusFilename() 1018 { 1019 if (m_fileWidget->isHidden()) { 1020 hideFile(false); 1021 } 1022 if (isFilenameEditEnabled()) { 1023 m_nameLineEdit->setFocus(); 1024 } else { 1025 m_formatComboBox->setFocus(); 1026 } 1027 } 1028 1029 /** 1030 * Set focus on tag controls. 1031 * @param tagNr tag number 1032 */ 1033 void Kid3Form::setFocusTag(Frame::TagNumber tagNr) 1034 { 1035 if (m_tagWidget[tagNr]->isHidden()) { 1036 hideTag(tagNr, false); 1037 } 1038 m_frameTable[tagNr]->setFocus(); 1039 } 1040 1041 /** 1042 * Set focus on next tag controls. 1043 * @param tagNr current tag, Frame::Tag_NumValues if not on tag 1044 */ 1045 void Kid3Form::setFocusNextTag(Frame::TagNumber tagNr) 1046 { 1047 for (int i = tagNr == Frame::Tag_NumValues ? Frame::Tag_1 1048 : tagNr + 1; ; ++i) { 1049 if (i >= Frame::Tag_NumValues) { 1050 setFocusFileList(); 1051 break; 1052 } 1053 if (i >= Frame::Tag_1) { 1054 if (m_tagWidget[i]->isEnabled()) { 1055 setFocusTag(static_cast<Frame::TagNumber>(i)); 1056 break; 1057 } 1058 } else { 1059 break; 1060 } 1061 } 1062 } 1063 1064 /** 1065 * Set focus on previous tag controls. 1066 * @param tagNr current tag, Frame::Tag_NumValues if not on tag 1067 */ 1068 void Kid3Form::setFocusPreviousTag(Frame::TagNumber tagNr) 1069 { 1070 for (int i = tagNr - 1; ; --i) { 1071 if (i < Frame::Tag_1) { 1072 setFocusFilename(); 1073 break; 1074 } 1075 if (i < Frame::Tag_NumValues) { 1076 if (m_tagWidget[i]->isEnabled()) { 1077 setFocusTag(static_cast<Frame::TagNumber>(i)); 1078 break; 1079 } 1080 } else { 1081 break; 1082 } 1083 } 1084 } 1085 1086 /** 1087 * Set focus on file list. 1088 */ 1089 void Kid3Form::setFocusFileList() 1090 { 1091 m_fileListBox->setFocus(); 1092 } 1093 1094 /** 1095 * Set focus on directory list. 1096 */ 1097 void Kid3Form::setFocusDirList() 1098 { 1099 m_dirListBox->setFocus(); 1100 } 1101 1102 /** 1103 * Save the local settings to the configuration. 1104 */ 1105 void Kid3Form::saveConfig() 1106 { 1107 GuiConfig& guiCfg = GuiConfig::instance(); 1108 FileConfig& fileCfg = FileConfig::instance(); 1109 guiCfg.setSplitterSizes(sizes()); 1110 guiCfg.setVSplitterSizes(m_vSplitter->sizes()); 1111 fileCfg.setToFilenameFormat(m_formatComboBox->currentText()); 1112 fileCfg.setToFilenameFormats(getItemsFromComboBox(m_formatComboBox)); 1113 fileCfg.setFromFilenameFormat(m_formatFromFilenameComboBox->currentText()); 1114 fileCfg.setFromFilenameFormats(getItemsFromComboBox(m_formatFromFilenameComboBox)); 1115 if (!guiCfg.autoHideTags()) { 1116 guiCfg.setHideFile(m_fileWidget->isHidden()); 1117 FOR_ALL_TAGS(tagNr) { 1118 guiCfg.setHideTag(tagNr, m_tagWidget[tagNr]->isHidden()); 1119 } 1120 } 1121 saveFileAndDirListConfig(); 1122 } 1123 1124 /** 1125 * Read the local settings from the configuration. 1126 */ 1127 void Kid3Form::readConfig() 1128 { 1129 const GuiConfig& guiCfg = GuiConfig::instance(); 1130 const FileConfig& fileCfg = FileConfig::instance(); 1131 if (!guiCfg.splitterSizes().isEmpty()) { 1132 setSizes(guiCfg.splitterSizes()); 1133 } else { 1134 setSizes({307, 601}); 1135 } 1136 if (!guiCfg.vSplitterSizes().isEmpty()) { 1137 m_vSplitter->setSizes(guiCfg.vSplitterSizes()); 1138 } else { 1139 m_vSplitter->setSizes({451, 109}); 1140 } 1141 1142 setToFilenameFormats(); 1143 setFromFilenameFormats(); 1144 connect(&fileCfg, &FileConfig::toFilenameFormatsChanged, 1145 this, &Kid3Form::setToFilenameFormats, Qt::UniqueConnection); 1146 connect(&fileCfg, &FileConfig::fromFilenameFormatsChanged, 1147 this, &Kid3Form::setFromFilenameFormats, Qt::UniqueConnection); 1148 1149 if (!guiCfg.autoHideTags()) { 1150 hideFile(guiCfg.hideFile()); 1151 FOR_ALL_TAGS(tagNr) { 1152 hideTag(tagNr, guiCfg.hideTag(tagNr)); 1153 } 1154 } 1155 hidePicture(guiCfg.hidePicture()); 1156 readFileAndDirListConfig(); 1157 } 1158 1159 /** 1160 * Save file and directory list columns to the configuration. 1161 */ 1162 void Kid3Form::saveFileAndDirListConfig() 1163 { 1164 GuiConfig& guiCfg = GuiConfig::instance(); 1165 int column; 1166 Qt::SortOrder order; 1167 m_fileListBox->getSortByColumn(column, order); 1168 guiCfg.setFileListSortColumn(column); 1169 guiCfg.setFileListSortOrder(order); 1170 guiCfg.setFileListVisibleColumns(m_fileListBox->getVisibleColumns()); 1171 bool customColumWidthsEnabled = m_fileListBox->areCustomColumnWidthsEnabled(); 1172 guiCfg.setFileListCustomColumnWidthsEnabled(customColumWidthsEnabled); 1173 if (customColumWidthsEnabled) { 1174 guiCfg.setFileListColumnWidths(m_fileListBox->getColumnWidths()); 1175 } 1176 m_dirListBox->getSortByColumn(column, order); 1177 guiCfg.setDirListSortColumn(column); 1178 guiCfg.setDirListSortOrder(order); 1179 guiCfg.setDirListVisibleColumns(m_dirListBox->getVisibleColumns()); 1180 customColumWidthsEnabled = m_dirListBox->areCustomColumnWidthsEnabled(); 1181 guiCfg.setDirListCustomColumnWidthsEnabled(customColumWidthsEnabled); 1182 if (customColumWidthsEnabled) { 1183 guiCfg.setDirListColumnWidths(m_dirListBox->getColumnWidths()); 1184 } 1185 } 1186 1187 /** 1188 * Set file and directory list columns from the configuration. 1189 */ 1190 void Kid3Form::readFileAndDirListConfig() 1191 { 1192 const GuiConfig& guiCfg = GuiConfig::instance(); 1193 m_fileListBox->sortByColumn(guiCfg.fileListSortColumn(), 1194 guiCfg.fileListSortOrder()); 1195 m_fileListBox->setVisibleColumns(guiCfg.fileListVisibleColumns()); 1196 m_fileListBox->setColumnWidths(guiCfg.fileListColumnWidths()); 1197 m_fileListBox->setCustomColumnWidthsEnabled( 1198 guiCfg.fileListCustomColumnWidthsEnabled()); 1199 m_dirListBox->sortByColumn(guiCfg.dirListSortColumn(), 1200 guiCfg.dirListSortOrder()); 1201 m_dirListBox->setVisibleColumns(guiCfg.dirListVisibleColumns()); 1202 m_dirListBox->setColumnWidths(guiCfg.dirListColumnWidths()); 1203 m_dirListBox->setCustomColumnWidthsEnabled( 1204 guiCfg.dirListCustomColumnWidthsEnabled()); 1205 } 1206 1207 /** 1208 * Set items of "Format <arrow up>" combo box from file configuration. 1209 */ 1210 void Kid3Form::setToFilenameFormats() 1211 { 1212 const FileConfig& fileCfg = FileConfig::instance(); 1213 setItemsInComboBox(fileCfg.toFilenameFormats(), fileCfg.toFilenameFormat(), 1214 m_formatComboBox); 1215 } 1216 1217 /** 1218 * Set items of "Format <arrow down>" combo box from file configuration. 1219 */ 1220 void Kid3Form::setFromFilenameFormats() 1221 { 1222 const FileConfig& fileCfg = FileConfig::instance(); 1223 setItemsInComboBox(fileCfg.fromFilenameFormats(), fileCfg.fromFilenameFormat(), 1224 m_formatFromFilenameComboBox); 1225 } 1226 1227 /** 1228 * Set preview picture data. 1229 * @param data picture data, empty if no picture is available 1230 */ 1231 void Kid3Form::setPictureData(const QByteArray& data) 1232 { 1233 if (m_pictureLabel) { 1234 m_pictureLabel->setData(data); 1235 } 1236 } 1237 1238 /** 1239 * Set details info text. 1240 * 1241 * @param str detail information summary as string 1242 */ 1243 void Kid3Form::setDetailInfo(const QString& str) 1244 { 1245 m_fileLabel->setText(!str.isEmpty() 1246 ? tr("F&ile") + QLatin1String(": ") + str 1247 : tr("F&ile")); 1248 } 1249 1250 /** 1251 * Select all files. 1252 */ 1253 void Kid3Form::selectAllFiles() 1254 { 1255 m_fileListBox->selectAll(); 1256 } 1257 1258 /** 1259 * Deselect all files. 1260 */ 1261 void Kid3Form::deselectAllFiles() 1262 { 1263 m_fileListBox->clearSelection(); 1264 } 1265 1266 /** 1267 * Set the next file as the current file. 1268 * 1269 * @param select true to select the file 1270 * @param onlyTaggedFiles only consider tagged files 1271 * 1272 * @return true if a next file exists. 1273 */ 1274 bool Kid3Form::nextFile(bool select, bool onlyTaggedFiles) 1275 { 1276 FrameTable* editingFrameTable = getEditingFrameTable(); 1277 bool ok = m_app->nextFile(select, onlyTaggedFiles); 1278 if (ok && editingFrameTable) { 1279 editingFrameTable->edit(editingFrameTable->currentIndex()); 1280 } 1281 return ok; 1282 } 1283 1284 /** 1285 * Set the previous file as the current file. 1286 * 1287 * @param select true to select the file 1288 * @param onlyTaggedFiles only consider tagged files 1289 * 1290 * @return true if a previous file exists. 1291 */ 1292 bool Kid3Form::previousFile(bool select, bool onlyTaggedFiles) 1293 { 1294 FrameTable* editingFrameTable = getEditingFrameTable(); 1295 bool ok = m_app->previousFile(select, onlyTaggedFiles); 1296 if (ok && editingFrameTable) { 1297 editingFrameTable->edit(editingFrameTable->currentIndex()); 1298 } 1299 return ok; 1300 } 1301 1302 /** 1303 * Select the next tagged file as the current file. 1304 * Same as nextFile() with default arguments, provided for functor-based 1305 * connections. 1306 * @return true if a next file exists. 1307 */ 1308 bool Kid3Form::selectNextTaggedFile() 1309 { 1310 return nextFile(true, true); 1311 } 1312 1313 /** 1314 * Select the previous tagged file as the current file. 1315 * Same as previousFile() with default arguments, provided for functor-based 1316 * connections. 1317 * @return true if a previous file exists. 1318 */ 1319 bool Kid3Form::selectPreviousTaggedFile() 1320 { 1321 return previousFile(true, true); 1322 } 1323 1324 /** 1325 * Get frame table which is currently in editing state. 1326 * The returned frame table can be used to restore the editing state after 1327 * changing the current file. 1328 * @return frame table which is in editing state, 0 if none. 1329 */ 1330 FrameTable* Kid3Form::getEditingFrameTable() const 1331 { 1332 if (QWidget* focusWidget = QApplication::focusWidget()) { 1333 FOR_ALL_TAGS(tagNr) { 1334 if (m_frameTable[tagNr]->getCurrentEditor() == focusWidget) { 1335 return m_frameTable[tagNr]; 1336 } 1337 } 1338 } 1339 return nullptr; 1340 } 1341 1342 /** 1343 * Set the root index of the file list. 1344 * 1345 * @param index root index of directory in file proxy model 1346 */ 1347 void Kid3Form::setFileRootIndex(const QModelIndex& index) 1348 { 1349 if (index.isValid()) { 1350 m_fileListBox->setRootIndex(index); 1351 m_fileListBox->scrollTo(m_fileListBox->currentIndex()); 1352 } 1353 } 1354 1355 /** 1356 * Set the root index of the directory list. 1357 * 1358 * @param index root index of directory in directory model 1359 */ 1360 void Kid3Form::setDirRootIndex(const QModelIndex& index) 1361 { 1362 if (index.isValid()) { 1363 m_dirListBox->setRootIndex(index); 1364 } 1365 } 1366 1367 /** 1368 * Set a widget to be displayed at the left side instead of the file lists. 1369 * @param widget widget to be shown at the left side 1370 */ 1371 void Kid3Form::setLeftSideWidget(QWidget* widget) 1372 { 1373 int idx = m_leftSideWidget->addWidget(widget); 1374 m_leftSideWidget->setCurrentIndex(idx); 1375 } 1376 1377 /** 1378 * Remove widget set with setLeftSideWidget(). 1379 * 1380 * The widget will not be deleted. 1381 * 1382 * @param widget widget to be removed 1383 */ 1384 void Kid3Form::removeLeftSideWidget(QWidget* widget) 1385 { 1386 m_leftSideWidget->removeWidget(widget); 1387 } 1388 1389 /** 1390 * Copy tags using QAction::data(). 1391 * The source and destination tag numbers are taken from the first two bytes 1392 * in QAction::data().toByteArray() if the sender() is a QAction. 1393 */ 1394 void Kid3Form::copyTagsActionData() 1395 { 1396 if (auto action = qobject_cast<QAction*>(sender())) { 1397 if (QByteArray ba = action->data().toByteArray(); ba.size() == 2) { 1398 Frame::TagNumber srcTagNr = Frame::tagNumberCast(ba.at(0)); 1399 Frame::TagNumber dstTagNr = Frame::tagNumberCast(ba.at(1)); 1400 if (srcTagNr != Frame::Tag_NumValues && 1401 dstTagNr != Frame::Tag_NumValues) { 1402 m_app->copyTag(srcTagNr, dstTagNr); 1403 } 1404 } 1405 } 1406 }