playlist.cpp 10.9 KB
Newer Older
1 2 3 4 5
/***************************************************************************
                          playlist.cpp  -  description
                             -------------------
    begin                : Sat Feb 16 2002
    copyright            : (C) 2002 by Scott Wheeler
6
    email                : wheeler@kde.org
7 8 9 10 11 12 13 14 15 16 17 18
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <kmessagebox.h>
19 20 21
#include <kurl.h>
#include <kurldrag.h>
#include <kiconloader.h>
22
#include <kstandarddirs.h>
23 24 25 26 27 28 29 30 31
#include <klocale.h>
#include <kdebug.h>

#include <qfileinfo.h>
#include <qdir.h>
#include <qtimer.h>
#include <qapplication.h>
#include <qptrlist.h>

32 33 34
#include <stdlib.h>
#include <time.h>

35
#include "playlist.h"
36
#include "collectionlist.h"
37
#include "playlistsplitter.h"
38 39 40 41 42

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

43
Playlist::Playlist(PlaylistSplitter *s, QWidget *parent, const QString &name) : KListView(parent, name.latin1())
44
{
45
    playlistName = name;
46
    internalFile = true;
47
    playlistFileName = QString::null;
48 49
    splitter = s;
    setup();
50 51
}

52
Playlist::Playlist(PlaylistSplitter *s, const QFileInfo &playlistFile, QWidget *parent, const char *name) : KListView(parent, name)
53
{
54
    internalFile = false;
55
    playlistFileName = playlistFile.absFilePath();
56 57
    splitter = s;
    setup();
58

59 60 61
    QFile file(playlistFileName);
    if(!file.open(IO_ReadOnly))
	return;
62 63 64

    QTextStream stream(&file);

65 66 67 68 69
    // turn off non-explicit sorting
    setSorting(columns() + 1);

    PlaylistItem *after = 0;

70 71 72
    while(!stream.atEnd()) {
	QString itemName = (stream.readLine()).stripWhiteSpace();

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
	// Here we're checking to see if JuK has recorded a name for the playlist
	// in the m3u file.  i.e. if one of the lines is 
	// "#NAME=Really Good Music" this will pick that out.

	if(itemName.startsWith("#")) {
	    if(itemName.startsWith("#NAME="))
		setName(itemName.section('=', 1, -1));
	}
	else {
	    QFileInfo item(itemName);

	    if(item.isRelative())
		item.setFile(QDir::cleanDirPath(playlistFile.dirPath(true) + "/" + itemName));
	    
	    if(item.exists() && item.isFile() && item.isReadable()) {
		if(after)
		    after = createItem(item, after);
		else
		    after = createItem(item);
	    }
93
	}
94 95 96
    }
    
    file.close();
97 98 99 100 101 102 103
}

Playlist::~Playlist()
{

}

104
void Playlist::save(bool autoGenerateFileName)
105
{
106 107 108 109 110
    if(autoGenerateFileName && playlistFileName == QString::null) {
	QString dataDir = KGlobal::dirs()->saveLocation("appdata");
	playlistFileName = dataDir + name() + "." + splitter->playlistExtensions().first();
    }
    else if(!autoGenerateFileName && (internalFile || playlistFileName == QString::null))
111 112 113 114 115 116 117 118 119
	return saveAs();
    
    QFile file(playlistFileName);

    if(!file.open(IO_WriteOnly))
	return KMessageBox::error(this, i18n("Could not save to file %1.").arg(playlistFileName));
    
    QTextStream stream(&file);

120 121 122
    if(playlistName != QString::null)
	stream << "#NAME=" << playlistName << endl;

123 124 125 126 127 128
    QStringList fileList = files();

    for(QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it)
	stream << *it << endl;
    
    file.close();
129 130 131 132
}

void Playlist::saveAs()
{
133 134 135 136 137 138 139 140 141
    // If this is one of our internal files, remove it when we save it to an
    // "external" file.

    if(internalFile && playlistFileName != QString::null)
	QFile::remove(playlistFileName);

    // This needs to be replace with something that sets the default to the 
    // playlist name.

142
    QStringList extensions = splitter->playlistExtensions();
143 144

    playlistFileName = KFileDialog::getSaveFileName(QString::null, 
145
						    splitter->extensionsString(splitter->playlistExtensions(),
146 147 148 149 150
										       i18n("Playlists")));
    playlistFileName = playlistFileName.stripWhiteSpace();
    internalFile = false;

    if(playlistFileName != QString::null) {
151 152 153 154 155 156 157
	if(!splitter->playlistExtensions().contains(playlistFileName.section('.', -1)))
	    playlistFileName.append('.' + splitter->playlistExtensions().first());

	
	if(playlistName == QString::null)
	    emit(nameChanged(name()));

158 159
	save();
    }
160 161 162 163 164 165 166 167 168 169
}

void Playlist::refresh()
{
    PlaylistItemList list;
    for(PlaylistItem *i = static_cast<PlaylistItem *>(firstChild()); i; i = static_cast<PlaylistItem *>(i->itemBelow()))
	i->refreshFromDisk();
}

void Playlist::clearItems(const PlaylistItemList &items)
170 171 172 173 174 175 176 177 178
{
    QPtrListIterator<PlaylistItem> it(items);
    while(it.current()) {
        members.remove(it.current()->absFilePath());
        delete(it.current());
        ++it;
    }
}

179
QStringList Playlist::files() const
180
{
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
    QStringList list;
    for(PlaylistItem *i = static_cast<PlaylistItem *>(firstChild()); i; i = static_cast<PlaylistItem *>(i->itemBelow()))
	list.append(i->absFilePath());

    return(list);
}

PlaylistItemList Playlist::items() const
{
    PlaylistItemList list;
    for(PlaylistItem *i = static_cast<PlaylistItem *>(firstChild()); i; i = static_cast<PlaylistItem *>(i->itemBelow()))
	list.append(i);

    return(list);
}

PlaylistItemList Playlist::selectedItems() const
{
    PlaylistItemList list;
    for(PlaylistItem *i = static_cast<PlaylistItem *>(firstChild()); i; i = static_cast<PlaylistItem *>(i->itemBelow()))
201 202
        if(i->isSelected())
            list.append(i);
203
    
204 205 206 207 208 209 210 211
    return(list);
}

void Playlist::remove()
{
    remove(selectedItems());
}

212
void Playlist::remove(const PlaylistItemList &items)
213 214 215
{
    if(isVisible() && !items.isEmpty()) {

216
        QStringList files;
217
	for(QPtrListIterator<PlaylistItem> it(items); it.current(); ++it)
218
            files.append(it.current()->fileName());
219

220 221 222 223 224 225 226 227
	QString message;

	if(files.count() == 1)
	    message = i18n("Do you really want to delete this item?");
	else
	    message = i18n("Do you really want to delete these %1 items?").arg(QString::number(files.count()));
	
	if(KMessageBox::questionYesNoList(this, message, files) == KMessageBox::Yes) {
228
	    for(QPtrListIterator<PlaylistItem> it(items); it.current(); ++it) {
229 230 231 232 233 234 235 236 237 238
		if(QFile::remove(it.current()->filePath()))
		    delete(it.current());
		else
		    KMessageBox::sorry(this, i18n("Could not save delete ") + it.current()->fileName() + ".");
	    }

	}
    }
}

239
PlaylistItem *Playlist::nextItem(PlaylistItem *current, bool random)
240
{
241 242
    if(!current)
	return(0);
243

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    PlaylistItem *i;

    if(random) {
	Playlist *list = static_cast<Playlist *>(current->listView());
	PlaylistItemList items = list->items();
	
	if(items.count() > 1) {
	    srand(time(0));
	    i = current;
	    while(i == current)
		i = items.at(rand() % items.count());
	}
	else
	    i = 0;
    }
    else
	i = static_cast<PlaylistItem *>(current->itemBelow());	

    return(i);
263 264
}

265 266 267 268 269
bool Playlist::isInternalFile() const
{
    return(internalFile);
}

270 271 272 273 274
void Playlist::setInternal(bool internal)
{
    internalFile = internal;
}

275
QString Playlist::fileName() const
276
{
277
    return(playlistFileName);
278 279
}

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
void Playlist::setFileName(const QString &n)
{
    playlistFileName = n;
}

QString Playlist::name() const
{
    if(playlistName == QString::null)
	return(playlistFileName.section(QDir::separator(), -1).section('.', 0, -2));
    else
	return(playlistName);
}

void Playlist::setName(const QString &n)
{
    playlistName = n;
    emit(nameChanged(playlistName));
}

299 300 301 302
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////

303 304
QDragObject *Playlist::dragObject()
{
305
    PlaylistItemList items = selectedItems();
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    KURL::List urls;
    for(PlaylistItem *i = items.first(); i; i = items.next()) {
	KURL url;
	url.setPath(i->absFilePath());
	urls.append(url);
    }
    
    KURLDrag *drag = new KURLDrag(urls, this, "Playlist Items");
    drag->setPixmap(SmallIcon("sound"));

    return(drag);
}

void Playlist::contentsDropEvent(QDropEvent *e)
{
    QListViewItem *moveAfter = itemAt(e->pos());
    if(!moveAfter)
	moveAfter = lastItem();

    // This is slightly more efficient since it doesn't have to cast everything
    // to PlaylistItem.

    if(e->source() == this) {
	QPtrList<QListViewItem> items = KListView::selectedItems();
	
331
	for(QPtrListIterator<QListViewItem> it(items); it.current(); ++it) {
332 333 334 335 336 337 338 339 340
	    (*it)->moveItem(moveAfter);
	    moveAfter = *it;
	}
    }
    else {
	KURL::List urls;
    
	if(KURLDrag::decode(e, urls) && !urls.isEmpty()) {
	    
341
	    QStringList fileList;
342 343
	    
	    for(KURL::List::Iterator it = urls.begin(); it != urls.end(); it++)
344
		fileList.append((*it).path());
345
	    
346 347
	    if(splitter)
		splitter->add(fileList, this);
348 349 350 351 352 353 354 355 356 357 358 359
	}
    }
}

void Playlist::contentsDragMoveEvent(QDragMoveEvent *e)
{
    if(KURLDrag::canDecode(e))
	e->accept(true);
    else
	e->accept(false);
}

360
PlaylistItem *Playlist::createItem(const QFileInfo &file, QListViewItem *after)
361 362 363
{
    CollectionListItem *item = CollectionList::instance()->lookup(file.absFilePath());

364
    if(!item && CollectionList::instance())
365
	item = new CollectionListItem(file);
366
    
367 368
    if(item && members.contains(file.absFilePath()) == 0 || allowDuplicates) {
	members.append(file.absFilePath());
369 370 371 372
	if(after)
	    return(new PlaylistItem(item, this, after));
	else
	    return(new PlaylistItem(item, this));
373 374 375 376 377
    }
    else
	return(0);
}

378 379 380 381 382
PlaylistSplitter *Playlist::playlistSplitter() const
{
    return(splitter);
}

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void Playlist::setup()
{
    addColumn(i18n("Track Name"));
    addColumn(i18n("Artist"));
    addColumn(i18n("Album"));
    addColumn(i18n("Track"));
    addColumn(i18n("Genre"));
    addColumn(i18n("Year"));
    addColumn(i18n("Length"));
    addColumn(i18n("File Name"));

    setAllColumnsShowFocus(true);
    setSelectionMode(QListView::Extended);
    setShowSortIndicator(true);
    setItemMargin(3);

    setSorting(1);

    connect(this, SIGNAL(selectionChanged()), this, SLOT(emitSelected()));
406 407 408 409

    addColumn(QString::null);
    setResizeMode(QListView::LastColumn);
    // setFullWidth(true);
410

411 412
    setAcceptDrops(true);
    allowDuplicates = false;
413 414

    playlistName = QString::null;
415 416 417 418 419 420 421 422 423 424 425 426
}

////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

void Playlist::emitSelected()
{
    emit(selectionChanged(selectedItems()));
}

#include "playlist.moc"