source: smplayer/vendor/current/src/playlist.cpp@ 154

Last change on this file since 154 was 154, checked in by Silvan Scherrer, 12 years ago

SMPlayer: update vendor to 0.8.6

File size: 37.1 KB
Line 
1/* smplayer, GUI front-end for mplayer.
2 Copyright (C) 2006-2013 Ricardo Villalba <[email protected]>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17*/
18
19#include "playlist.h"
20
21#include <QToolBar>
22#include <QFile>
23#include <QTextStream>
24#include <QDir>
25#include <QFileInfo>
26#include <QMessageBox>
27#include <QPushButton>
28#include <QRegExp>
29#include <QMenu>
30#include <QDateTime>
31#include <QSettings>
32#include <QInputDialog>
33#include <QToolButton>
34#include <QTimer>
35#include <QVBoxLayout>
36#include <QUrl>
37#include <QDragEnterEvent>
38#include <QDropEvent>
39#include <QHeaderView>
40#include <QTextCodec>
41#include <QApplication>
42
43#include "mytablewidget.h"
44#include "myaction.h"
45#include "filedialog.h"
46#include "helper.h"
47#include "images.h"
48#include "preferences.h"
49#include "multilineinputdialog.h"
50#include "version.h"
51#include "global.h"
52#include "core.h"
53#include "extensions.h"
54#include "guiconfig.h"
55
56#include <stdlib.h>
57
58#if USE_INFOPROVIDER
59#include "infoprovider.h"
60#endif
61
62#define DRAG_ITEMS 0
63
64#define COL_PLAY 0
65#define COL_NAME 1
66#define COL_TIME 2
67
68using namespace Global;
69
70
71Playlist::Playlist( Core *c, QWidget * parent, Qt::WindowFlags f)
72 : QWidget(parent,f)
73{
74 save_playlist_in_config = true;
75 recursive_add_directory = false;
76 automatically_get_info = false;
77 play_files_from_start = true;
78
79 automatically_play_next = true;
80
81 row_spacing = -1; // Default height
82
83 modified = false;
84
85 core = c;
86 playlist_path = "";
87 latest_dir = "";
88
89 createTable();
90 createActions();
91 createToolbar();
92
93 connect( core, SIGNAL(mediaFinished()), this, SLOT(playNext()), Qt::QueuedConnection );
94 connect( core, SIGNAL(mediaLoaded()), this, SLOT(getMediaInfo()) );
95
96 QVBoxLayout *layout = new QVBoxLayout;
97 layout->addWidget( listView );
98 layout->addWidget( toolbar );
99 setLayout(layout);
100
101 clear();
102
103 retranslateStrings();
104
105#if !DOCK_PLAYLIST
106 setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding );
107 adjustSize();
108#else
109 //setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Expanding );
110 //setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
111 setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
112#endif
113
114 setAcceptDrops(true);
115 setAttribute(Qt::WA_NoMousePropagation);
116
117 // Random seed
118 QTime t;
119 t.start();
120 srand( t.hour() * 3600 + t.minute() * 60 + t.second() );
121
122 loadSettings();
123
124 // Ugly hack to avoid to play next item automatically
125 if (!automatically_play_next) {
126 disconnect( core, SIGNAL(mediaFinished()), this, SLOT(playNext()) );
127 }
128
129 // Save config every 5 minutes.
130 save_timer = new QTimer(this);
131 connect( save_timer, SIGNAL(timeout()), this, SLOT(maybeSaveSettings()) );
132 save_timer->start( 5 * 60000 );
133}
134
135Playlist::~Playlist() {
136 saveSettings();
137}
138
139void Playlist::setModified(bool mod) {
140 qDebug("Playlist::setModified: %d", mod);
141
142 modified = mod;
143 emit modifiedChanged(modified);
144}
145
146void Playlist::createTable() {
147 listView = new MyTableWidget( 0, COL_TIME + 1, this);
148 listView->setObjectName("playlist_table");
149 listView->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
150 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
151 listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
152 listView->setContextMenuPolicy( Qt::CustomContextMenu );
153 listView->setShowGrid(false);
154 listView->setSortingEnabled(false);
155 //listView->setAlternatingRowColors(true);
156 listView->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
157 listView->horizontalHeader()->setResizeMode(COL_NAME, QHeaderView::Stretch);
158 /*
159 listView->horizontalHeader()->setResizeMode(COL_TIME, QHeaderView::ResizeToContents);
160 listView->horizontalHeader()->setResizeMode(COL_PLAY, QHeaderView::ResizeToContents);
161 */
162 listView->setIconSize( Images::icon("ok").size() );
163
164#if DRAG_ITEMS
165 listView->setSelectionMode(QAbstractItemView::SingleSelection);
166 listView->setDragEnabled(true);
167 listView->setAcceptDrops(true);
168 listView->setDropIndicatorShown(true);
169 listView->setDragDropMode(QAbstractItemView::InternalMove);
170#endif
171
172 connect( listView, SIGNAL(cellActivated(int,int)),
173 this, SLOT(itemDoubleClicked(int)) );
174
175 // EDIT BY NEO -->
176 connect( listView->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(sortBy(int)));
177 // <--
178}
179
180void Playlist::createActions() {
181 openAct = new MyAction(this, "pl_open", false);
182 connect( openAct, SIGNAL(triggered()), this, SLOT(load()) );
183
184 saveAct = new MyAction(this, "pl_save", false);
185 connect( saveAct, SIGNAL(triggered()), this, SLOT(save()) );
186
187 playAct = new MyAction(this, "pl_play", false);
188 connect( playAct, SIGNAL(triggered()), this, SLOT(playCurrent()) );
189
190 nextAct = new MyAction(Qt::Key_N /*Qt::Key_Greater*/, this, "pl_next", false);
191 connect( nextAct, SIGNAL(triggered()), this, SLOT(playNext()) );
192
193 prevAct = new MyAction(Qt::Key_P /*Qt::Key_Less*/, this, "pl_prev", false);
194 connect( prevAct, SIGNAL(triggered()), this, SLOT(playPrev()) );
195
196 moveUpAct = new MyAction(this, "pl_move_up", false);
197 connect( moveUpAct, SIGNAL(triggered()), this, SLOT(upItem()) );
198
199 moveDownAct = new MyAction(this, "pl_move_down", false);
200 connect( moveDownAct, SIGNAL(triggered()), this, SLOT(downItem()) );
201
202 repeatAct = new MyAction(this, "pl_repeat", false);
203 repeatAct->setCheckable(true);
204
205 shuffleAct = new MyAction(this, "pl_shuffle", false);
206 shuffleAct->setCheckable(true);
207
208 // Add actions
209 addCurrentAct = new MyAction(this, "pl_add_current", false);
210 connect( addCurrentAct, SIGNAL(triggered()), this, SLOT(addCurrentFile()) );
211
212 addFilesAct = new MyAction(this, "pl_add_files", false);
213 connect( addFilesAct, SIGNAL(triggered()), this, SLOT(addFiles()) );
214
215 addDirectoryAct = new MyAction(this, "pl_add_directory", false);
216 connect( addDirectoryAct, SIGNAL(triggered()), this, SLOT(addDirectory()) );
217
218 addUrlsAct = new MyAction(this, "pl_add_urls", false);
219 connect( addUrlsAct, SIGNAL(triggered()), this, SLOT(addUrls()) );
220
221 // Remove actions
222 removeSelectedAct = new MyAction(this, "pl_remove_selected", false);
223 connect( removeSelectedAct, SIGNAL(triggered()), this, SLOT(removeSelected()) );
224
225 removeAllAct = new MyAction(this, "pl_remove_all", false);
226 connect( removeAllAct, SIGNAL(triggered()), this, SLOT(removeAll()) );
227
228 // Edit
229 editAct = new MyAction(this, "pl_edit", false);
230 connect( editAct, SIGNAL(triggered()), this, SLOT(editCurrentItem()) );
231}
232
233void Playlist::createToolbar() {
234 toolbar = new QToolBar(this);
235 toolbar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
236
237 toolbar->addAction(openAct);
238 toolbar->addAction(saveAct);;
239 toolbar->addSeparator();
240
241 add_menu = new QMenu( this );
242 add_menu->addAction(addCurrentAct);
243 add_menu->addAction(addFilesAct );
244 add_menu->addAction(addDirectoryAct);
245 add_menu->addAction(addUrlsAct);
246
247 add_button = new QToolButton( this );
248 add_button->setMenu( add_menu );
249 add_button->setPopupMode(QToolButton::InstantPopup);
250
251 remove_menu = new QMenu( this );
252 remove_menu->addAction(removeSelectedAct);
253 remove_menu->addAction(removeAllAct);
254
255 remove_button = new QToolButton( this );
256 remove_button->setMenu( remove_menu );
257 remove_button->setPopupMode(QToolButton::InstantPopup);
258
259 toolbar->addWidget(add_button);
260 toolbar->addWidget(remove_button);
261
262 toolbar->addSeparator();
263 toolbar->addAction(playAct);
264 toolbar->addSeparator();
265 toolbar->addAction(prevAct);
266 toolbar->addAction(nextAct);
267 toolbar->addSeparator();
268 toolbar->addAction(repeatAct);
269 toolbar->addAction(shuffleAct);
270 toolbar->addSeparator();
271 toolbar->addAction(moveUpAct);
272 toolbar->addAction(moveDownAct);
273
274 // Popup menu
275 popup = new QMenu(this);
276 popup->addAction(playAct);
277 popup->addAction(removeSelectedAct);
278 popup->addAction(editAct);
279
280 connect( listView, SIGNAL(customContextMenuRequested(const QPoint &)),
281 this, SLOT(showPopup(const QPoint &)) );
282}
283
284void Playlist::retranslateStrings() {
285 listView->setHorizontalHeaderLabels( QStringList() << " " <<
286 tr("Name") << tr("Length") );
287
288 openAct->change( Images::icon("open"), tr("&Load") );
289 saveAct->change( Images::icon("save"), tr("&Save") );
290
291 playAct->change( tr("&Play") );
292
293 nextAct->change( tr("&Next") );
294 prevAct->change( tr("Pre&vious") );
295
296 playAct->setIcon( Images::icon("play") );
297 nextAct->setIcon( Images::icon("next") );
298 prevAct->setIcon( Images::icon("previous") );
299
300 moveUpAct->change( Images::icon("up"), tr("Move &up") );
301 moveDownAct->change( Images::icon("down"), tr("Move &down") );
302
303 repeatAct->change( Images::icon("repeat"), tr("&Repeat") );
304 shuffleAct->change( Images::icon("shuffle"), tr("S&huffle") );
305
306 // Add actions
307 addCurrentAct->change( tr("Add &current file") );
308 addFilesAct->change( tr("Add &file(s)") );
309 addDirectoryAct->change( tr("Add &directory") );
310 addUrlsAct->change( tr("Add &URL(s)") );
311
312 // Remove actions
313 removeSelectedAct->change( tr("Remove &selected") );
314 removeAllAct->change( tr("Remove &all") );
315
316 // Edit
317 editAct->change( tr("&Edit") );
318
319 // Tool buttons
320 add_button->setIcon( Images::icon("plus") );
321 add_button->setToolTip( tr("Add...") );
322 remove_button->setIcon( Images::icon("minus") );
323 remove_button->setToolTip( tr("Remove...") );
324
325 // Icon
326 setWindowIcon( Images::icon("logo", 64) );
327 setWindowTitle( tr( "SMPlayer - Playlist" ) );
328}
329
330void Playlist::list() {
331 qDebug("Playlist::list");
332
333 PlaylistItemList::iterator it;
334 for ( it = pl.begin(); it != pl.end(); ++it ) {
335 qDebug( "filename: '%s', name: '%s' duration: %f",
336 (*it).filename().toUtf8().data(), (*it).name().toUtf8().data(),
337 (*it).duration() );
338 }
339}
340
341
342QString Playlist::print(QString seperator){
343 qDebug("Playlist::print");
344 QString output = "";
345
346 PlaylistItemList::iterator it;
347 for ( it = pl.begin(); it != pl.end(); ++it ) {
348 output += it->filename() + seperator + it->name() + seperator + QString::number(it->duration()) + "\r\n";
349 }
350
351 return output;
352}
353
354void Playlist::updateView() {
355 qDebug("Playlist::updateView");
356
357 listView->setRowCount( pl.count() );
358
359 //QString number;
360 QString name;
361 QString time;
362
363 for (int n=0; n < pl.count(); n++) {
364 name = pl[n].name();
365 if (name.isEmpty()) name = pl[n].filename();
366 time = Helper::formatTime( (int) pl[n].duration() );
367
368 //listView->setText(n, COL_POS, number);
369 qDebug("Playlist::updateView: name: '%s'", name.toUtf8().data());
370 listView->setText(n, COL_NAME, name);
371 listView->setText(n, COL_TIME, time);
372
373 if (pl[n].played()) {
374 listView->setIcon(n, COL_PLAY, Images::icon("ok") );
375 } else {
376 listView->setIcon(n, COL_PLAY, QPixmap() );
377 }
378
379 if (row_spacing > -1) listView->setRowHeight(n, listView->font().pointSize() + row_spacing);
380 }
381 //listView->resizeColumnsToContents();
382 listView->resizeColumnToContents(COL_PLAY);
383 listView->resizeColumnToContents(COL_TIME);
384
385 setCurrentItem(current_item);
386
387 //adjustSize();
388}
389
390void Playlist::setCurrentItem(int current) {
391 QIcon play_icon;
392 play_icon = Images::icon("play");
393
394 int old_current = current_item;
395 current_item = current;
396
397 if ((current_item > -1) && (current_item < pl.count())) {
398 pl[current_item].setPlayed(TRUE);
399 }
400
401 if ( (old_current >= 0) && (old_current < listView->rowCount()) ) {
402 listView->setIcon(old_current, COL_PLAY, QPixmap() );
403 }
404
405 if ( (current_item >= 0) && (current_item < listView->rowCount()) ) {
406 listView->setIcon(current_item, COL_PLAY, play_icon );
407 }
408 //if (current_item >= 0) listView->selectRow(current_item);
409 if (current_item >= 0) {
410 listView->clearSelection();
411 listView->setCurrentCell( current_item, 0);
412 }
413}
414
415void Playlist::clear() {
416 pl.clear();
417
418 listView->clearContents();
419 listView->setRowCount(0);
420
421 setCurrentItem(0);
422
423 setModified( false );
424}
425
426void Playlist::remove(int i){
427 if(i > -1 && i < pl.count()){
428 pl.removeAt(i);
429 if(current_item == i && i == (pl.count() - 1))
430 setCurrentItem(i - 1);
431 setModified(false);
432 updateView();
433 } //end if
434}
435
436int Playlist::count() {
437 return pl.count();
438}
439
440bool Playlist::isEmpty() {
441 return pl.isEmpty();
442}
443
444void Playlist::addItem(QString filename, QString name, double duration) {
445 qDebug("Playlist::addItem: '%s'", filename.toUtf8().data());
446
447 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
448 filename = Helper::changeSlashes(filename);
449 #endif
450
451 // Test if already is in the list
452 bool exists = false;
453 for ( int n = 0; n < pl.count(); n++) {
454 if ( pl[n].filename() == filename ) {
455 exists = true;
456 int last_item = pl.count()-1;
457 pl.move(n, last_item);
458 qDebug("Playlist::addItem: item already in list (%d), moved to %d", n, last_item);
459 if (current_item > -1) {
460 if (current_item > n) current_item--;
461 else
462 if (current_item == n) current_item = last_item;
463 }
464 break;
465 }
466 }
467
468 if (!exists) {
469 if (name.isEmpty()) {
470 QFileInfo fi(filename);
471 // Let's see if it looks like a file (no dvd://1 or something)
472 if (filename.indexOf(QRegExp("^.*://.*")) == -1) {
473 // Local file
474 name = fi.fileName(); //fi.baseName(TRUE);
475 } else {
476 // Stream
477 name = filename;
478 }
479 }
480 pl.append( PlaylistItem(filename, name, duration) );
481 //setModified( true ); // Better set the modified on a higher level
482 } else {
483 qDebug("Playlist::addItem: item not added, already in the list");
484 }
485}
486
487// EDIT BY NEO -->
488void Playlist::sortBy(int section) {
489 qDebug("Playlist::sortBy");
490
491 sortBy(section, false, 0);
492}
493
494void Playlist::sortBy(int section, bool revert, int count) {
495 // Bubble sort
496 bool swaped = false;
497
498 for ( int n = 0; n < (pl.count() - count); n++) {
499
500 int last = n - 1;
501 int current = n;
502
503 // Revert the sort
504 if (revert) {
505 last = n;
506 current = n - 1;
507 }
508
509 if (n > 0) {
510 int compare = 0;
511
512 if (section == 0) {
513 // Sort by played
514 bool lastItem = pl[last].played();
515 bool currentItem = pl[current].played();
516
517 if (!lastItem && currentItem) {
518 compare = 1;
519 } else if (lastItem && currentItem) {
520 if (last == current_item) {
521 compare = 1;
522 } else {
523 compare = -1;
524 }
525 } else {
526 compare = -1;
527 }
528 }
529 else if (section == 1) {
530 // Sort alphabetically
531 QString lastItem = pl[last].name();
532 QString currentItem = pl[current].name();
533 compare = lastItem.compare(currentItem);
534 } else if (section == 2) {
535 // Sort by duration
536 double lastItem = pl[last].duration();
537 double currentItem = pl[current].duration();
538
539 if (lastItem == currentItem) {
540 compare = 0;
541 } else if (lastItem > currentItem) {
542 compare = 1;
543 } else {
544 compare = -1;
545 }
546 }
547
548 // Swap items
549 if(compare > 0) {
550 swapItems(n, n - 1);
551
552 if (current_item == (n - 1)) {
553 current_item = n;
554 } else if (current_item == n) {
555 current_item = n - 1;
556 }
557
558 listView->clearSelection();
559 listView->setCurrentCell(n - 1, 0);
560
561 swaped = true;
562 }
563 }
564 }
565
566 if ((count == 0) && !swaped && !revert) {
567 // Revert sort
568 sortBy(section, true, 0);
569 }else if(swaped) {
570 // Sort until there is nothing to sort
571 sortBy(section, revert, ++count);
572 } else {
573 updateView();
574 }
575}
576// <--
577
578void Playlist::load_m3u(QString file) {
579 qDebug("Playlist::load_m3u");
580
581 bool utf8 = (QFileInfo(file).suffix().toLower() == "m3u8");
582
583 QRegExp m3u_id("^#EXTM3U|^#M3U");
584 QRegExp info("^#EXTINF:(.*),(.*)");
585
586 QFile f( file );
587 if ( f.open( QIODevice::ReadOnly ) ) {
588 playlist_path = QFileInfo(file).path();
589
590 clear();
591 QString filename="";
592 QString name="";
593 double duration=0;
594
595 QTextStream stream( &f );
596
597 if (utf8)
598 stream.setCodec("UTF-8");
599 else
600 stream.setCodec(QTextCodec::codecForLocale());
601
602 QString line;
603 while ( !stream.atEnd() ) {
604 line = stream.readLine(); // line of text excluding '\n'
605 qDebug( " * line: '%s'", line.toUtf8().data() );
606 if (m3u_id.indexIn(line)!=-1) {
607 //#EXTM3U
608 // Ignore line
609 }
610 else
611 if (info.indexIn(line)!=-1) {
612 duration = info.cap(1).toDouble();
613 name = info.cap(2);
614 qDebug(" * name: '%s', duration: %f", name.toUtf8().data(), duration );
615 }
616 else
617 if (line.startsWith("#")) {
618 // Comment
619 // Ignore
620 } else {
621 filename = line;
622 QFileInfo fi(filename);
623 if (fi.exists()) {
624 filename = fi.absoluteFilePath();
625 }
626 if (!fi.exists()) {
627 if (QFileInfo( playlist_path + "/" + filename).exists() ) {
628 filename = playlist_path + "/" + filename;
629 }
630 }
631 addItem( filename, name, duration );
632 name="";
633 duration = 0;
634 }
635 }
636 f.close();
637 list();
638 updateView();
639
640 setModified( false );
641
642 startPlay();
643 }
644}
645
646void Playlist::load_pls(QString file) {
647 qDebug("Playlist::load_pls");
648
649 if (!QFile::exists(file)) {
650 qDebug("Playlist::load_pls: '%s' doesn't exist, doing nothing", file.toUtf8().constData());
651 return;
652 }
653
654 playlist_path = QFileInfo(file).path();
655
656 QSettings set(file, QSettings::IniFormat);
657 set.beginGroup("playlist");
658
659 if (set.status() == QSettings::NoError) {
660 clear();
661 QString filename;
662 QString name;
663 double duration;
664
665 int num_items = set.value("NumberOfEntries", 0).toInt();
666
667 for (int n=0; n < num_items; n++) {
668 filename = set.value("File"+QString::number(n+1), "").toString();
669 name = set.value("Title"+QString::number(n+1), "").toString();
670 duration = (double) set.value("Length"+QString::number(n+1), 0).toInt();
671
672 QFileInfo fi(filename);
673 if (fi.exists()) {
674 filename = fi.absoluteFilePath();
675 }
676 if (!fi.exists()) {
677 if (QFileInfo( playlist_path + "/" + filename).exists() ) {
678 filename = playlist_path + "/" + filename;
679 }
680 }
681 addItem( filename, name, duration );
682 }
683 }
684
685 set.endGroup();
686
687 list();
688 updateView();
689
690 setModified( false );
691
692 if (set.status() == QSettings::NoError) startPlay();
693}
694
695bool Playlist::save_m3u(QString file) {
696 qDebug("Playlist::save_m3u: '%s'", file.toUtf8().data());
697
698 QString dir_path = QFileInfo(file).path();
699 if (!dir_path.endsWith("/")) dir_path += "/";
700
701 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
702 dir_path = Helper::changeSlashes(dir_path);
703 #endif
704
705 qDebug(" * dirPath: '%s'", dir_path.toUtf8().data());
706
707 bool utf8 = (QFileInfo(file).suffix().toLower() == "m3u8");
708
709 QFile f( file );
710 if ( f.open( QIODevice::WriteOnly ) ) {
711 QTextStream stream( &f );
712
713 if (utf8)
714 stream.setCodec("UTF-8");
715 else
716 stream.setCodec(QTextCodec::codecForLocale());
717
718 QString filename;
719
720 stream << "#EXTM3U" << "\n";
721 stream << "# Playlist created by SMPlayer " << Version::printable() << " \n";
722
723 PlaylistItemList::iterator it;
724 for ( it = pl.begin(); it != pl.end(); ++it ) {
725 filename = (*it).filename();
726 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
727 filename = Helper::changeSlashes(filename);
728 #endif
729 stream << "#EXTINF:";
730 stream << (*it).duration() << ",";
731 stream << (*it).name() << "\n";
732 // Try to save the filename as relative instead of absolute
733 if (filename.startsWith( dir_path )) {
734 filename = filename.mid( dir_path.length() );
735 }
736 stream << filename << "\n";
737 }
738 f.close();
739
740 setModified( false );
741 return true;
742 } else {
743 return false;
744 }
745}
746
747
748bool Playlist::save_pls(QString file) {
749 qDebug("Playlist::save_pls: '%s'", file.toUtf8().data());
750
751 QString dir_path = QFileInfo(file).path();
752 if (!dir_path.endsWith("/")) dir_path += "/";
753
754 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
755 dir_path = Helper::changeSlashes(dir_path);
756 #endif
757
758 qDebug(" * dirPath: '%s'", dir_path.toUtf8().data());
759
760 QSettings set(file, QSettings::IniFormat);
761 set.beginGroup( "playlist");
762
763 QString filename;
764
765 PlaylistItemList::iterator it;
766 for ( int n=0; n < pl.count(); n++ ) {
767 filename = pl[n].filename();
768 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
769 filename = Helper::changeSlashes(filename);
770 #endif
771
772 // Try to save the filename as relative instead of absolute
773 if (filename.startsWith( dir_path )) {
774 filename = filename.mid( dir_path.length() );
775 }
776
777 set.setValue("File"+QString::number(n+1), filename);
778 set.setValue("Title"+QString::number(n+1), pl[n].name());
779 set.setValue("Length"+QString::number(n+1), (int) pl[n].duration());
780 }
781
782 set.setValue("NumberOfEntries", pl.count());
783 set.setValue("Version", 2);
784
785 set.endGroup();
786
787 set.sync();
788
789 bool ok = (set.status() == QSettings::NoError);
790 if (ok) setModified( false );
791
792 return ok;
793}
794
795
796void Playlist::load() {
797 if (maybeSave()) {
798 Extensions e;
799 QString s = MyFileDialog::getOpenFileName(
800 this, tr("Choose a file"),
801 lastDir(),
802 tr("Playlists") + e.playlist().forFilter());
803
804 if (!s.isEmpty()) {
805 latest_dir = QFileInfo(s).absolutePath();
806
807 if (QFileInfo(s).suffix().toLower() == "pls")
808 load_pls(s);
809 else
810 load_m3u(s);
811 }
812 }
813}
814
815bool Playlist::save() {
816 Extensions e;
817 QString s = MyFileDialog::getSaveFileName(
818 this, tr("Choose a filename"),
819 lastDir(),
820 tr("Playlists") + e.playlist().forFilter());
821
822 if (!s.isEmpty()) {
823 // If filename has no extension, add it
824 if (QFileInfo(s).suffix().isEmpty()) {
825 s = s + ".m3u";
826 }
827 if (QFileInfo(s).exists()) {
828 int res = QMessageBox::question( this,
829 tr("Confirm overwrite?"),
830 tr("The file %1 already exists.\n"
831 "Do you want to overwrite?").arg(s),
832 QMessageBox::Yes,
833 QMessageBox::No,
834 QMessageBox::NoButton);
835 if (res == QMessageBox::No ) {
836 return false;
837 }
838 }
839 latest_dir = QFileInfo(s).absolutePath();
840
841 if (QFileInfo(s).suffix().toLower() == "pls")
842 return save_pls(s);
843 else
844 return save_m3u(s);
845
846 } else {
847 return false;
848 }
849}
850
851bool Playlist::maybeSave() {
852 if (!isModified()) return true;
853
854 int res = QMessageBox::question( this,
855 tr("Playlist modified"),
856 tr("There are unsaved changes, do you want to save the playlist?"),
857 QMessageBox::Yes,
858 QMessageBox::No,
859 QMessageBox::Cancel);
860
861 switch (res) {
862 case QMessageBox::No : return true; // Discard changes
863 case QMessageBox::Cancel : return false; // Cancel operation
864 default : return save();
865 }
866}
867
868void Playlist::playCurrent() {
869 int current = listView->currentRow();
870 if (current > -1) {
871 playItem(current);
872 }
873}
874
875void Playlist::itemDoubleClicked(int row) {
876 qDebug("Playlist::itemDoubleClicked: row: %d", row );
877 playItem(row);
878}
879
880void Playlist::showPopup(const QPoint & pos) {
881 qDebug("Playlist::showPopup: x: %d y: %d", pos.x(), pos.y() );
882
883 if (!popup->isVisible()) {
884 popup->move( listView->viewport()->mapToGlobal(pos) );
885 popup->show();
886 }
887}
888
889void Playlist::startPlay() {
890 // Start to play
891 if ( shuffleAct->isChecked() )
892 playItem( chooseRandomItem() );
893 else
894 playItem(0);
895}
896
897void Playlist::playItem( int n ) {
898 qDebug("Playlist::playItem: %d (count:%d)", n, pl.count());
899
900 if ( (n >= pl.count()) || (n < 0) ) {
901 qDebug("Playlist::playItem: out of range");
902 emit playlistEnded();
903 return;
904 }
905
906 qDebug(" playlist_path: '%s'", playlist_path.toUtf8().data() );
907
908 QString filename = pl[n].filename();
909 QString filename_with_path = playlist_path + "/" + filename;
910
911 if (!filename.isEmpty()) {
912 //pl[n].setPlayed(TRUE);
913 setCurrentItem(n);
914 if (play_files_from_start)
915 core->open(filename, 0);
916 else
917 core->open(filename);
918 }
919
920}
921
922void Playlist::playNext() {
923 qDebug("Playlist::playNext");
924
925 if (shuffleAct->isChecked()) {
926 // Shuffle
927 int chosen_item = chooseRandomItem();
928 if (chosen_item == -1) {
929 clearPlayedTag();
930 if (repeatAct->isChecked()) chosen_item = chooseRandomItem();
931 }
932 playItem( chosen_item );
933 } else {
934 bool finished_list = (current_item+1 >= pl.count());
935 if (finished_list) clearPlayedTag();
936
937 if ( (repeatAct->isChecked()) && (finished_list) ) {
938 playItem(0);
939 } else {
940 playItem( current_item+1 );
941 }
942 }
943}
944
945void Playlist::playPrev() {
946 qDebug("Playlist::playPrev");
947 if (current_item > 0) {
948 playItem( current_item-1 );
949 } else {
950 if (pl.count() > 1) playItem( pl.count() -1 );
951 }
952}
953
954void Playlist::getMediaInfo() {
955 qDebug("Playlist:: getMediaInfo");
956
957 QString filename = core->mdat.filename;
958 double duration = core->mdat.duration;
959 QString name = core->mdat.clip_name;
960 QString artist = core->mdat.clip_artist;
961
962 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
963 filename = Helper::changeSlashes(filename);
964 #endif
965
966 if (name.isEmpty()) {
967 QFileInfo fi(filename);
968 if (fi.exists()) {
969 // Local file
970 name = fi.fileName();
971 } else {
972 // Stream
973 name = filename;
974 }
975 }
976 if (!artist.isEmpty()) name = artist + " - " + name;
977
978 int pos=0;
979 PlaylistItemList::iterator it;
980 for ( it = pl.begin(); it != pl.end(); ++it ) {
981 if ( (*it).filename() == filename ) {
982 if ((*it).duration()<1) {
983 if (!name.isEmpty()) {
984 (*it).setName(name);
985 }
986 (*it).setDuration(duration);
987 //setModified( true );
988 }
989 else
990 // Edited name (sets duration to 1)
991 if ((*it).duration()==1) {
992 (*it).setDuration(duration);
993 //setModified( true );
994 }
995 setCurrentItem(pos);
996 }
997 pos++;
998 }
999 updateView();
1000}
1001
1002// Add current file to playlist
1003void Playlist::addCurrentFile() {
1004 qDebug("Playlist::addCurrentFile");
1005 if (!core->mdat.filename.isEmpty()) {
1006 addItem( core->mdat.filename, "", 0 );
1007 getMediaInfo();
1008 }
1009}
1010
1011void Playlist::addFiles() {
1012 Extensions e;
1013 QStringList files = MyFileDialog::getOpenFileNames(
1014 this, tr("Select one or more files to open"),
1015 lastDir(),
1016 tr("Multimedia") + e.multimedia().forFilter() + ";;" +
1017 tr("All files") +" (*.*)" );
1018
1019 if (files.count()!=0) addFiles(files);
1020}
1021
1022void Playlist::addFiles(QStringList files, AutoGetInfo auto_get_info) {
1023 qDebug("Playlist::addFiles");
1024
1025#if USE_INFOPROVIDER
1026 bool get_info = (auto_get_info == GetInfo);
1027 if (auto_get_info == UserDefined) {
1028 get_info = automatically_get_info;
1029 }
1030
1031 MediaData data;
1032 setCursor(Qt::WaitCursor);
1033#endif
1034
1035 QStringList::Iterator it = files.begin();
1036 while( it != files.end() ) {
1037#if USE_INFOPROVIDER
1038 if ( (get_info) && (QFile::exists((*it))) ) {
1039 data = InfoProvider::getInfo( (*it) );
1040 addItem( (*it), data.displayName(), data.duration );
1041 //updateView();
1042 //qApp->processEvents();
1043 } else {
1044 addItem( (*it), "", 0 );
1045 }
1046#else
1047 addItem( (*it), "", 0 );
1048#endif
1049
1050 if (QFile::exists(*it)) {
1051 latest_dir = QFileInfo((*it)).absolutePath();
1052 }
1053
1054 ++it;
1055 }
1056#if USE_INFOPROVIDER
1057 unsetCursor();
1058#endif
1059 updateView();
1060
1061 qDebug( "Playlist::addFiles: latest_dir: '%s'", latest_dir.toUtf8().constData() );
1062}
1063
1064void Playlist::addFile(QString file, AutoGetInfo auto_get_info) {
1065 addFiles( QStringList() << file, auto_get_info );
1066}
1067
1068void Playlist::addDirectory() {
1069 QString s = MyFileDialog::getExistingDirectory(
1070 this, tr("Choose a directory"),
1071 lastDir() );
1072
1073 if (!s.isEmpty()) {
1074 addDirectory(s);
1075 latest_dir = s;
1076 }
1077}
1078
1079void Playlist::addUrls() {
1080 MultilineInputDialog d(this);
1081 if (d.exec() == QDialog::Accepted) {
1082 QStringList urls = d.lines();
1083 foreach(QString u, urls) {
1084 if (!u.isEmpty()) addItem( u, "", 0 );
1085 }
1086 updateView();
1087 }
1088}
1089
1090void Playlist::addOneDirectory(QString dir) {
1091 QStringList filelist;
1092
1093 Extensions e;
1094 QRegExp rx_ext(e.multimedia().forRegExp());
1095 rx_ext.setCaseSensitivity(Qt::CaseInsensitive);
1096
1097 QStringList dir_list = QDir(dir).entryList();
1098
1099 QString filename;
1100 QStringList::Iterator it = dir_list.begin();
1101 while( it != dir_list.end() ) {
1102 filename = dir;
1103 if (filename.right(1)!="/") filename += "/";
1104 filename += (*it);
1105 QFileInfo fi(filename);
1106 if (!fi.isDir()) {
1107 if (rx_ext.indexIn(fi.suffix()) > -1) {
1108 filelist << filename;
1109 }
1110 }
1111 ++it;
1112 }
1113 addFiles(filelist);
1114}
1115
1116void Playlist::addDirectory(QString dir) {
1117 addOneDirectory(dir);
1118
1119 if (recursive_add_directory) {
1120 QFileInfoList dir_list = QDir(dir).entryInfoList(QStringList() << "*", QDir::AllDirs | QDir::NoDotAndDotDot);
1121 for (int n=0; n < dir_list.count(); n++) {
1122 if (dir_list[n].isDir()) {
1123 qDebug("Playlist::addDirectory: adding directory: %s", dir_list[n].filePath().toUtf8().data());
1124 addDirectory(dir_list[n].filePath());
1125 }
1126 }
1127 }
1128}
1129
1130// Remove selected items
1131void Playlist::removeSelected() {
1132 qDebug("Playlist::removeSelected");
1133
1134 int first_selected = -1;
1135 int number_previous_item = 0;
1136
1137 for (int n=0; n < listView->rowCount(); n++) {
1138 if (listView->isSelected(n, 0)) {
1139 qDebug(" row %d selected", n);
1140 pl[n].setMarkForDeletion(TRUE);
1141 number_previous_item++;
1142 if (first_selected == -1) first_selected = n;
1143 }
1144 }
1145
1146 PlaylistItemList::iterator it;
1147 for ( it = pl.begin(); it != pl.end(); ++it ) {
1148 if ( (*it).markedForDeletion() ) {
1149 qDebug("Remove '%s'", (*it).filename().toUtf8().data());
1150 it = pl.erase(it);
1151 it--;
1152 setModified( true );
1153 }
1154 }
1155
1156
1157 if (first_selected < current_item) {
1158 current_item -= number_previous_item;
1159 }
1160
1161 if (isEmpty()) setModified(false);
1162 updateView();
1163
1164 if (first_selected >= listView->rowCount())
1165 first_selected = listView->rowCount() - 1;
1166
1167 if ( ( first_selected > -1) && ( first_selected < listView->rowCount() ) ) {
1168 listView->clearSelection();
1169 listView->setCurrentCell( first_selected, 0);
1170 //listView->selectRow( first_selected );
1171 }
1172}
1173
1174void Playlist::removeAll() {
1175 /*
1176 pl.clear();
1177 updateView();
1178 setModified( false );
1179 */
1180 clear();
1181}
1182
1183void Playlist::clearPlayedTag() {
1184 PlaylistItemList::iterator it;
1185 for ( it = pl.begin(); it != pl.end(); ++it ) {
1186 (*it).setPlayed(FALSE);
1187 }
1188 updateView();
1189}
1190
1191int Playlist::chooseRandomItem() {
1192 qDebug( "Playlist::chooseRandomItem");
1193 QList <int> fi; //List of not played items (free items)
1194
1195 int n=0;
1196 PlaylistItemList::iterator it;
1197 for ( it = pl.begin(); it != pl.end(); ++it ) {
1198 if (! (*it).played() ) fi.append(n);
1199 n++;
1200 }
1201
1202 qDebug(" * free items: %d", fi.count() );
1203
1204 if (fi.count()==0) return -1; // none free
1205
1206 qDebug(" * items: ");
1207 for (int i=0; i < fi.count(); i++) {
1208 qDebug(" * item: %d", fi[i]);
1209 }
1210
1211 int selected = (int) ((double) fi.count() * rand()/(RAND_MAX+1.0));
1212 qDebug(" * selected item: %d (%d)", selected, fi[selected]);
1213 return fi[selected];
1214}
1215
1216void Playlist::swapItems(int item1, int item2 ) {
1217 PlaylistItem it1 = pl[item1];
1218 pl[item1] = pl[item2];
1219 pl[item2] = it1;
1220 setModified( true );
1221}
1222
1223
1224void Playlist::upItem() {
1225 qDebug("Playlist::upItem");
1226
1227 int current = listView->currentRow();
1228 qDebug(" currentRow: %d", current );
1229
1230 moveItemUp(current);
1231
1232}
1233
1234void Playlist::downItem() {
1235 qDebug("Playlist::downItem");
1236
1237 int current = listView->currentRow();
1238 qDebug(" currentRow: %d", current );
1239
1240 moveItemDown(current);
1241}
1242
1243void Playlist::moveItemUp(int current){
1244 qDebug("Playlist::moveItemUp");
1245
1246 if (current >= 1) {
1247 swapItems( current, current-1 );
1248 if (current_item == (current-1)) current_item = current;
1249 else
1250 if (current_item == current) current_item = current-1;
1251 updateView();
1252 listView->clearSelection();
1253 listView->setCurrentCell( current-1, 0);
1254 }
1255}
1256void Playlist::moveItemDown(int current ){
1257 qDebug("Playlist::moveItemDown");
1258
1259 if ( (current > -1) && (current < (pl.count()-1)) ) {
1260 swapItems( current, current+1 );
1261 if (current_item == (current+1)) current_item = current;
1262 else
1263 if (current_item == current) current_item = current+1;
1264 updateView();
1265 listView->clearSelection();
1266 listView->setCurrentCell( current+1, 0);
1267 }
1268}
1269
1270void Playlist::editCurrentItem() {
1271 int current = listView->currentRow();
1272 if (current > -1) editItem(current);
1273}
1274
1275void Playlist::editItem(int item) {
1276 QString current_name = pl[item].name();
1277 if (current_name.isEmpty()) current_name = pl[item].filename();
1278
1279 bool ok;
1280 QString text = QInputDialog::getText( this,
1281 tr("Edit name"),
1282 tr("Type the name that will be displayed in the playlist for this file:"),
1283 QLineEdit::Normal,
1284 current_name, &ok );
1285 if ( ok && !text.isEmpty() ) {
1286 // user entered something and pressed OK
1287 pl[item].setName(text);
1288
1289 // If duration == 0 the name will be overwritten!
1290 if (pl[item].duration()<1) pl[item].setDuration(1);
1291 updateView();
1292
1293 setModified( true );
1294 }
1295}
1296
1297// Drag&drop
1298void Playlist::dragEnterEvent( QDragEnterEvent *e ) {
1299 qDebug("Playlist::dragEnterEvent");
1300
1301 if (e->mimeData()->hasUrls()) {
1302 e->acceptProposedAction();
1303 }
1304}
1305
1306void Playlist::dropEvent( QDropEvent *e ) {
1307 qDebug("Playlist::dropEvent");
1308
1309 QStringList files;
1310
1311 if (e->mimeData()->hasUrls()) {
1312 QList <QUrl> l = e->mimeData()->urls();
1313 QString s;
1314 for (int n=0; n < l.count(); n++) {
1315 if (l[n].isValid()) {
1316 qDebug("Playlist::dropEvent: scheme: '%s'", l[n].scheme().toUtf8().data());
1317 if (l[n].scheme() == "file")
1318 s = l[n].toLocalFile();
1319 else
1320 s = l[n].toString();
1321 /*
1322 qDebug(" * '%s'", l[n].toString().toUtf8().data());
1323 qDebug(" * '%s'", l[n].toLocalFile().toUtf8().data());
1324 */
1325 qDebug("Playlist::dropEvent: file: '%s'", s.toUtf8().data());
1326 files.append(s);
1327 }
1328 }
1329 }
1330
1331
1332 QStringList only_files;
1333 for (int n = 0; n < files.count(); n++) {
1334 if ( QFileInfo( files[n] ).isDir() ) {
1335 addDirectory( files[n] );
1336 } else {
1337 only_files.append( files[n] );
1338 }
1339 }
1340 addFiles( only_files );
1341}
1342
1343
1344void Playlist::hideEvent( QHideEvent * ) {
1345 emit visibilityChanged(false);
1346}
1347
1348void Playlist::showEvent( QShowEvent * ) {
1349 emit visibilityChanged(true);
1350}
1351
1352void Playlist::closeEvent( QCloseEvent * e ) {
1353 saveSettings();
1354 e->accept();
1355}
1356
1357
1358void Playlist::maybeSaveSettings() {
1359 qDebug("Playlist::maybeSaveSettings");
1360 if (isModified()) saveSettings();
1361}
1362
1363void Playlist::saveSettings() {
1364 qDebug("Playlist::saveSettings");
1365
1366 QSettings * set = settings;
1367
1368 set->beginGroup( "directories");
1369 bool save_dirs = set->value("save_dirs", false).toBool();
1370 set->endGroup();
1371
1372 set->beginGroup( "playlist");
1373
1374 set->setValue( "repeat", repeatAct->isChecked() );
1375 set->setValue( "shuffle", shuffleAct->isChecked() );
1376
1377 set->setValue( "auto_get_info", automatically_get_info );
1378 set->setValue( "recursive_add_directory", recursive_add_directory );
1379 set->setValue( "save_playlist_in_config", save_playlist_in_config );
1380 set->setValue( "play_files_from_start", play_files_from_start );
1381 set->setValue( "automatically_play_next", automatically_play_next );
1382
1383 set->setValue( "row_spacing", row_spacing );
1384
1385#if !DOCK_PLAYLIST
1386 set->setValue( "size", size() );
1387#endif
1388 if (save_dirs) {
1389 set->setValue( "latest_dir", latest_dir );
1390 } else {
1391 set->setValue( "latest_dir", "" );
1392 }
1393
1394 set->endGroup();
1395
1396 if (save_playlist_in_config) {
1397 //Save current list
1398 set->beginGroup( "playlist_contents");
1399
1400 set->setValue( "count", (int) pl.count() );
1401 for ( int n=0; n < pl.count(); n++ ) {
1402 set->setValue( QString("item_%1_filename").arg(n), pl[n].filename() );
1403 set->setValue( QString("item_%1_duration").arg(n), pl[n].duration() );
1404 set->setValue( QString("item_%1_name").arg(n), pl[n].name() );
1405 }
1406 set->setValue( "current_item", current_item );
1407 set->setValue( "modified", modified );
1408
1409 set->endGroup();
1410 }
1411}
1412
1413void Playlist::loadSettings() {
1414 qDebug("Playlist::loadSettings");
1415
1416 QSettings * set = settings;
1417
1418 set->beginGroup( "playlist");
1419
1420 repeatAct->setChecked( set->value( "repeat", repeatAct->isChecked() ).toBool() );
1421 shuffleAct->setChecked( set->value( "shuffle", shuffleAct->isChecked() ).toBool() );
1422
1423 automatically_get_info = set->value( "auto_get_info", automatically_get_info ).toBool();
1424 recursive_add_directory = set->value( "recursive_add_directory", recursive_add_directory ).toBool();
1425 save_playlist_in_config = set->value( "save_playlist_in_config", save_playlist_in_config ).toBool();
1426 play_files_from_start = set->value( "play_files_from_start", play_files_from_start ).toBool();
1427 automatically_play_next = set->value( "automatically_play_next", automatically_play_next ).toBool();
1428
1429 row_spacing = set->value( "row_spacing", row_spacing ).toInt();
1430
1431#if !DOCK_PLAYLIST
1432 resize( set->value("size", size()).toSize() );
1433#endif
1434
1435 latest_dir = set->value( "latest_dir", latest_dir ).toString();
1436
1437 set->endGroup();
1438
1439 if (save_playlist_in_config) {
1440 //Load latest list
1441 set->beginGroup( "playlist_contents");
1442
1443 int count = set->value( "count", 0 ).toInt();
1444 QString filename, name;
1445 double duration;
1446 for ( int n=0; n < count; n++ ) {
1447 filename = set->value( QString("item_%1_filename").arg(n), "" ).toString();
1448 duration = set->value( QString("item_%1_duration").arg(n), -1 ).toDouble();
1449 name = set->value( QString("item_%1_name").arg(n), "" ).toString();
1450 addItem( filename, name, duration );
1451 }
1452 setCurrentItem( set->value( "current_item", -1 ).toInt() );
1453 setModified( set->value( "modified", false ).toBool() );
1454 updateView();
1455
1456 set->endGroup();
1457 }
1458}
1459
1460QString Playlist::lastDir() {
1461 QString last_dir = latest_dir;
1462 if (last_dir.isEmpty()) last_dir = pref->latest_dir;
1463 return last_dir;
1464}
1465
1466// Language change stuff
1467void Playlist::changeEvent(QEvent *e) {
1468 if (e->type() == QEvent::LanguageChange) {
1469 retranslateStrings();
1470 } else {
1471 QWidget::changeEvent(e);
1472 }
1473}
1474
1475#include "moc_playlist.cpp"
Note: See TracBrowser for help on using the repository browser.