source: smplayer/vendor/0.8.1/src/playlist.cpp@ 177

Last change on this file since 177 was 121, checked in by Silvan Scherrer, 14 years ago

SMPlayer 0.7.1: vendor update

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