playlistbox.cpp 13.6 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 <klineeditdlg.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 96
    
    m_viewModeAction->plug(m_contextMenu);
97 98
    connect(m_viewModeAction, SIGNAL(activated(int)), this, SLOT(slotSetViewMode(int)));

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

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

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

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

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

130 131 132 133 134 135
void PlaylistBox::createSearchItem(SearchPlaylist *playlist, const QString &searchCategory)
{
    Item *i = m_viewModes[m_viewModeIndex]->createSearchItem(this, playlist, searchCategory);
    setupItem(i, playlist);
}

136 137 138 139 140
void PlaylistBox::raise(Playlist *playlist)
{
    if(!playlist)
	return;

141
    Item *i = m_playlistDict.find(playlist);
142 143 144

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

146
    setSingleItem(i);
147 148 149
    ensureCurrentVisible();
}

150
PlaylistList PlaylistBox::playlists()
151
{
152
    PlaylistList l;
153

154 155 156 157 158
    CollectionList *collection = CollectionList::instance();

    Item *i = static_cast<Item *>(firstChild());
    for(; i; i = static_cast<Item *>(i->itemBelow()))
	if(i->playlist() && i->playlist() != collection)
159
	    l.append(i->playlist());
160 161 162 163

    return l;
}

164 165
void PlaylistBox::save()
{
Scott Wheeler's avatar
Scott Wheeler committed
166
    save(static_cast<Item *>(currentItem()));
167 168
}

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

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

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

184 185 186 187
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox public slots
////////////////////////////////////////////////////////////////////////////////

188 189
void PlaylistBox::deleteItem()
{
Scott Wheeler's avatar
Scott Wheeler committed
190
    deleteItem(static_cast<Item *>(currentItem()));
191 192 193 194
}

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

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

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

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

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

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

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

240
    bool ok;
241

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

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

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

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

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

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

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

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

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

278
void PlaylistBox::deleteItem(Item *item)
279
{
280 281 282 283 284 285 286 287 288
    if(!item || !item->playlist())
	return;

    if(!item->playlist()->fileName().isEmpty()) {
	int remove = KMessageBox::warningYesNoCancel(this, i18n("Do you want to delete this file from the disk as well?"));
	
	if(remove == KMessageBox::Yes) {
	    if(!QFile::remove(item->playlist()->fileName()))
		KMessageBox::sorry(this, i18n("Could not delete the specified file."));
289
	}
290 291
	else if(remove == KMessageBox::Cancel)
	    return;
292
    }
293 294 295 296
    else {
	if(KMessageBox::warningYesNo(this, i18n("Are you sure you want to remove this item?")) == KMessageBox::No)
	    return;
    }
297
    
298
    m_names.remove(item->text(0));
299
    m_playlistDict.remove(item->playlist());
300

301
    setSingleItem(item->nextSibling() ? item->nextSibling() : item->itemAbove());
302

303 304
    delete item->playlist();
    delete item;
305 306
}

307
void PlaylistBox::decode(QMimeSource *s, Item *item)
308
{
309 310 311
    if(!s || !item || !item->playlist())
	return;

312 313
    KURL::List urls;
    
314
    if(KURLDrag::decode(s, urls) && !urls.isEmpty()) {
315
	QStringList files;
316
	
317 318
	for(KURL::List::Iterator it = urls.begin(); it != urls.end(); it++)
	    files.append((*it).path());
319
	
320
	m_splitter->slotAddToPlaylist(files, item->playlist());
321 322 323
    }
}

324
void PlaylistBox::contentsDropEvent(QDropEvent *e)
325
{
326
    Item *i = static_cast<Item *>(itemAt(e->pos()));
327 328 329
    decode(e, i);
}

