playlistbox.cpp 14.5 KB
Newer Older
1 2 3 4 5
/***************************************************************************
                          playlistbox.cpp  -  description
                             -------------------
    begin                : Thu Sep 12 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 <kiconloader.h>
19
#include <kurldrag.h>
20
#include <kmessagebox.h>
Nadeem Hasan's avatar
Nadeem Hasan committed
21
#include <kinputdialog.h>
Nadeem Hasan's avatar
Nadeem Hasan committed
22
#include <kpopupmenu.h>
23
#include <kaction.h>
24
#include <kmainwindow.h>
25
#include <kdebug.h>
26

27
#include <qheader.h>
28 29
#include <qpainter.h>
#include <qregexp.h>
30 31

#include "playlistbox.h"
32
#include "playlistsplitter.h"
33
#include "viewmode.h"
34
#include "searchplaylist.h"
35 36 37 38 39

////////////////////////////////////////////////////////////////////////////////
// PlaylistBox public methods
////////////////////////////////////////////////////////////////////////////////

40 41
PlaylistBox::PlaylistBox(PlaylistSplitter *parent, const QString &name) :
    KListView(parent, name.latin1()),
42 43 44 45
    m_splitter(parent),
    m_updatePlaylistStack(true),
    m_viewModeIndex(0),
    m_hasSelection(false)
46
{
47
    readConfig();
48
    addColumn("Playlists", width());
49 50
    header()->hide();
    setSorting(0);
51 52
    setFullWidth(true);
    setItemMargin(3);
53 54 55
	
    setAcceptDrops(true);
    setSelectionModeExt(Extended);
56
    
57
    m_contextMenu = new KPopupMenu(this);
58

59
    // Find the main window and then get the associated KActionCollection.
60

61 62 63 64 65 66
    QObject *w = parent;
    while(w && !dynamic_cast<KMainWindow *>(w))
	w = w->parent();
    
    if(!w)
	return;
67
    
68 69 70 71 72
    KActionCollection *actions = static_cast<KMainWindow *>(w)->actionCollection();
    
    actions->action("file_new")->plug(m_contextMenu);
    actions->action("renamePlaylist")->plug(m_contextMenu);
    actions->action("duplicatePlaylist")->plug(m_contextMenu);
73
    actions->action("reloadPlaylist")->plug(m_contextMenu);
74 75 76 77
    actions->action("deleteItemPlaylist")->plug(m_contextMenu);
    actions->action("file_save")->plug(m_contextMenu);
    actions->action("file_save_as")->plug(m_contextMenu);
    
78
    // add the view modes stuff
79
	
80
    m_viewModeAction = new KSelectAction(actions, "viewModeMenu");
81
    m_viewModeAction->setText(i18n("View Modes"));
82
    
83 84 85 86 87 88
    m_viewModes.append(new ViewMode(this));
    m_viewModes.append(new CompactViewMode(this));
    m_viewModes.append(new TreeViewMode(this));

    QStringList modeNames;

89 90 91
    QValueListIterator<ViewMode *> it = m_viewModes.begin();
    for(; it != m_viewModes.end(); ++it)
	modeNames.append((*it)->name());
92 93 94

    m_viewModeAction->setItems(modeNames);
    m_viewModeAction->setCurrentItem(m_viewModeIndex);
95
    m_viewModes[m_viewModeIndex]->setShown(true);
96 97
    
    m_viewModeAction->plug(m_contextMenu);
98 99
    connect(m_viewModeAction, SIGNAL(activated(int)), this, SLOT(slotSetViewMode(int)));

100
    connect(this, SIGNAL(selectionChanged()),
101
	    this, SLOT(slotPlaylistChanged()));
102
    
103 104
    connect(this, SIGNAL(doubleClicked(QListViewItem *)), 
	    this, SLOT(slotDoubleClicked(QListViewItem *)));
105
    
106 107
    connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)),
	    this, SLOT(slotShowContextMenu(QListViewItem *, const QPoint &, int)));
108 109 110 111
}

PlaylistBox::~PlaylistBox()
{
112
    saveConfig();
113 114
}

115
void PlaylistBox::createItem(Playlist *playlist, const char *icon, bool raise, bool sortedFirst)
116 117 118 119
{
    if(!playlist)
	return;

120
    Item *i = new Item(this, icon, playlist->name(), playlist);
121 122
    
    setupItem(i, playlist);
Nadeem Hasan's avatar
Nadeem Hasan committed
123

124
    if(raise) {
125
	setSingleItem(i);
Nadeem Hasan's avatar
Nadeem Hasan committed
126
	ensureCurrentVisible();
127
    }
128
    i->setSortedFirst(sortedFirst);
129 130 131

    if(playlist == CollectionList::instance())
	emit signalCollectionInitialized();
132 133
}

134 135 136 137 138 139
void PlaylistBox::createSearchItem(SearchPlaylist *playlist, const QString &searchCategory)
{
    Item *i = m_viewModes[m_viewModeIndex]->createSearchItem(this, playlist, searchCategory);
    setupItem(i, playlist);
}

140 141 142 143 144
void PlaylistBox::raise(Playlist *playlist)
{
    if(!playlist)
	return;

145
    Item *i = m_playlistDict.find(playlist);
146 147 148

    clearSelection();
    setSelected(i, true);
149

150
    setSingleItem(i);
151 152 153
    ensureCurrentVisible();
}

154
PlaylistList PlaylistBox::playlists()
155
{
156
    PlaylistList l;
157

158 159 160
    CollectionList *collection = CollectionList::instance();

    Item *i = static_cast<Item *>(firstChild());
161
    for(; i; i = static_cast<Item *>(i->nextSibling()))
162
	if(i->playlist() && i->playlist() != collection)
163
	    l.append(i->playlist());
164 165 166 167

    return l;
}

168 169
void PlaylistBox::save()
{
Scott Wheeler's avatar
Scott Wheeler committed
170
    save(static_cast<Item *>(currentItem()));
171 172
}

173
void PlaylistBox::saveAs()
174
{
Scott Wheeler's avatar
Scott Wheeler committed
175
    saveAs(static_cast<Item *>(currentItem()));
176 177
}

178 179
void PlaylistBox::rename()
{
Scott Wheeler's avatar
Scott Wheeler committed
180
    rename(static_cast<Item *>(currentItem()));
181 182 183
}

void PlaylistBox::duplicate()
184
{
Scott Wheeler's avatar
Scott Wheeler committed
185
    duplicate(static_cast<Item *>(currentItem()));
186 187
}

188 189 190 191
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox public slots
////////////////////////////////////////////////////////////////////////////////

192 193
void PlaylistBox::paste()
{
Scott Wheeler's avatar
Scott Wheeler committed
194
    Item *i = static_cast<Item *>(currentItem());
195 196 197 198 199 200 201
    decode(kapp->clipboard()->data(), i);
}

////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private methods
////////////////////////////////////////////////////////////////////////////////

202 203 204 205 206
void PlaylistBox::readConfig()
{
    KConfig *config = kapp->config();
    {
	KConfigGroupSaver saver(config, "PlaylistBox");
207
	m_viewModeIndex = config->readNumEntry("ViewMode", 0);
208 209 210 211 212 213 214 215 216 217 218 219 220
    }
}

void PlaylistBox::saveConfig()
{
    KConfig *config = kapp->config();
    {
	KConfigGroupSaver saver(config, "PlaylistBox");
	config->writeEntry("ViewMode", m_viewModeAction->currentItem());
	config->sync();
    }
}

221
void PlaylistBox::save(Item *item)
222 223
{
    if(item)
224
	item->playlist()->save();
225 226
}

227
void PlaylistBox::saveAs(Item *item)
228
{
229
    // kdDebug(65432) << "saveAs() - " << bool(item) << endl;
230 231
    if(item)
        item->playlist()->saveAs();
232 233
}

234
void PlaylistBox::rename(Item *item)
235
{
236 237
    if(!item)
	return;
238

239
    bool ok;
240

Nadeem Hasan's avatar
Nadeem Hasan committed
241
    QString name = KInputDialog::getText(i18n("Rename"),
242
        i18n("Please enter a name for this playlist:"), item->text(), &ok);
Nadeem Hasan's avatar
Nadeem Hasan committed
243

244
    if(ok) {
245
	item->setText(0, name);
Nadeem Hasan's avatar
Nadeem Hasan committed
246

247
	// Telling the playlist to change it's name will emit a signal that
248
	// is connected to Item::slotSetName().
Nadeem Hasan's avatar
Nadeem Hasan committed
249

250 251
	if(item->playlist())
	    item->playlist()->setName(name);
Nadeem Hasan's avatar
Nadeem Hasan committed
252

253 254 255
	sort();
	setSelected(item, true);
	ensureCurrentVisible();
256 257 258
    }
}

259
void PlaylistBox::duplicate(Item *item)
260 261 262 263 264 265
{
    if(item) {
	bool ok;

	// If this text is changed, please also change it in PlaylistSplitter::createPlaylist().

Nadeem Hasan's avatar
Nadeem Hasan committed
266
	QString name = KInputDialog::getText(i18n("New Playlist"), 
267 268
					     i18n("Please enter a name for the new playlist:"), 
					     m_splitter->uniquePlaylistName(item->text(0), true), &ok);
Nadeem Hasan's avatar
Nadeem Hasan committed
269

270
	if(ok) {
271
	    Playlist *p = m_splitter->createPlaylist(name);
272
	    p->createItems(item->playlist()->items());
273 274 275 276
	}
    }
}

277
void PlaylistBox::deleteItems(const ItemList &items)
278
{
279
    if(items.isEmpty())
280 281
	return;

282 283 284 285 286 287 288 289 290 291
    QStringList files;

    for(ItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
	if(*it && (*it)->playlist() && !(*it)->playlist()->fileName().isEmpty())
	    files.append((*it)->playlist()->fileName());
    }

    if(!files.isEmpty()) {
	int remove = KMessageBox::warningYesNoCancelList(
	    this, i18n("Do you want to delete these files from the disk as well?"), files);
292 293
	
	if(remove == KMessageBox::Yes) {
294 295 296 297 298 299 300 301 302 303 304
	    QStringList couldNotDelete;
	    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
		if(!QFile::remove(*it))
		    couldNotDelete.append(*it);
	    }

	    // Would be nice if there were a KMessageBox::sorryList() to use with
	    // couldNotDelete.

	    if(!couldNotDelete.isEmpty())
		KMessageBox::sorry(this, i18n("Could not delete all of the specified files."));
305
	}
306 307
	else if(remove == KMessageBox::Cancel)
	    return;
308
    }
309
    else {
310
	if(KMessageBox::warningYesNo(this, i18n("Are you sure you want to remove these items?")) == KMessageBox::No)
311 312
	    return;
    }
313

314 315 316 317 318 319 320 321 322 323 324
    QListViewItem *nextItem = 0;

    for(ItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
	m_names.remove((*it)->text(0));
	m_playlistDict.remove((*it)->playlist());

	nextItem = (*it)->nextSibling() ? (*it)->nextSibling() : (*it)->itemAbove();

	delete (*it)->playlist();
	delete *it;
    }
325

326
    setSingleItem(nextItem);
327 328
}

329
void PlaylistBox::decode(QMimeSource *s, Item *item)
330
{
331
    if(!s)
332 333
	return;

334 335
    KURL::List urls;
    
336
    if(KURLDrag::decode(s, urls) && !urls.isEmpty()) {
337
	QStringList files;
338
	
339 340
	for(KURL::List::Iterator it = urls.begin(); it != urls.end(); it++)
	    files.append((*it).path());
341 342 343 344 345

	if(item && item->playlist())
	    m_splitter->slotAddToPlaylist(files, item->playlist());
	else
	    emit signalCreatePlaylist(files);
346 347 348
    }
}

349
void PlaylistBox::contentsDropEvent(QDropEvent *e)
350
{
351
    Item *i = static_cast<Item *>(itemAt(contentsToViewport(e->pos())));
352 353 354
    decode(e, i);
}

355
void PlaylistBox::contentsDragMoveEvent(QDragMoveEvent *e)
356 357
{
    // If we can decode the input source, there is a non-null item at the "move"
358
    // position, the playlist for that Item is non-null, is not the 
359 360 361 362
    // selected playlist and is not the CollectionList, then accept the event.
    //
    // Otherwise, do not accept the event.
    
363 364 365 366 367 368
    if(!KURLDrag::canDecode(e)) {
	e->accept(false);
	return;
    }

    if(itemAt(e->pos())) {
369
	Item *target = static_cast<Item *>(itemAt(e->pos()));
370 371 372 373 374 375 376

	// This is a semi-dirty hack to check if the items are coming from within
	// JuK.  If they are not coming from a Playlist (or subclass) then the
	// dynamic_cast will fail and we can safely assume that the item is 
	// coming from outside of JuK.

	if(dynamic_cast<Playlist *>(e->source())) {
377 378 379 380
	    if(target->playlist() &&
	       target->playlist() != CollectionList::instance() &&
	       !target->isSelected())
	    {
381
		e->accept(true);
382
	    }
383 384
	    else
		e->accept(false);
385 386 387
	}
	else // the dropped items are coming from outside of JuK
	    e->accept(true);
388
    }
389 390 391 392 393 394 395
    else {

	// We're dragging over the whitespace.  We'll use this case to make it
	// possible to create new lists.

	e->accept(true);
    }
396 397
}

398
PlaylistBox::ItemList PlaylistBox::selectedItems()
399
{
400
    ItemList l;
401

402 403 404
    for(QListViewItemIterator it(this); it.current(); ++it) {
	if(isSelected(*it))
	    l.append(static_cast<Item *>(*it));
405
    }
406

407 408 409
    return l;
}

410
void PlaylistBox::setSingleItem(QListViewItem *item)
411
{
412 413 414
    setSelectionModeExt(Single);
    KListView::setCurrentItem(item);
    setSelectionModeExt(Extended);
415 416
}

417 418 419 420
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private slots
////////////////////////////////////////////////////////////////////////////////

421
void PlaylistBox::slotPlaylistChanged()
422
{
423
    ItemList items = selectedItems();
424
    m_hasSelection = !items.isEmpty();
425

426
    if(!m_updatePlaylistStack)
427
	return;
428

429 430
    PlaylistList playlists;
    for(ItemList::ConstIterator i = items.begin(); i != items.end(); ++i) {
431 432 433
	if((*i)->playlist())
	    playlists.append((*i)->playlist());
    }
434

435
    emit signalCurrentChanged(playlists);
436 437
}

438
void PlaylistBox::slotDoubleClicked(QListViewItem *)
439
{
440
    emit signalDoubleClicked();
441 442
}

443
void PlaylistBox::slotShowContextMenu(QListViewItem *, const QPoint &point, int)
444
{
445
    m_contextMenu->popup(point);
446 447
}

448
void PlaylistBox::slotSetViewMode(int index)
449
{
450 451 452 453 454 455
    if(index == m_viewModeIndex)
	return;

    viewMode()->setShown(false);
    m_viewModeIndex = index;
    viewMode()->setShown(true);    
456 457
}

458 459 460 461 462
void PlaylistBox::setupItem(Item *item, Playlist *playlist)
{
    m_playlistDict.insert(playlist, item);
}

463
////////////////////////////////////////////////////////////////////////////////
464
// PlaylistBox::Item protected methods
465 466
////////////////////////////////////////////////////////////////////////////////

467 468
PlaylistBox::Item *PlaylistBox::Item::m_collectionItem = 0;

469 470
PlaylistBox::Item::Item(PlaylistBox *listBox, const char *icon, const QString &text, Playlist *l) 
    : QObject(listBox), KListViewItem(listBox, text),
471
      m_list(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
472
{
473 474
    init();
}
475

476 477 478 479 480
PlaylistBox::Item::Item(Item *parent, const char *icon, const QString &text, Playlist *l)
    : QObject(parent->listView()), KListViewItem(parent, text),
    m_list(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
{
    init();
481 482
}

483
PlaylistBox::Item::~Item()
484 485 486 487
{

}

488 489
int PlaylistBox::Item::compare(QListViewItem *i, int col, bool) const
{
490 491 492
    Item *otherItem = static_cast<Item *>(i);

    if(m_sortedFirst && !otherItem->m_sortedFirst)
493
	return -1;
494
    else if(otherItem->m_sortedFirst && !m_sortedFirst)
495 496 497 498 499
	return 1;

    return text(col).lower().localeAwareCompare(i->text(col).lower());
}

500 501
void PlaylistBox::Item::paintCell(QPainter *painter, const QColorGroup &colorGroup, int column, int width, int align)
{
502 503
    PlaylistBox *playlistBox = static_cast<PlaylistBox *>(listView());
    playlistBox->viewMode()->paintCell(this, painter, colorGroup, column, width, align);
504 505 506 507 508 509 510 511
}

void PlaylistBox::Item::setText(int column, const QString &text)
{
    m_text = text;
    KListViewItem::setText(column, text);
}

512 513 514 515 516 517 518 519 520 521 522 523 524 525
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox::Item protected slots
////////////////////////////////////////////////////////////////////////////////

void PlaylistBox::Item::slotSetName(const QString &name)
{
    if(listView()) {
	listView()->m_names.remove(text(0));
	listView()->m_names.append(name);

	setText(0, name);
    }
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox::Item private methods
////////////////////////////////////////////////////////////////////////////////

void PlaylistBox::Item::init()
{
    int iconSize = static_cast<PlaylistBox *>(listView())->viewModeIndex() == 0 ? 32 : 16;
    setPixmap(0, SmallIcon(m_iconName, iconSize));
    static_cast<PlaylistBox *>(listView())->addName(m_text);

    if(m_list)
	connect(m_list, SIGNAL(signalNameChanged(const QString &)), this, SLOT(slotSetName(const QString &)));

    if(m_list == CollectionList::instance())
	m_collectionItem = this;
}

543
#include "playlistbox.moc"