1 /***************************************************************************
2 * Copyright (C) 2008-2021 by Ilya Kotov *
3 * forkotov02@ya.ru *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 #include <QDialog>
22 #include <QMenu>
23 #include <QWidget>
24 #include <QAction>
25 #include <QSettings>
26 #include <QApplication>
27 #include <QMessageBox>
28 #include <QFileInfo>
29 #include <algorithm>
30 #include <qmmp/soundcore.h>
31 #include <qmmp/metadatamanager.h>
32 #include "filedialog.h"
33 #include "playlistparser.h"
34 #include "playlistmanager.h"
35 #include "qmmpuisettings.h"
36 #include "general.h"
37 #include "generalfactory.h"
38 #include "jumptotrackdialog_p.h"
39 #include "aboutdialog_p.h"
40 #include "addurldialog_p.h"
41 #include "mediaplayer.h"
42 #include "uihelper.h"
43
44 UiHelper *UiHelper::m_instance = nullptr;
45
UiHelper(QObject * parent)46 UiHelper::UiHelper(QObject *parent)
47 : QObject(parent)
48 {
49 m_instance = this;
50 General::create(parent);
51 QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
52 m_lastDir = settings.value("General/last_dir", QDir::homePath()).toString(); //last directory
53 }
54
~UiHelper()55 UiHelper::~UiHelper()
56 {
57 QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
58 settings.setValue("General/last_dir",m_lastDir);
59 }
60
visibilityControl()61 bool UiHelper::visibilityControl()
62 {
63 const QList<GeneralFactory *> factories = General::enabledFactories();
64 return std::any_of(factories.cbegin(), factories.cend(),
65 [](GeneralFactory *factory){ return factory->properties().visibilityControl; });
66 }
67
addAction(QAction * action,MenuType type)68 void UiHelper::addAction(QAction *action, MenuType type)
69 {
70 connect(action, SIGNAL(destroyed (QObject *)), SLOT(removeAction(QObject*)));
71
72 if(!m_menus[type].actions.contains(action))
73 {
74 m_menus[type].actions.append(action);
75 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
76 action->setShortcutVisibleInContextMenu(true);
77 #endif
78 }
79 if(m_menus[type].menu && !m_menus[type].menu->actions().contains(action))
80 {
81 if(m_menus[type].before)
82 m_menus[type].menu->insertAction(m_menus[type].before, action);
83 else
84 m_menus[type].menu->addAction(action);
85 m_menus[type].menu->menuAction()->setVisible(!m_menus[type].autoHide || !m_menus[type].actions.isEmpty());
86 }
87 }
88
removeAction(QAction * action)89 void UiHelper::removeAction(QAction *action)
90 {
91 for(MenuType type : m_menus.keys())
92 {
93 m_menus[type].actions.removeAll(action);
94 if(m_menus[type].menu)
95 {
96 m_menus[type].menu->removeAction(action);
97 m_menus[type].menu->menuAction()->setVisible(!m_menus[type].autoHide || !m_menus[type].actions.isEmpty());
98 }
99 }
100 }
101
actions(MenuType type)102 QList<QAction *> UiHelper::actions(MenuType type)
103 {
104 return m_menus[type].actions;
105 }
106
createMenu(MenuType type,const QString & title,bool autoHide,QWidget * parent)107 QMenu *UiHelper::createMenu(MenuType type, const QString &title, bool autoHide, QWidget *parent)
108 {
109 if(m_menus[type].menu)
110 {
111 m_menus[type].menu->setTitle(title);
112 }
113 else
114 {
115 m_menus[type].menu = new QMenu(title, parent);
116 m_menus[type].menu->addActions(m_menus[type].actions);
117 }
118 m_menus[type].autoHide = autoHide;
119 m_menus[type].menu->menuAction()->setVisible(!autoHide || !m_menus[type].actions.isEmpty());
120 return m_menus[type].menu;
121 }
122
registerMenu(UiHelper::MenuType type,QMenu * menu,bool autoHide,QAction * before)123 void UiHelper::registerMenu(UiHelper::MenuType type, QMenu *menu, bool autoHide, QAction *before)
124 {
125 m_menus[type].menu = menu;
126 m_menus[type].before = before;
127 m_menus[type].autoHide = autoHide;
128 if(before)
129 m_menus[type].menu->insertActions(before, m_menus[type].actions);
130 else
131 m_menus[type].menu->addActions(m_menus[type].actions);
132 m_menus[type].menu->menuAction()->setVisible(!autoHide || !m_menus[type].actions.isEmpty());
133 }
134
addFiles(QWidget * parent,PlayListModel * model)135 void UiHelper::addFiles(QWidget *parent, PlayListModel *model)
136 {
137 QStringList filters;
138 filters << tr("All Supported Bitstreams")+" (" +
139 MetaDataManager::instance()->nameFilters().join (" ") +")";
140 filters << MetaDataManager::instance()->filters();
141 m_model = model;
142 FileDialog::popup(parent, FileDialog::PlayDirsFiles, &m_lastDir,
143 this, SLOT(addSelectedFiles(QStringList,bool)),
144 tr("Select one or more files to open"), filters.join(";;"));
145 }
146
playFiles(QWidget * parent,PlayListModel * model)147 void UiHelper::playFiles(QWidget *parent, PlayListModel *model)
148 {
149 QStringList filters;
150 filters << tr("All Supported Bitstreams")+" (" +
151 MetaDataManager::instance()->nameFilters().join (" ") +")";
152 filters << MetaDataManager::instance()->filters();
153 m_model = model;
154 FileDialog::popup(parent, FileDialog::AddDirsFiles, &m_lastDir,
155 this, SLOT(playSelectedFiles(QStringList)),
156 tr("Select one or more files to play"), filters.join(";;"));
157
158 }
159
addDirectory(QWidget * parent,PlayListModel * model)160 void UiHelper::addDirectory(QWidget *parent, PlayListModel *model)
161 {
162 FileDialog::popup(parent, FileDialog::AddDirs, &m_lastDir,
163 model, SLOT(add(QStringList)),
164 tr("Choose a directory"));
165 }
166
addUrl(QWidget * parent,PlayListModel * model)167 void UiHelper::addUrl(QWidget *parent, PlayListModel *model)
168 {
169 AddUrlDialog::popup(parent, model);
170 }
171
loadPlayList(QWidget * parent,PlayListModel * model)172 void UiHelper::loadPlayList(QWidget *parent, PlayListModel *model)
173 {
174 if(PlayListParser::nameFilters().isEmpty())
175 {
176 qWarning("UiHelper: There is no registered playlist parsers");
177 return;
178 }
179
180 QString mask = tr("Playlist Files") + " (" + PlayListParser::nameFilters().join(" ") + ")";
181 //TODO use nonmodal dialog and multiplier playlists
182 QString f_path = FileDialog::getOpenFileName(parent, tr("Open Playlist"), m_lastDir, mask);
183 if (!f_path.isEmpty())
184 {
185 if(QmmpUiSettings::instance()->clearPreviousPlayList())
186 {
187 model->clear();
188 model->setName(QFileInfo(f_path).baseName());
189 }
190 model->loadPlaylist(f_path);
191 m_lastDir = QFileInfo(f_path).absoluteDir().path();
192 }
193 }
194
savePlayList(QWidget * parent,PlayListModel * model)195 void UiHelper::savePlayList(QWidget *parent, PlayListModel *model)
196 {
197 QStringList nameFilters = PlayListParser::nameFilters();
198
199 if(nameFilters.isEmpty())
200 {
201 qWarning("UiHelper: There is no registered playlist parsers");
202 return;
203 }
204
205 QStringList filters;
206 filters << tr("Playlist Files") + " (" + nameFilters.join(" ") + ")";
207 filters << PlayListParser::filters();
208 QString selectedFilter = filters.at(1);
209 QString f_name = FileDialog::getSaveFileName(parent, tr("Save Playlist"), m_lastDir + "/" +
210 model->name(), filters.join(";;"), &selectedFilter);
211
212 if(f_name.isEmpty())
213 return;
214
215 if(!PlayListParser::isPlayList(f_name)) //append selected extension
216 {
217 QStringList selectedFilters = selectedFilter.section("(", 1).remove(")").split(" ");
218 if(selectedFilters.isEmpty())
219 return;
220
221 QString ext = selectedFilters.first().remove("*"); //use first extension
222 f_name.append(ext);
223
224 QFileInfo info(f_name);
225
226 if(info.exists())
227 {
228 if (QMessageBox::question(parent, tr("Save Playlist"), tr("%1 already exists.\nDo you want to replace it?")
229 .arg(info.fileName()), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok)
230 {
231 return;
232 }
233 }
234 }
235
236 if (!f_name.isEmpty())
237 {
238 model->savePlaylist(f_name);
239 m_lastDir = QFileInfo(f_name).absoluteDir().path();
240 }
241 }
242
jumpToTrack(QWidget * parent,PlayListModel * model)243 void UiHelper::jumpToTrack(QWidget *parent, PlayListModel *model)
244 {
245 if(!m_jumpDialog)
246 {
247 m_jumpDialog = new JumpToTrackDialog(model, parent);
248 }
249 if(m_jumpDialog->isHidden())
250 {
251 m_jumpDialog->show();
252 m_jumpDialog->refresh();
253 }
254 m_jumpDialog->raise();
255 }
256
about(QWidget * parent)257 void UiHelper::about(QWidget *parent)
258 {
259 AboutDialog *dialog = new AboutDialog(parent);
260 dialog->exec();
261 dialog->deleteLater();
262 }
263
toggleVisibility()264 void UiHelper::toggleVisibility()
265 {
266 emit toggleVisibilityCalled();
267 }
268
showMainWindow()269 void UiHelper::showMainWindow()
270 {
271 emit showMainWindowCalled();
272 }
273
exit()274 void UiHelper::exit()
275 {
276 //send non-spontaneous close event
277 //for all windows
278 for(QWidget *widget : qApp->topLevelWidgets())
279 widget->close();
280
281 qApp->closeAllWindows();
282 qApp->quit();
283 }
284
instance()285 UiHelper* UiHelper::instance()
286 {
287 return m_instance;
288 }
289
removeAction(QObject * action)290 void UiHelper::removeAction(QObject *action)
291 {
292 for(MenuType type : m_menus.keys())
293 {
294 for(QList<QAction *>::iterator it = m_menus[type].actions.begin(); it != m_menus[type].actions.end(); ++it)
295 {
296 if(*it == action)
297 {
298 m_menus[type].actions.erase(it);
299 m_menus[type].menu->menuAction()->setVisible(!m_menus[type].autoHide || !m_menus[type].actions.isEmpty());
300 break;
301 }
302 }
303 }
304 }
305
addSelectedFiles(const QStringList & files,bool play)306 void UiHelper::addSelectedFiles(const QStringList &files, bool play)
307 {
308 if(files.isEmpty() || !PlayListManager::instance()->playLists().contains(m_model))
309 return;
310 if(play)
311 playSelectedFiles(files);
312 else
313 m_model->add(files);
314 }
315
playSelectedFiles(const QStringList & files)316 void UiHelper::playSelectedFiles(const QStringList &files)
317 {
318 if(files.isEmpty() || !PlayListManager::instance()->playLists().contains(m_model))
319 return;
320 m_model->clear();
321 PlayListManager::instance()->activatePlayList(m_model);
322 connect(m_model, SIGNAL(trackAdded(PlayListTrack*)), MediaPlayer::instance(), SLOT(play()));
323 connect(m_model, SIGNAL(trackAdded(PlayListTrack*)), SLOT(disconnectPl()));
324 m_model->add(files);
325 }
326
disconnectPl()327 void UiHelper::disconnectPl()
328 {
329 PlayListModel *model = qobject_cast<PlayListModel*>(sender());
330 if(model)
331 {
332 disconnect(model, SIGNAL(trackAdded(PlayListTrack*)), MediaPlayer::instance(), SLOT(play()));
333 disconnect(model, SIGNAL(trackAdded(PlayListTrack*)), this, SLOT(disconnectPl()));
334 }
335 }
336