1 /*
2 * Cantata
3 *
4 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5 *
6 * ----
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "streamspage.h"
25 #include "streamdialog.h"
26 #include "streamssettings.h"
27 #include "mpd-interface/mpdconnection.h"
28 #include "support/messagebox.h"
29 #include "widgets/icons.h"
30 #include "gui/stdactions.h"
31 #include "support/actioncollection.h"
32 #include "network/networkaccessmanager.h"
33 #include "support/configuration.h"
34 #include "support/monoicon.h"
35 #include "gui/settings.h"
36 #include "widgets/menubutton.h"
37 #include "widgets/itemview.h"
38 #include "widgets/servicestatuslabel.h"
39 #include "models/digitallyimported.h"
40 #include "models/playqueuemodel.h"
41 #include "digitallyimportedsettings.h"
42 #include <QToolButton>
43 #include <QFileDialog>
44 #include <QDir>
45 #include <QMenu>
46 #include <QFileInfo>
47 #include <QUrlQuery>
48
49 static const int constMsgDisplayTime=1500;
50 static const char *constNameProperty="name";
51
StreamsPage(QWidget * p)52 StreamsPage::StreamsPage(QWidget *p)
53 : StackedPageWidget(p)
54 {
55 qRegisterMetaType<StreamItem>("StreamItem");
56 qRegisterMetaType<QList<StreamItem> >("QList<StreamItem>");
57
58 browse=new StreamsBrowsePage(this);
59 addWidget(browse);
60 connect(browse, SIGNAL(close()), this, SIGNAL(close()));
61 connect(browse, SIGNAL(searchForStreams()), this, SLOT(searchForStreams()));
62 search=new StreamSearchPage(this);
63 addWidget(search);
64 connect(search, SIGNAL(close()), this, SLOT(closeSearch()));
65
66 disconnect(browse, SIGNAL(add(const QStringList &, int, quint8, bool)), MPDConnection::self(), SLOT(add(const QStringList &, int, quint8, bool)));
67 disconnect(search, SIGNAL(add(const QStringList &, int, quint8, bool)), MPDConnection::self(), SLOT(add(const QStringList &, int, quint8, bool)));
68 connect(browse, SIGNAL(add(const QStringList &, int, quint8, bool)), PlayQueueModel::self(), SLOT(addItems(const QStringList &, int, quint8, bool)));
69 connect(search, SIGNAL(add(const QStringList &, int, quint8, bool)), PlayQueueModel::self(), SLOT(addItems(const QStringList &, int, quint8, bool)));
70 connect(StreamsModel::self()->addToFavouritesAct(), SIGNAL(triggered()), this, SLOT(addToFavourites()));
71 connect(search, SIGNAL(addToFavourites(QList<StreamItem>)), browse, SLOT(addToFavourites(QList<StreamItem>)));
72 }
73
~StreamsPage()74 StreamsPage:: ~StreamsPage()
75 {
76 }
77
searchForStreams()78 void StreamsPage::searchForStreams()
79 {
80 setCurrentWidget(search);
81 }
82
closeSearch()83 void StreamsPage::closeSearch()
84 {
85 setCurrentWidget(browse);
86 }
87
addToFavourites()88 void StreamsPage::addToFavourites()
89 {
90 QWidget *w=currentWidget();
91 if (browse==w) {
92 browse->addToFavourites();
93 } else if (search==w) {
94 search->addToFavourites();
95 }
96 }
97
StreamsBrowsePage(QWidget * p)98 StreamsBrowsePage::StreamsBrowsePage(QWidget *p)
99 : SinglePageWidget(p)
100 , settings(nullptr)
101 {
102 QColor iconCol=Utils::monoIconColor();
103 importAction = new Action(MonoIcon::icon(FontAwesome::arrowright, iconCol), tr("Import Streams Into Favorites"), this);
104 exportAction = new Action(MonoIcon::icon(FontAwesome::arrowleft, iconCol), tr("Export Favorite Streams"), this);
105 addAction = ActionCollection::get()->createAction("addstream", tr("Add New Stream To Favorites"));
106 editAction = new Action(Icons::self()->editIcon, tr("Edit"), this);
107 searchAction = new Action(Icons::self()->searchIcon, tr("Seatch For Streams"), this);
108 connect(searchAction, SIGNAL(triggered()), this, SIGNAL(searchForStreams()));
109 // connect(view, SIGNAL(itemsSelected(bool)), addToPlaylist, SLOT(setEnabled(bool)));
110 connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &)));
111 connect(view, SIGNAL(itemsSelected(bool)), SLOT(controlActions()));
112 connect(addAction, SIGNAL(triggered()), this, SLOT(addStream()));
113 connect(StreamsModel::self()->addBookmarkAct(), SIGNAL(triggered()), this, SLOT(addBookmark()));
114 connect(StreamsModel::self()->configureDiAct(), SIGNAL(triggered()), this, SLOT(configureDi()));
115 connect(StreamsModel::self()->reloadAct(), SIGNAL(triggered()), this, SLOT(reload()));
116 connect(editAction, SIGNAL(triggered()), this, SLOT(edit()));
117 connect(importAction, SIGNAL(triggered()), this, SLOT(importXml()));
118 connect(exportAction, SIGNAL(triggered()), this, SLOT(exportXml()));
119 connect(StreamsModel::self(), SIGNAL(error(const QString &)), this, SIGNAL(error(const QString &)));
120 connect(StreamsModel::self(), SIGNAL(loading()), view, SLOT(showSpinner()));
121 connect(StreamsModel::self(), SIGNAL(loaded()), view, SLOT(hideSpinner()));
122 connect(StreamsModel::self(), SIGNAL(categoriesChanged()), view, SLOT(closeSearch()));
123 connect(StreamsModel::self(), SIGNAL(favouritesLoaded()), SLOT(expandFavourites()));
124 connect(StreamsModel::self(), SIGNAL(addedToFavourites(QString)), SLOT(addedToFavourites(QString)));
125 connect(DigitallyImported::self(), SIGNAL(loginStatus(bool,QString)), SLOT(updateDiStatus()));
126 connect(DigitallyImported::self(), SIGNAL(updated()), SLOT(updateDiStatus()));
127 connect(view, SIGNAL(headerClicked(int)), SLOT(headerClicked(int)));
128 StreamsModel::self()->configureDiAct()->setEnabled(false);
129
130 proxy.setSourceModel(StreamsModel::self());
131 view->setModel(&proxy);
132 view->setDeleteAction(StdActions::self()->removeAction);
133 view->setSearchResetLevel(1);
134 view->alwaysShowHeader();
135
136 Configuration config(metaObject()->className());
137 view->setMode(ItemView::Mode_DetailedTree);
138 view->load(config);
139
140 MenuButton *menuButton=new MenuButton(this);
141 Action *configureAction=new Action(Icons::self()->configureIcon, tr("Configure"), this);
142 connect(configureAction, SIGNAL(triggered()), SLOT(configure()));
143 menuButton->addAction(createViewMenu(QList<ItemView::Mode>() << ItemView::Mode_BasicTree << ItemView::Mode_SimpleTree
144 << ItemView::Mode_DetailedTree << ItemView::Mode_List));
145 menuButton->addAction(configureAction);
146 menuButton->addAction(StreamsModel::self()->configureDiAct());
147 menuButton->addSeparator();
148 menuButton->addAction(addAction);
149 menuButton->addAction(StdActions::self()->removeAction);
150 menuButton->addAction(editAction);
151 menuButton->addAction(StreamsModel::self()->reloadAct());
152 menuButton->addSeparator();
153 menuButton->addAction(importAction);
154 menuButton->addAction(exportAction);
155
156 diStatusLabel=new ServiceStatusLabel(this);
157 diStatusLabel->setText("DI", tr("Digitally Imported", "Service name"));
158 connect(diStatusLabel, SIGNAL(clicked()), SLOT(diSettings()));
159 updateDiStatus();
160 ToolButton *searchButton=new ToolButton(this);
161 searchButton->setDefaultAction(searchAction);
162 init(ReplacePlayQueue, QList<QWidget *>() << menuButton << diStatusLabel, QList<QWidget *>() << searchButton);
163
164 view->addAction(editAction);
165 view->addAction(StdActions::self()->removeAction);
166 view->addAction(StreamsModel::self()->addToFavouritesAct());
167 view->addAction(StreamsModel::self()->addBookmarkAct());
168 view->addAction(StreamsModel::self()->reloadAct());
169 }
170
~StreamsBrowsePage()171 StreamsBrowsePage::~StreamsBrowsePage()
172 {
173 for (NetworkJob *job: resolveJobs) {
174 disconnect(job, SIGNAL(finished()), this, SLOT(tuneInResolved()));
175 job->deleteLater();
176 }
177 resolveJobs.clear();
178 Configuration config(metaObject()->className());
179 view->save(config);
180 }
181
showEvent(QShowEvent * e)182 void StreamsBrowsePage::showEvent(QShowEvent *e)
183 {
184 view->focusView();
185 QWidget::showEvent(e);
186 }
187
addSelectionToPlaylist(const QString & name,int action,quint8 priority,bool decreasePriority)188 void StreamsBrowsePage::addSelectionToPlaylist(const QString &name, int action, quint8 priority, bool decreasePriority)
189 {
190 Q_UNUSED(name)
191 addItemsToPlayQueue(view->selectedIndexes(), action, priority, decreasePriority);
192 }
193
addItemsToPlayQueue(const QModelIndexList & indexes,int action,quint8 priority,bool decreasePriority)194 void StreamsBrowsePage::addItemsToPlayQueue(const QModelIndexList &indexes, int action, quint8 priority, bool decreasePriority)
195 {
196 if (indexes.isEmpty()) {
197 return;
198 }
199 QModelIndexList mapped;
200 for (const QModelIndex &idx: indexes) {
201 mapped.append(proxy.mapToSource(idx));
202 }
203
204 QStringList files=StreamsModel::self()->filenames(mapped, true);
205
206 if (!files.isEmpty()) {
207 emit add(files, action, priority, decreasePriority);
208 view->clearSelection();
209 }
210 }
211
itemDoubleClicked(const QModelIndex & index)212 void StreamsBrowsePage::itemDoubleClicked(const QModelIndex &index)
213 {
214 if (!static_cast<StreamsModel::Item *>(proxy.mapToSource(index).internalPointer())->isCategory()) {
215 QModelIndexList indexes;
216 indexes.append(index);
217 addItemsToPlayQueue(indexes, false);
218 }
219 }
220
configure()221 void StreamsBrowsePage::configure()
222 {
223 if (!settings) {
224 settings=new StreamsSettings(this);
225 }
226 if (!settings->isVisible()) {
227 settings->load();
228 }
229 settings->show();
230 settings->raiseWindow();
231 }
232
configureDi()233 void StreamsBrowsePage::configureDi()
234 {
235 QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
236 if (1!=selected.count()) {
237 return;
238 }
239
240 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(proxy.mapToSource(selected.first()).internalPointer());
241 if (item->isCategory() && static_cast<const StreamsModel::CategoryItem *>(item)->isDi()) {
242 diSettings();
243 }
244 }
245
diSettings()246 void StreamsBrowsePage::diSettings()
247 {
248 DigitallyImportedSettings(this).show();
249 }
250
importXml()251 void StreamsBrowsePage::importXml()
252 {
253 QString fileName=QFileDialog::getOpenFileName(this, tr("Import Streams"), QDir::homePath(),
254 tr("XML Streams (*.xml *.xml.gz *.cantata)"));
255
256 if (fileName.isEmpty()) {
257 return;
258 }
259 StreamsModel::self()->importIntoFavourites(fileName);
260 }
261
exportXml()262 void StreamsBrowsePage::exportXml()
263 {
264 QLatin1String ext(".xml.gz");
265 QString fileName=QFileDialog::getSaveFileName(this, tr("Export Streams"), QDir::homePath()+QLatin1String("/streams")+ext, tr("XML Streams (*.xml.gz)"));
266
267 if (fileName.isEmpty()) {
268 return;
269 }
270
271 if (!fileName.endsWith(ext)) {
272 fileName+=ext;
273 }
274
275 if (!StreamsModel::self()->exportFavourites(fileName)) {
276 MessageBox::error(this, tr("Failed to create '%1'!").arg(fileName));
277 }
278 }
279
addStream()280 void StreamsBrowsePage::addStream()
281 {
282 StreamDialog dlg(this);
283
284 if (QDialog::Accepted==dlg.exec()) {
285 QString name=dlg.name();
286 QString url=dlg.url();
287 QString existingNameForUrl=StreamsModel::self()->favouritesNameForUrl(url);
288
289 if (!existingNameForUrl.isEmpty()) {
290 MessageBox::error(this, tr("Stream '%1' already exists!").arg(existingNameForUrl));
291 } else if (StreamsModel::self()->nameExistsInFavourites(name)) {
292 MessageBox::error(this, tr("A stream named '%1' already exists!").arg(name));
293 } else {
294 StreamsModel::self()->addToFavourites(url, name);
295 }
296 }
297 }
298
addBookmark()299 void StreamsBrowsePage::addBookmark()
300 {
301 QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
302
303 if (1!=selected.count()) {
304 return;
305 }
306
307 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(proxy.mapToSource(selected.first()).internalPointer());
308
309 // TODO: In future, if other categories support bookmarking, then we will need to calculate parent here!!!
310 if (StreamsModel::self()->addBookmark(item->url, item->name, nullptr)) {
311 view->showMessage(tr("Bookmark added"), constMsgDisplayTime);
312 } else {
313 view->showMessage(tr("Already bookmarked"), constMsgDisplayTime);
314 }
315 }
316
addToFavourites()317 void StreamsBrowsePage::addToFavourites()
318 {
319 QModelIndexList selected = view->selectedIndexes();
320
321 QList<const StreamsModel::Item *> items;
322
323 for (const QModelIndex &i: selected) {
324 QModelIndex mapped=proxy.mapToSource(i);
325 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(mapped.internalPointer());
326 if (!item->isCategory() && item->parent && !item->parent->isFavourites()) {
327 items.append(item);
328 }
329 }
330 QList<StreamItem> itemsToAdd;
331 for (const StreamsModel::Item *item: items) {
332 itemsToAdd.append(StreamItem(item->url, item->modifiedName()));
333 }
334 addToFavourites(itemsToAdd);
335 }
336
addToFavourites(const QList<StreamItem> & items)337 void StreamsBrowsePage::addToFavourites(const QList<StreamItem> &items)
338 {
339 int added=0;
340 for (const StreamItem item: items) {
341 QUrl url(item.url);
342 QUrlQuery query(url);
343 query.removeQueryItem(QLatin1String("locale"));
344 if (!query.isEmpty()) {
345 url.setQuery(query);
346 }
347 QString urlStr=url.toString();
348 if (urlStr.endsWith('&')) {
349 urlStr=urlStr.left(urlStr.length()-1);
350 }
351 if (urlStr.startsWith(QLatin1String("http://opml.radiotime.com/Tune.ashx"))) {
352 NetworkJob *job=NetworkAccessManager::self()->get(urlStr, 5000);
353 job->setProperty(constNameProperty, item.modifiedName);
354 connect(job, SIGNAL(finished()), this, SLOT(tuneInResolved()));
355 resolveJobs.insert(job);
356 added++;
357 } else if (StreamsModel::self()->addToFavourites(urlStr, item.modifiedName)) {
358 added++;
359 }
360 }
361
362 if (!added) {
363 view->showMessage(tr("Already in favorites"), constMsgDisplayTime);
364 }
365 }
366
tuneInResolved()367 void StreamsBrowsePage::tuneInResolved()
368 {
369 NetworkJob *job=qobject_cast<NetworkJob *>(sender());
370 if (!job) {
371 return;
372 }
373 job->deleteLater();
374 if (!resolveJobs.contains(job)) {
375 return;
376 }
377 resolveJobs.remove(job);
378 QString url=job->readAll().split('\n').first();
379 QString name=job->property(constNameProperty).toString();
380 if (!url.isEmpty() && !name.isEmpty() && !StreamsModel::self()->addToFavourites(url, name)) {
381 view->showMessage(tr("Already in favorites"), constMsgDisplayTime);
382 }
383 }
384
headerClicked(int level)385 void StreamsBrowsePage::headerClicked(int level)
386 {
387 if (0==level) {
388 emit close();
389 }
390 }
391
reload()392 void StreamsBrowsePage::reload()
393 {
394 QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
395 if (1!=selected.count()) {
396 return;
397 }
398
399 QModelIndex mapped=proxy.mapToSource(selected.first());
400 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(mapped.internalPointer());
401 if (!item->isCategory()) {
402 return;
403 }
404 const StreamsModel::CategoryItem *cat=static_cast<const StreamsModel::CategoryItem *>(item);
405 if (!cat->canReload()) {
406 return;
407 }
408
409 if (cat->children.isEmpty() || cat->cacheName.isEmpty() || MessageBox::Yes==MessageBox::questionYesNo(this, tr("Reload '%1' streams?").arg(cat->name))) {
410 StreamsModel::self()->reload(mapped);
411 }
412 }
413
removeItems()414 void StreamsBrowsePage::removeItems()
415 {
416 QModelIndexList selected = view->selectedIndexes();
417
418 if (1==selected.count()) {
419 QModelIndex mapped=proxy.mapToSource(selected.first());
420 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(mapped.internalPointer());
421 if (item->isCategory() && item->parent) {
422 if (item->parent->isBookmarks) {
423 if (MessageBox::No==MessageBox::warningYesNo(this, tr("Are you sure you wish to remove bookmark to '%1'?").arg(item->name))) {
424 return;
425 }
426 StreamsModel::self()->removeBookmark(mapped);
427 return;
428 } else if (static_cast<const StreamsModel::CategoryItem *>(item)->isBookmarks) {
429 if (MessageBox::No==MessageBox::warningYesNo(this, tr("Are you sure you wish to remove all '%1' bookmarks?").arg(item->parent->name))) {
430 return;
431 }
432 StreamsModel::self()->removeAllBookmarks(mapped);
433 return;
434 }
435 }
436 }
437
438 QModelIndexList useable;
439
440 for (const QModelIndex &i: selected) {
441 QModelIndex mapped=proxy.mapToSource(i);
442 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(mapped.internalPointer());
443 if (!item->isCategory() && item->parent && item->parent->isFavourites()) {
444 useable.append(mapped);
445 }
446 }
447
448 if (useable.isEmpty()) {
449 return;
450 }
451
452 if (useable.size()>1) {
453 if (MessageBox::No==MessageBox::warningYesNo(this, tr("Are you sure you wish to remove the %1 selected streams?").arg(useable.size()))) {
454 return;
455 }
456 } else {
457 if (MessageBox::No==MessageBox::warningYesNo(this, tr("Are you sure you wish to remove '%1'?").arg(
458 StreamsModel::self()->data(useable.first(), Qt::DisplayRole).toString()))) {
459 return;
460 }
461 }
462
463 StreamsModel::self()->removeFromFavourites(useable);
464 }
465
edit()466 void StreamsBrowsePage::edit()
467 {
468 QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
469
470 if (1!=selected.size()) {
471 return;
472 }
473
474 QModelIndex index=proxy.mapToSource(selected.first());
475 StreamsModel::Item *item=static_cast<StreamsModel::Item *>(index.internalPointer());
476 if (item->isCategory() || !item->parent || !item->parent->isFavourites()) {
477 return;
478 }
479
480 QString name=item->name;
481 QString url=item->url;
482
483 StreamDialog dlg(this);
484 dlg.setEdit(name, url);
485
486 if (QDialog::Accepted==dlg.exec()) {
487 QString newName=dlg.name();
488 QString newUrl=dlg.url();
489 QString existingNameForUrl=newUrl!=url ? StreamsModel::self()->favouritesNameForUrl(newUrl) : QString();
490
491 if (!existingNameForUrl.isEmpty()) {
492 MessageBox::error(this, tr("Stream '%1' already exists!").arg(existingNameForUrl));
493 } else if (newName!=name && StreamsModel::self()->nameExistsInFavourites(newName)) {
494 MessageBox::error(this, tr("A stream named '%1' already exists!").arg(newName));
495 } else {
496 StreamsModel::self()->updateFavouriteStream(newUrl, newName, index);
497 }
498 }
499 }
500
doSearch()501 void StreamsBrowsePage::doSearch()
502 {
503 QString text=view->searchText().trimmed();
504 if (!view->isSearchActive()) {
505 proxy.setFilterItem(nullptr);
506 }
507 proxy.update(view->isSearchActive() ? text : QString());
508 if (proxy.enabled() && !text.isEmpty()) {
509 view->expandAll(proxy.filterItem()
510 ? proxy.mapFromSource(StreamsModel::self()->categoryIndex(static_cast<const StreamsModel::CategoryItem *>(proxy.filterItem())))
511 : QModelIndex());
512 }
513 }
514
controlActions()515 void StreamsBrowsePage::controlActions()
516 {
517 QModelIndexList selected=view->selectedIndexes(false); // Dont need sorted selection here...
518 bool haveSelection=!selected.isEmpty();
519 bool enableAddToFav=true;
520 bool onlyStreamsSelected=true;
521 StreamsModel::self()->addBookmarkAct()->setEnabled(false);
522
523 editAction->setEnabled(false);
524 StreamsModel::self()->reloadAct()->setEnabled(false);
525
526 bool enableRemove=true;
527 for (const QModelIndex &idx: selected) {
528 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(proxy.mapToSource(idx).internalPointer());
529 if (item->isCategory() || (item->parent && !item->parent->isFavourites())) {
530 enableRemove=false;
531 }
532 if (item->isCategory() || (item->parent && item->parent->isFavourites())) {
533 enableAddToFav=false;
534 }
535 if (item->isCategory()) {
536 onlyStreamsSelected=false;
537 }
538 if (!enableRemove && !enableAddToFav && !onlyStreamsSelected) {
539 break;
540 }
541 }
542
543 StdActions::self()->removeAction->setEnabled(haveSelection && enableRemove);
544 StreamsModel::self()->addToFavouritesAct()->setEnabled(haveSelection && enableAddToFav);
545
546 if (1==selected.size()) {
547 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(proxy.mapToSource(selected.first()).internalPointer());
548 if (!item->isCategory() && item->parent && item->parent->isFavourites()) {
549 editAction->setEnabled(true);
550 }
551 StreamsModel::self()->reloadAct()->setEnabled(item->isCategory() && static_cast<const StreamsModel::CategoryItem *>(item)->canReload());
552 StreamsModel::self()->addBookmarkAct()->setEnabled(item->isCategory() && static_cast<const StreamsModel::CategoryItem *>(item)->canBookmark);
553 if (!StdActions::self()->removeAction->isEnabled()) {
554 StdActions::self()->removeAction->setEnabled(item->isCategory() && item->parent &&
555 (item->parent->isBookmarks || (static_cast<const StreamsModel::CategoryItem *>(item)->isBookmarks)));
556 }
557 StreamsModel::self()->configureDiAct()->setEnabled(item->isCategory() && static_cast<const StreamsModel::CategoryItem *>(item)->isDi());
558 } else {
559 StreamsModel::self()->configureDiAct()->setEnabled(false);
560 }
561
562 StdActions::self()->replacePlayQueueAction->setEnabled(haveSelection && onlyStreamsSelected);
563 }
564
updateDiStatus()565 void StreamsBrowsePage::updateDiStatus()
566 {
567 if (DigitallyImported::self()->user().isEmpty() || DigitallyImported::self()->pass().isEmpty()) {
568 diStatusLabel->setVisible(false);
569 } else {
570 diStatusLabel->setStatus(DigitallyImported::self()->loggedIn());
571 }
572 }
573
expandFavourites()574 void StreamsBrowsePage::expandFavourites()
575 {
576 view->expand(proxy.mapFromSource(StreamsModel::self()->favouritesIndex()), true);
577 }
578
addedToFavourites(const QString & name)579 void StreamsBrowsePage::addedToFavourites(const QString &name)
580 {
581 view->showMessage(tr("Added '%1'' to favorites").arg(name), constMsgDisplayTime);
582 }
583
StreamSearchPage(QWidget * p)584 StreamSearchPage::StreamSearchPage(QWidget *p)
585 : SinglePageWidget(p)
586 {
587 proxy.setSourceModel(&model);
588 view->setModel(&proxy);
589 view->alwaysShowHeader();
590 view->setPermanentSearch();
591 connect(view, SIGNAL(headerClicked(int)), SLOT(headerClicked(int)));
592 view->setMode(ItemView::Mode_DetailedTree);
593 init(ReplacePlayQueue);
594 connect(StreamsModel::self(), SIGNAL(addedToFavourites(QString)), SLOT(addedToFavourites(QString)));
595 }
596
~StreamSearchPage()597 StreamSearchPage::~StreamSearchPage()
598 {
599
600 }
601
showEvent(QShowEvent * e)602 void StreamSearchPage::showEvent(QShowEvent *e)
603 {
604 SinglePageWidget::showEvent(e);
605 view->focusSearch();
606 }
607
headerClicked(int level)608 void StreamSearchPage::headerClicked(int level)
609 {
610 if (0==level) {
611 emit close();
612 }
613 }
614
doSearch()615 void StreamSearchPage::doSearch()
616 {
617 model.search(view->searchText().trimmed(), false);
618 }
619
addSelectionToPlaylist(const QString & name,int action,quint8 priority,bool decreasePriority)620 void StreamSearchPage::addSelectionToPlaylist(const QString &name, int action, quint8 priority, bool decreasePriority)
621 {
622 Q_UNUSED(name)
623 QModelIndexList indexes=view->selectedIndexes();
624 if (indexes.isEmpty()) {
625 return;
626 }
627 QModelIndexList mapped;
628 for (const QModelIndex &idx: indexes) {
629 mapped.append(proxy.mapToSource(idx));
630 }
631
632 QStringList files=StreamsModel::self()->filenames(mapped, true);
633
634 if (!files.isEmpty()) {
635 emit add(files, action, priority, decreasePriority);
636 view->clearSelection();
637 }
638 }
639
addToFavourites()640 void StreamSearchPage::addToFavourites()
641 {
642 QModelIndexList selected = view->selectedIndexes();
643
644 QList<const StreamsModel::Item *> items;
645
646 for (const QModelIndex &i: selected) {
647 QModelIndex mapped=proxy.mapToSource(i);
648 const StreamsModel::Item *item=static_cast<const StreamsModel::Item *>(mapped.internalPointer());
649 if (!item->isCategory() && item->parent && !item->parent->isFavourites()) {
650 items.append(item);
651 }
652 }
653 QList<StreamItem> itemsToAdd;
654 for (const StreamsModel::Item *item: items) {
655 itemsToAdd.append(StreamItem(item->url, item->modifiedName()));
656 }
657 emit addToFavourites(itemsToAdd);
658 }
659
addedToFavourites(const QString & name)660 void StreamSearchPage::addedToFavourites(const QString &name)
661 {
662 view->showMessage(tr("Added '%1'' to favorites").arg(name), constMsgDisplayTime);
663 }
664
665 #include "moc_streamspage.cpp"
666