1 /**
2  * \file playlistdialog.cpp
3  * Create playlist dialog.
4  *
5  * \b Project: Kid3
6  * \author Urs Fleisch
7  * \date 13 Sep 2009
8  *
9  * Copyright (C) 2009-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 "playlistdialog.h"
28 #include <QLayout>
29 #include <QPushButton>
30 #include <QLabel>
31 #include <QString>
32 #include <QCheckBox>
33 #include <QComboBox>
34 #include <QLineEdit>
35 #include <QRadioButton>
36 #include <QButtonGroup>
37 #include <QFrame>
38 #include <QToolTip>
39 #include <QVBoxLayout>
40 #include <QHBoxLayout>
41 #include <QGroupBox>
42 #include "playlistconfig.h"
43 #include "contexthelp.h"
44 #include "trackdata.h"
45 
46 /**
47  * Constructor.
48  *
49  * @param parent  parent widget
50  */
PlaylistDialog(QWidget * parent)51 PlaylistDialog::PlaylistDialog(QWidget* parent)
52   : QDialog(parent)
53 {
54   setObjectName(QLatin1String("PlaylistDialog"));
55   setModal(true);
56   setWindowTitle(tr("Create Playlist"));
57   setSizeGripEnabled(true);
58 
59   auto vlayout = new QVBoxLayout(this);
60   auto fnGroupBox = new QGroupBox(this);
61   auto fnGroupBoxLayout = new QVBoxLayout(fnGroupBox);
62   m_sameAsDirNameButton = new QRadioButton(this);
63   fnGroupBoxLayout->addWidget(m_sameAsDirNameButton);
64   auto fileNameFormatLayout = new QHBoxLayout;
65   m_fileNameFormatButton = new QRadioButton(this);
66   m_fileNameFormatComboBox = new QComboBox(this);
67   m_fileNameFormatComboBox->setToolTip(TrackDataFormatReplacer::getToolTip());
68   fileNameFormatLayout->addWidget(m_fileNameFormatButton);
69   fileNameFormatLayout->addWidget(m_fileNameFormatComboBox);
70   fnGroupBoxLayout->addLayout(fileNameFormatLayout);
71 
72   auto fileNameForEmptyLayout = new QHBoxLayout;
73   m_fileNameForEmptyButton = new QRadioButton(this);
74   m_fileNameForEmptyEdit = new QLineEdit(this);
75   fileNameForEmptyLayout->addWidget(m_fileNameForEmptyButton);
76   fileNameForEmptyLayout->addWidget(m_fileNameForEmptyEdit);
77   fnGroupBoxLayout->addLayout(fileNameForEmptyLayout);
78 
79   auto locationLayout = new QHBoxLayout;
80   QLabel* locationLabel = new QLabel(this);
81   m_locationComboBox = new QComboBox(this);
82   locationLayout->addWidget(locationLabel);
83   locationLayout->addWidget(m_locationComboBox);
84   fnGroupBoxLayout->addLayout(locationLayout);
85   vlayout->addWidget(fnGroupBox);
86 
87   auto pcGroupBox = new QGroupBox(this);
88   auto pcGroupBoxLayout = new QVBoxLayout(pcGroupBox);
89   auto formatLayout = new QHBoxLayout;
90   QLabel* formatLabel = new QLabel(this);
91   m_formatComboBox = new QComboBox(this);
92   formatLayout->addWidget(formatLabel);
93   formatLayout->addWidget(m_formatComboBox);
94   pcGroupBoxLayout->addLayout(formatLayout);
95   m_onlySelectedFilesCheckBox = new QCheckBox(this);
96   pcGroupBoxLayout->addWidget(m_onlySelectedFilesCheckBox);
97 
98   QFrame* sortLine = new QFrame(pcGroupBox);
99   sortLine->setFrameShape(QFrame::HLine);
100   sortLine->setFrameShadow(QFrame::Sunken);
101   pcGroupBoxLayout->addWidget(sortLine);
102   auto sortButtonGroup = new QButtonGroup(pcGroupBox);
103   m_sortFileNameButton = new QRadioButton(this);
104   pcGroupBoxLayout->addWidget(m_sortFileNameButton);
105   auto sortTagFieldLayout = new QHBoxLayout;
106   m_sortTagFieldButton = new QRadioButton(this);
107   m_sortTagFieldComboBox = new QComboBox(this);
108   m_sortTagFieldComboBox->setToolTip(TrackDataFormatReplacer::getToolTip());
109   sortTagFieldLayout->addWidget(m_sortTagFieldButton);
110   sortTagFieldLayout->addWidget(m_sortTagFieldComboBox);
111   pcGroupBoxLayout->addLayout(sortTagFieldLayout);
112   sortButtonGroup->addButton(m_sortFileNameButton);
113   sortButtonGroup->addButton(m_sortTagFieldButton);
114 
115   QFrame* pathLine = new QFrame(pcGroupBox);
116   pathLine->setFrameShape(QFrame::HLine);
117   pathLine->setFrameShadow(QFrame::Sunken);
118   pcGroupBoxLayout->addWidget(pathLine);
119   auto pathButtonGroup = new QButtonGroup(pcGroupBox);
120   m_relPathButton = new QRadioButton(this);
121   pcGroupBoxLayout->addWidget(m_relPathButton);
122   m_fullPathButton = new QRadioButton(this);
123   pcGroupBoxLayout->addWidget(m_fullPathButton);
124   pathButtonGroup->addButton(m_relPathButton);
125   pathButtonGroup->addButton(m_fullPathButton);
126 
127   QFrame* writeLine = new QFrame(pcGroupBox);
128   writeLine->setFrameShape(QFrame::HLine);
129   writeLine->setFrameShadow(QFrame::Sunken);
130   pcGroupBoxLayout->addWidget(writeLine);
131   auto writeButtonGroup = new QButtonGroup(pcGroupBox);
132   m_writeListButton = new QRadioButton(this);
133   pcGroupBoxLayout->addWidget(m_writeListButton);
134   auto writeInfoLayout = new QHBoxLayout;
135   m_writeInfoButton = new QRadioButton(this);
136   m_writeInfoComboBox = new QComboBox(this);
137   m_writeInfoComboBox->setToolTip(TrackDataFormatReplacer::getToolTip());
138   writeInfoLayout->addWidget(m_writeInfoButton);
139   writeInfoLayout->addWidget(m_writeInfoComboBox);
140   pcGroupBoxLayout->addLayout(writeInfoLayout);
141   writeButtonGroup->addButton(m_writeListButton);
142   writeButtonGroup->addButton(m_writeInfoButton);
143   vlayout->addWidget(pcGroupBox);
144   fnGroupBox->setTitle(tr("Playlist File Name"));
145   m_sameAsDirNameButton->setText(tr("Same as &folder name"));
146   m_sameAsDirNameButton->setChecked(true);
147   m_fileNameFormatButton->setText(tr("&Format:"));
148   m_fileNameFormatComboBox->setEditable(true);
149   m_fileNameFormatComboBox->setEnabled(false);
150   m_fileNameFormatComboBox->addItems({
151     QLatin1String("%{artist} - %{album}"),
152     QLatin1String(R"(%{artist} - %{"["year"] "}%{album})"),
153     QLatin1String("%{album}"),
154     QLatin1String("playlist_%{artist}_-_%{album}"),
155     QLatin1String("playlist")
156   });
157   connect(m_fileNameFormatButton, &QAbstractButton::toggled,
158           m_fileNameFormatComboBox, &QWidget::setEnabled);
159   m_fileNameForEmptyButton->setText(tr("Create ne&w empty playlist:"));
160   m_fileNameForEmptyEdit->setText(tr("New"));
161   m_fileNameForEmptyEdit->setEnabled(false);
162   // Position line edit aligned with combo box.
163   m_fileNameForEmptyEdit->setSizePolicy(m_fileNameFormatComboBox->sizePolicy());
164   connect(m_fileNameForEmptyButton, &QAbstractButton::toggled,
165           m_fileNameForEmptyEdit, &QWidget::setEnabled);
166   locationLabel->setText(tr("Cr&eate in:"));
167   locationLabel->setBuddy(m_locationComboBox);
168   m_locationComboBox->addItems({
169     tr("Current folder"),
170     tr("Every folder"),
171     tr("Top-level folder")
172   });
173   pcGroupBox->setTitle(tr("Playlist Content"));
174   formatLabel->setText(tr("For&mat:"));
175   formatLabel->setBuddy(m_formatComboBox);
176   m_formatComboBox->addItems(
177     {QLatin1String("M3U"), QLatin1String("PLS"), QLatin1String("XSPF")});
178   m_onlySelectedFilesCheckBox->setText(tr("Incl&ude only the selected files"));
179   m_sortFileNameButton->setText(tr("Sort by file &name"));
180   m_sortFileNameButton->setChecked(true);
181   m_sortTagFieldButton->setText(tr("Sort by &tag field"));
182   m_sortTagFieldComboBox->setEditable(true);
183   m_sortTagFieldComboBox->setEnabled(false);
184   QStringList lst;
185   lst.reserve(Frame::FT_LastFrame - Frame::FT_FirstFrame + 1);
186   for (int type = Frame::FT_FirstFrame; type <= Frame::FT_LastFrame; ++type) {
187     QString frameName =
188         Frame::ExtendedType(static_cast<Frame::Type>(type), QLatin1String(""))
189         .getName().toLower();
190     if (frameName == QLatin1String("track number")) frameName = QLatin1String("track.3");
191     lst.append(QLatin1String("%{") + frameName + QLatin1String("}"));
192   }
193   m_sortTagFieldComboBox->addItems(lst);
194   connect(m_sortTagFieldButton, &QAbstractButton::toggled,
195           m_sortTagFieldComboBox, &QWidget::setEnabled);
196   m_relPathButton->setText(tr("Use &relative path for files in playlist"));
197   m_relPathButton->setChecked(true);
198   m_fullPathButton->setText(tr("Use full p&ath for files in playlist"));
199   m_writeListButton->setText(tr("Write only &list of files"));
200   m_writeListButton->setChecked(true);
201   m_writeInfoButton->setText(tr("Write &info using"));
202   m_writeInfoComboBox->setEditable(true);
203   m_writeInfoComboBox->setEnabled(false);
204   m_writeInfoComboBox->addItems({
205     QLatin1String("%{artist} - %{title}"), QLatin1String("%{title}"),
206     QLatin1String("%{track.1}/%{tracks} - %{artist} - %{album} - %{title}")
207   });
208   connect(m_writeInfoButton, &QAbstractButton::toggled,
209           m_writeInfoComboBox, &QWidget::setEnabled);
210 
211   auto hlayout = new QHBoxLayout;
212   QPushButton* helpButton = new QPushButton(tr("&Help"), this);
213   helpButton->setAutoDefault(false);
214   hlayout->addWidget(helpButton);
215   connect(helpButton, &QAbstractButton::clicked, this, &PlaylistDialog::showHelp);
216   QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this);
217   saveButton->setAutoDefault(false);
218   hlayout->addWidget(saveButton);
219   connect(saveButton, &QAbstractButton::clicked, this, &PlaylistDialog::saveConfig);
220   auto hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding,
221                                          QSizePolicy::Minimum);
222   hlayout->addItem(hspacer);
223 
224   QPushButton* okButton = new QPushButton(tr("&OK"), this);
225   hlayout->addWidget(okButton);
226   connect(okButton, &QAbstractButton::clicked, this, &QDialog::accept);
227   QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this);
228   hlayout->addWidget(cancelButton);
229   connect(cancelButton, &QAbstractButton::clicked, this, &QDialog::reject);
230   vlayout->addLayout(hlayout);
231 }
232 
233 /**
234  * Read the local settings from the configuration.
235  */
readConfig()236 void PlaylistDialog::readConfig()
237 {
238   const PlaylistConfig& playlistCfg = PlaylistConfig::instance();
239   m_fileNameFormatButton->setChecked(
240     playlistCfg.useFileNameFormat());
241   m_sameAsDirNameButton->setChecked(
242     !playlistCfg.useFileNameFormat());
243   m_onlySelectedFilesCheckBox->setChecked(
244     playlistCfg.onlySelectedFiles());
245   m_sortTagFieldButton->setChecked(playlistCfg.useSortTagField());
246   m_sortFileNameButton->setChecked(!playlistCfg.useSortTagField());
247   m_fullPathButton->setChecked(playlistCfg.useFullPath());
248   m_relPathButton->setChecked(!playlistCfg.useFullPath());
249   m_writeInfoButton->setChecked(playlistCfg.writeInfo());
250   m_writeListButton->setChecked(!playlistCfg.writeInfo());
251   m_locationComboBox->setCurrentIndex(playlistCfg.location());
252   m_formatComboBox->setCurrentIndex(playlistCfg.format());
253   m_fileNameFormatComboBox->setEditText(
254     playlistCfg.fileNameFormat());
255   m_sortTagFieldComboBox->setEditText(playlistCfg.sortTagField());
256   m_writeInfoComboBox->setEditText(playlistCfg.infoFormat());
257 
258   QByteArray geometry = playlistCfg.windowGeometry();
259   if (!geometry.isEmpty()) {
260     restoreGeometry(geometry);
261   }
262 }
263 
264 /**
265  * Get the current dialog configuration.
266  *
267  * @param cfg the current configuration is returned here
268  */
getCurrentConfig(PlaylistConfig & cfg) const269 void PlaylistDialog::getCurrentConfig(PlaylistConfig& cfg) const
270 {
271   cfg.setUseFileNameFormat(m_fileNameFormatButton->isChecked());
272   cfg.setOnlySelectedFiles(m_onlySelectedFilesCheckBox->isChecked());
273   cfg.setUseSortTagField(m_sortTagFieldButton->isChecked());
274   cfg.setUseFullPath(m_fullPathButton->isChecked());
275   cfg.setWriteInfo(m_writeInfoButton->isChecked());
276   cfg.setLocation(static_cast<PlaylistConfig::PlaylistLocation>(
277     m_locationComboBox->currentIndex()));
278   cfg.setFormat(static_cast<PlaylistConfig::PlaylistFormat>(
279     m_formatComboBox->currentIndex()));
280   cfg.setFileNameFormat(m_fileNameFormatComboBox->currentText());
281   cfg.setSortTagField(m_sortTagFieldComboBox->currentText());
282   cfg.setInfoFormat(m_writeInfoComboBox->currentText());
283   QByteArray geometry = saveGeometry();
284   cfg.setWindowGeometry(geometry);
285 }
286 
287 /**
288  * Get the entered file name to create a new empty playlist.
289  * @return file name if "Create new empty playlist" is selected, else empty.
290  */
getFileNameForNewEmptyPlaylist() const291 QString PlaylistDialog::getFileNameForNewEmptyPlaylist() const
292 {
293   return m_fileNameForEmptyButton->isChecked()
294       ? m_fileNameForEmptyEdit->text() : QString();
295 }
296 
297 /**
298  * Save the local settings to the configuration.
299  */
saveConfig() const300 void PlaylistDialog::saveConfig() const
301 {
302   getCurrentConfig(PlaylistConfig::instance());
303 }
304 
305 /**
306  * Show help.
307  */
showHelp()308 void PlaylistDialog::showHelp()
309 {
310   ContextHelp::displayHelp(QLatin1String("create-playlist"));
311 }
312