source: smplayer/trunk/src/playlist.cpp@ 99

Last change on this file since 99 was 93, checked in by Silvan Scherrer, 16 years ago

smplayer: 0.6.9

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