330
void PlaylistBox::contentsDragMoveEvent(QDragMoveEvent *e)
331 332
{
    // If we can decode the input source, there is a non-null item at the "move"
333
    // position, the playlist for that Item is non-null, is not the 
334 335 336 337 338
    // selected playlist and is not the CollectionList, then accept the event.
    //
    // Otherwise, do not accept the event.
    
    if(KURLDrag::canDecode(e) && itemAt(e->pos())) {
339
	Item *target = static_cast<Item *>(itemAt(e->pos()));
340 341 342 343 344 345 346

	// 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())) {
347 348
	    if(target->playlist() && target->playlist() != CollectionList::instance() && !target->isSelected())
		e->accept(true);
349 350
	    else
		e->accept(false);
351 352 353
	}
	else // the dropped items are coming from outside of JuK
	    e->accept(true);
354 355 356
    }
    else
	e->accept(false);
357 358
}

359
QValueList<PlaylistBox::Item *> PlaylistBox::selectedItems()
360
{
361 362
    QValueList<Item *> l;

363 364 365
    for(QListViewItemIterator it(this); it.current(); ++it) {
	if(isSelected(*it))
	    l.append(static_cast<Item *>(*it));
366
    }
367

368 369 370
    return l;
}

371
void PlaylistBox::setSingleItem(QListViewItem *item)
372
{
373 374 375
    setSelectionModeExt(Single);
    KListView::setCurrentItem(item);
    setSelectionModeExt(Extended);
376 377
}

378 379 380 381
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private slots
////////////////////////////////////////////////////////////////////////////////

382
void PlaylistBox::slotPlaylistChanged()
383
{
384
    QValueList<Item *> items = selectedItems();
385
    m_hasSelection = !items.isEmpty();
386

387
    if(!m_updatePlaylistStack)
388
	return;
389

390
    QValueList<Playlist *> playlists;
391 392 393 394
    for(QValueList<Item *>::iterator i = items.begin(); i != items.end(); ++i) {
	if((*i)->playlist())
	    playlists.append((*i)->playlist());
    }
395

396
    emit signalCurrentChanged(playlists);
397 398
}

399
void PlaylistBox::slotDoubleClicked(QListViewItem *)
400
{
401
    emit signalDoubleClicked();
402 403
}

404
void PlaylistBox::slotShowContextMenu(QListViewItem *, const QPoint &point, int)
405
{
406
    m_contextMenu->popup(point);
407 408
}

409
void PlaylistBox::slotSetViewMode(int index)
410
{
411 412 413 414 415 416
    if(index == m_viewModeIndex)
	return;

    viewMode()->setShown(false);
    m_viewModeIndex = index;
    viewMode()->setShown(true);    
417 418
}

419 420 421 422 423
void PlaylistBox::setupItem(Item *item, Playlist *playlist)
{
    m_playlistDict.insert(playlist, item);
}

424
////////////////////////////////////////////////////////////////////////////////
425
// PlaylistBox::Item protected methods
426 427
////////////////////////////////////////////////////////////////////////////////

428 429
PlaylistBox::Item *PlaylistBox::Item::m_collectionItem = 0;

430 431
PlaylistBox::Item::Item(PlaylistBox *listBox, const char *icon, const QString &text, Playlist *l) 
    : QObject(listBox), KListViewItem(listBox, text),
432
      m_list(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
433
{
434 435
    init();
}
436

437 438 439 440 441
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();
442 443
}

444
PlaylistBox::Item::~Item()
445 446 447 448
{

}

449 450
int PlaylistBox::Item::compare(QListViewItem *i, int col, bool) const
{
451 452 453
    Item *otherItem = static_cast<Item *>(i);

    if(m_sortedFirst && !otherItem->m_sortedFirst)
454
	return -1;
455
    else if(otherItem->m_sortedFirst && !m_sortedFirst)
456 457 458 459 460
	return 1;

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

461 462
void PlaylistBox::Item::paintCell(QPainter *painter, const QColorGroup &colorGroup, int column, int width, int align)
{
463 464
    PlaylistBox *playlistBox = static_cast<PlaylistBox *>(listView());
    playlistBox->viewMode()->paintCell(this, painter, colorGroup, column, width, align);
465 466 467 468 469 470 471 472
}

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

473 474 475 476 477 478 479 480 481 482 483 484 485 486
////////////////////////////////////////////////////////////////////////////////
// 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);
    }
}

487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
////////////////////////////////////////////////////////////////////////////////
// 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;
}

504
#include "playlistbox.moc"