juk.cpp 30.5 KB
Newer Older
1 2 3 4 5
/***************************************************************************
                          juk.cpp  -  description
                             -------------------
    begin                : Mon Feb  4 23:40:41 EST 2002
    copyright            : (C) 2002 by Scott Wheeler
6
    email                : wheeler@kde.org
7
***************************************************************************/
8 9 10 11 12 13 14 15 16 17

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

18 19 20 21 22 23 24 25 26 27
#include <kaction.h>
#include <kcmdlineargs.h>
#include <kstatusbar.h>
#include <kdebug.h>
#include <kmessagebox.h>

#include <qmetaobject.h>
#include <qslider.h>
#include <qmime.h>

28 29 30 31 32 33 34 35
#include "juk.h"
#include "slideraction.h"
#include "statuslabel.h"
#include "splashscreen.h"
#include "genrelisteditor.h"
#include "systemtray.h"
#include "keydialog.h"
#include "tagguesserconfigdlg.h"
36
#include "filerenamerconfigdlg.h"
37

38 39 40 41
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
42 43 44 45
JuK::JuK(QWidget *parent, const char *name) :
    DCOPObject("Player"),
    KMainWindow(parent, name, WDestructiveClose),
    m_shuttingDown(false)
46
{
47 48
    // Expect segfaults if you change this order.

49
    readSettings();
50

51
    if(m_showSplash && !m_startDocked) {
52 53 54 55
	SplashScreen::instance()->show();
	kapp->processEvents();
    }

56
    setupActions();
57 58
    setupLayout();
    setupSplitterConnections();
59
    slotPlaylistChanged();
60
    readConfig();
61
    setupPlayer();
62
    setupSystemTray();
63
    setupGlobalAccels();
64
    processArgs();
65

66
    SplashScreen::finishedLoading();
Scott Wheeler's avatar
Scott Wheeler committed
67
    QTimer::singleShot(0, CollectionList::instance(), SLOT(slotCheckCache()));
68 69 70 71
}

JuK::~JuK()
{
72

73 74
}

75 76 77 78 79 80 81 82 83
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

void JuK::setVolume(float volume)
{
    if(m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
    {
Frerich Raabe's avatar
Frerich Raabe committed
84
        slotSetVolume(int(volume / 100) * m_sliderAction->getVolumeSlider()->maxValue());
85 86 87 88 89 90 91 92 93 94 95
    }
}

void JuK::startPlayingPlaylist()
{
    if(m_randomPlayAction->isChecked())
        play(m_splitter->playRandomFile());
    else
        play(m_splitter->playFirstFile());
}

96 97 98 99 100 101
void JuK::slotGuessTagInfo(int i)
{
    int index = m_guessMenu->popupMenu()->indexOf(i);
    m_splitter->slotGuessTagInfo(TagGuesser::Type(index));
}

102 103 104
void JuK::play()
{
    if(!m_player)
Scott Wheeler's avatar
Scott Wheeler committed
105
	return;
106 107 108

    if(m_player->paused()) {
        m_player->play();
109
        m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
Scott Wheeler's avatar
Scott Wheeler committed
110

111 112 113
	// Here, before doing anything, we want to make sure that the m_player did
	// in fact start.

Scott Wheeler's avatar
Scott Wheeler committed
114 115 116 117
	if(m_player->playing()) {
	    actionCollection()->action("pause")->setEnabled(true);
	    actionCollection()->action("stop")->setEnabled(true);
	    m_playTimer->start(m_pollInterval);
118 119
	    if(m_systemTray)
		m_systemTray->slotPlay();
Scott Wheeler's avatar
Scott Wheeler committed
120
	}
121 122 123 124
    }
    else if(m_player->playing())
	m_player->seekPosition(0);
    else
Scott Wheeler's avatar
Scott Wheeler committed
125 126
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				      m_loopPlaylistAction->isChecked()));
127 128 129 130 131 132 133 134 135
}

void JuK::pause()
{
    if(!m_player)
	return;

    m_playTimer->stop();
    m_player->pause();
136
    m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
    actionCollection()->action("pause")->setEnabled(false);
    if(m_systemTray)
	m_systemTray->slotPause();
}

void JuK::stop()
{
    if(!m_player || !m_sliderAction || !m_sliderAction->getVolumeSlider())
	return;

    m_playTimer->stop();
    m_player->stop();

    actionCollection()->action("pause")->setEnabled(false);
    actionCollection()->action("stop")->setEnabled(false);
    actionCollection()->action("back")->setEnabled(false);
    actionCollection()->action("forward")->setEnabled(false);

    m_sliderAction->getTrackPositionSlider()->setValue(0);
    m_sliderAction->getTrackPositionSlider()->setEnabled(false);

    m_splitter->stop();

    m_statusLabel->clear();

    if(m_systemTray)
	m_systemTray->slotStop();
}

void JuK::back()
{
    play(m_splitter->playPreviousFile(m_randomPlayAction->isChecked()));
}

void JuK::back(int howMany)
{
    for(--howMany; howMany > 0; --howMany)
        m_splitter->playPreviousFile(m_randomPlayAction->isChecked());

    play(m_splitter->playPreviousFile(m_randomPlayAction->isChecked()));
}

void JuK::slotPopulateBackMenu()
{
Scott Wheeler's avatar
Scott Wheeler committed
181 182
    m_splitter->populatePlayHistoryMenu(m_backAction->popupMenu(),
					m_randomPlayAction->isChecked());
183 184 185 186
}

void JuK::forward()
{
Scott Wheeler's avatar
Scott Wheeler committed
187 188
    play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				  m_loopPlaylistAction->isChecked()));
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
}

void JuK::seekBack()
{
    int position = m_sliderAction->getTrackPositionSlider()->value();
    position = QMAX(m_sliderAction->getTrackPositionSlider()->minValue(), position - 10);
    emit m_sliderAction->getTrackPositionSlider()->setValue(position);
}

void JuK::seekForward()
{
    int position = m_sliderAction->getTrackPositionSlider()->value();
    position = QMIN(m_sliderAction->getTrackPositionSlider()->maxValue(), position + 10);
    emit m_sliderAction->getTrackPositionSlider()->setValue(position);
}

void JuK::playPause()
{
    if(!m_player)
	return;

    if(m_player->playing())
	pause();
    else
	play();
}

void JuK::volumeUp()
{
    if(m_sliderAction && m_sliderAction->getVolumeSlider()) {
	int volume = m_sliderAction->getVolumeSlider()->value() +
Scott Wheeler's avatar
Scott Wheeler committed
220
	    m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% up
221 222 223 224 225 226 227 228 229
	slotSetVolume(volume);
	m_sliderAction->getVolumeSlider()->setValue(volume);
    }
}

void JuK::volumeDown()
{
    if(m_sliderAction && m_sliderAction->getVolumeSlider()) {
	int volume = m_sliderAction->getVolumeSlider()->value() -
Scott Wheeler's avatar
Scott Wheeler committed
230
	    m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% down
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
	slotSetVolume(volume);
	m_sliderAction->getVolumeSlider()->setValue(volume);
    }
}

void JuK::volumeMute()
{
    if(m_sliderAction && m_sliderAction->getVolumeSlider()) {
	if(m_muted)
	    slotSetVolume(m_sliderAction->getVolumeSlider()->value());
	else
	    slotSetVolume(0);
	    m_muted = !m_muted;
    }
}

void JuK::openFile(const QString &file)
{
    m_splitter->open(file);
}

252 253 254 255 256 257
void JuK::openFile(const QStringList &files)
{
    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
	openFile(*it);
}

258 259 260 261
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

262 263
void JuK::setupLayout()
{
264
    m_splitter = new PlaylistSplitter(this, "playlistSplitter");
265
    setCentralWidget(m_splitter);
266 267

    // playlist item activation connection
Scott Wheeler's avatar
Scott Wheeler committed
268 269 270 271
    connect(m_splitter, SIGNAL(signalActivated()),
	    this, SLOT(slotPlaySelectedFile()));
    connect(m_splitter, SIGNAL(signalListBoxDoubleClicked()),
	    this, SLOT(startPlayingPlaylist()));
272

273
    // create status bar
274 275
    m_statusLabel = new StatusLabel(statusBar());
    statusBar()->addWidget(m_statusLabel, 1);
276

Scott Wheeler's avatar
Scott Wheeler committed
277 278 279 280
    connect(m_splitter, SIGNAL(signalSelectedPlaylistCountChanged(int)),
	    m_statusLabel, SLOT(setPlaylistCount(int)));
    connect(m_statusLabel, SIGNAL(jumpButtonClicked()),
	    m_splitter, SLOT(slotSelectPlaying()));
281

282 283 284 285 286
    // Needs to be here because m_splitter is not called before setupActions
    // (because PlaylistSplitter itself accesses the actionCollection)
    new KAction(i18n("&Rename File"), 0, "CTRL+r", m_splitter, SLOT(slotRenameFile()),
                actionCollection(), "renameFile"); // 4

287
    m_splitter->setFocus();
288

289
    resize(750, 500);
290 291
}

292 293
void JuK::setupActions()
{
294
    //////////////////////////////////////////////////
295
    // file menu
296
    //////////////////////////////////////////////////
297

Scott Wheeler's avatar
Scott Wheeler committed
298 299 300
    KActionMenu *newMenu = new KActionMenu(i18n("&New"), "filenew",
					   actionCollection(), "file_new");

301 302 303 304 305 306
    // This connection will call the
    // PlaylistSplitter::slotCreatePlaylist(const QString &) slot - this is
    // possible because the QString parameter has a default value, so the
    // slot can be called without arguments (as required by the signal's
    // signature).

Scott Wheeler's avatar
Scott Wheeler committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
    newMenu->insert(createSplitterAction(i18n("Empty Playlist..."),
					 SLOT(slotCreatePlaylist()), 0, 0, 0));

    newMenu->insert(createSplitterAction(i18n("Playlist From Directory..."),
					 SLOT(slotCreatePlaylistFromDir()), 0, 0, 0));

    createSplitterAction(
	i18n("Open..."), SLOT(slotOpen()), "file_open", "fileopen", "CTRL+o");
    createSplitterAction(
	i18n("Open &Directory..."), SLOT(slotOpenDirectory()), "openDirectory", "fileopen");
    createSplitterAction(
	i18n("&Rename..."), SLOT(slotRenamePlaylist()), "renamePlaylist");
    createSplitterAction(
	i18n("D&uplicate..."), SLOT(slotDuplicatePlaylist()), "duplicatePlaylist");
    createSplitterAction(
	i18n("Save"), SLOT(slotSavePlaylist()), "file_save", "filesave", "CTRL+s");
    createSplitterAction(
	i18n("Save As..."), SLOT(slotSaveAsPlaylist()), "file_save_as", "filesaveas");
    createSplitterAction(
	i18n("R&emove"), SLOT(slotDeletePlaylist()), "deleteItemPlaylist", "edittrash");
    createSplitterAction(
	i18n("Reload"), SLOT(slotReloadPlaylist()), "reloadPlaylist", "reload");
329

330
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
331

332
    //////////////////////////////////////////////////
333
    // edit menu
334 335
    //////////////////////////////////////////////////

336 337 338 339
    KStdAction::cut(kapp,   SLOT(cut()),   actionCollection());
    KStdAction::copy(kapp,  SLOT(copy()),  actionCollection());
    KStdAction::paste(kapp, SLOT(paste()), actionCollection());
    KStdAction::clear(kapp, SLOT(clear()), actionCollection());
340

341
    KStdAction::selectAll(kapp, SLOT(selectAll()), actionCollection());
342

343
    createSplitterAction(
344
	i18n("Advanced Search"), SLOT(slotAdvancedSearch()), "advancedSearch", "find", "s");
345

346
    //////////////////////////////////////////////////
347
    // view menu
348
    //////////////////////////////////////////////////
349

Scott Wheeler's avatar
Scott Wheeler committed
350 351 352 353
    m_showSearchAction = new KToggleAction(i18n("Show &Search Bar"), "filefind",
					   0, actionCollection(), "showSearch");
    m_showEditorAction = new KToggleAction(i18n("Show &Tag Editor"), "edit",
					   0, actionCollection(), "showEditor");
354
    m_showHistoryAction = new KToggleAction(i18n("Show &History"), "history",
355
					   0, actionCollection(), "showHistory");
356

357
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
358

359
    //////////////////////////////////////////////////
360
    // play menu
361 362
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376
    m_randomPlayAction = new KToggleAction(i18n("&Random Play"), 0,
					   actionCollection(), "randomPlay");

    new KAction(i18n("&Play"),  "player_play",  0, this, SLOT(play()),
		actionCollection(), "play");

    new KAction(i18n("P&ause"), "player_pause", 0, this, SLOT(pause()),
		actionCollection(), "pause");

    new KAction(i18n("&Stop"),  "player_stop",  0, this, SLOT(stop()),
		actionCollection(), "stop");

    m_backAction = new KToolBarPopupAction(i18n("Skip &Back"), "player_start", 0,
					   this, SLOT(back()), actionCollection(), "back");
377

Scott Wheeler's avatar
Scott Wheeler committed
378 379
    connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),
	    this, SLOT(slotPopulateBackMenu()));
380

Scott Wheeler's avatar
Scott Wheeler committed
381 382
    connect(m_backAction->popupMenu(), SIGNAL(activated(int)),
	    this, SLOT(back(int)));
383

Scott Wheeler's avatar
Scott Wheeler committed
384 385
    new KAction(i18n("Skip &Forward"), "player_end", 0, this, SLOT(forward()),
		actionCollection(), "forward");
386

Scott Wheeler's avatar
Scott Wheeler committed
387 388
    m_loopPlaylistAction = new KToggleAction(i18n("&Loop Playlist"), 0, 0,
					     actionCollection(), "loopPlaylist");
389

390
    //////////////////////////////////////////////////
391
    // tagger menu
392 393
    //////////////////////////////////////////////////

394
    createSplitterAction(i18n("&Save"), SLOT(slotSaveTag()),
Scott Wheeler's avatar
Scott Wheeler committed
395 396 397
			 "saveItem",   "filesave", "CTRL+t");
    createSplitterAction(i18n("&Delete"), SLOT(slotDeleteSelectedItems()),
			 "removeItem", "editdelete");
398

399
    m_guessMenu = new KActionMenu(i18n("&Guess Tag Information"),
Scott Wheeler's avatar
Scott Wheeler committed
400 401
					     QString::null, actionCollection(),
					     "guessTag");
402

Scott Wheeler's avatar
Scott Wheeler committed
403
    KAction *a;
404

405 406
    a = new KAction(i18n("From &Filename"), "CTRL+f", actionCollection(), "guessTagFile");
    m_guessMenu->insert(a);
407
#if HAVE_MUSICBRAINZ
408 409
    a = new KAction(i18n("From &Internet"), "CTRL+i", actionCollection(), "guessTagInternet");
    m_guessMenu->insert(a);
410
#endif
Scott Wheeler's avatar
Scott Wheeler committed
411

412
    connect(m_guessMenu->popupMenu(), SIGNAL(activated(int)), this, SLOT(slotGuessTagInfo(int)));
Scott Wheeler's avatar
Scott Wheeler committed
413

414
    //////////////////////////////////////////////////
415
    // settings menu
416 417
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
418 419
    new KToggleAction(i18n("Show Menu Bar"), "CTRL+m", this,
		      SLOT(slotToggleMenuBar()), actionCollection(), "toggleMenuBar");
420

421
    setStandardToolBarMenuEnabled(true);
422

423
    m_toggleSplashAction = new KToggleAction(i18n("Show Splash Screen on Startp"),
Scott Wheeler's avatar
Scott Wheeler committed
424 425 426 427 428 429 430
					     0, actionCollection(), "showSplashScreen");

    m_toggleSystemTrayAction = new KToggleAction(i18n("&Dock in System Tray"),
						 0, actionCollection(), "toggleSystemTray");

    m_toggleDockOnCloseAction = new KToggleAction(i18n("&Stay in System Tray on Close"),
						  0, actionCollection(), "dockOnClose");
431

Scott Wheeler's avatar
Scott Wheeler committed
432 433
    m_togglePopupsAction = new KToggleAction(i18n("&Popup Track Announcement"),
					     0, this, 0, actionCollection(), "togglePopups");
434

Scott Wheeler's avatar
Scott Wheeler committed
435 436
    connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
	    this, SLOT(slotToggleSystemTray(bool)));
437

Scott Wheeler's avatar
Scott Wheeler committed
438 439 440

    new KAction(i18n("Genre List Editor..."), 0, this, SLOT(slotShowGenreListEditor()),
		actionCollection(), "showGenreListEditor");
441

442
    m_outputSelectAction = Player::playerSelectAction(actionCollection());
443

444
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
445 446
        m_outputSelectAction->setCurrentItem(0);
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
447
    }
448

Scott Wheeler's avatar
Scott Wheeler committed
449 450
    new KAction(i18n("&Tag Guesser..."), 0, 0, this, SLOT(slotConfigureTagGuesser()),
		actionCollection(), "tagGuesserConfig");
451

452 453 454
    new KAction(i18n("&File Renamer..."), 0, 0, this, SLOT(slotConfigureFileRenamer()),
		actionCollection(), "fileRenamerConfig");

455 456
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

457
    //////////////////////////////////////////////////
458
    // just in the toolbar
459 460
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
461 462
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(),
				      "trackPositionAction");
463 464

    createGUI();
465 466

    // set the slider to the proper orientation and make it stay that way
467
    m_sliderAction->slotUpdateOrientation();
468 469
}

470 471 472 473 474 475
void JuK::setupSplitterConnections()
{
    QValueListConstIterator<SplitterConnection> it = m_splitterConnections.begin();
    for(; it != m_splitterConnections.end(); ++it)
        connect((*it).first, SIGNAL(activated()), m_splitter, (*it).second);

Scott Wheeler's avatar
Scott Wheeler committed
476 477 478 479
    connect(m_showSearchAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetSearchVisible(bool)));
    connect(m_showEditorAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetEditorVisible(bool)));
480 481
    connect(m_showHistoryAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetHistoryVisible(bool)));
Scott Wheeler's avatar
Scott Wheeler committed
482 483 484 485
    connect(this, SIGNAL(dockWindowPositionChanged(QDockWindow *)), 
	    m_sliderAction, SLOT(slotUpdateOrientation(QDockWindow *)));
    connect(m_splitter, SIGNAL(signalPlaylistChanged()),
	    this, SLOT(slotPlaylistChanged()));
486 487
}

488
void JuK::setupSystemTray()
489
{
490
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
Frerich Raabe's avatar
Frerich Raabe committed
491 492
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
493

Scott Wheeler's avatar
Scott Wheeler committed
494 495
        connect(this, SIGNAL(signalNewSong(const QString&)), 
		m_systemTray, SLOT(slotNewSong(const QString&)));
496

Frerich Raabe's avatar
Frerich Raabe committed
497 498 499 500
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
501

Frerich Raabe's avatar
Frerich Raabe committed
502
        m_toggleDockOnCloseAction->setEnabled(true);
503

Frerich Raabe's avatar
Frerich Raabe committed
504
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
505
    }
506
    else {
Frerich Raabe's avatar
Frerich Raabe committed
507 508
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
509
    }
510 511
}

512 513
void JuK::setupPlayer()
{
514 515
    m_trackPositionDragging = false;
    m_noSeek = false;
Scott Wheeler's avatar
Scott Wheeler committed
516
    m_muted = false;
517 518 519 520
    actionCollection()->action("pause")->setEnabled(false);
    actionCollection()->action("stop")->setEnabled(false);
    actionCollection()->action("back")->setEnabled(false);
    actionCollection()->action("forward")->setEnabled(false);
521

522
    m_playTimer = new QTimer(this);
523
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
524

Scott Wheeler's avatar
Scott Wheeler committed
525 526 527 528 529 530 531 532 533 534
    if(m_sliderAction &&
       m_sliderAction->getTrackPositionSlider() &&
       m_sliderAction->getVolumeSlider())
    {
        connect(m_sliderAction->getTrackPositionSlider(), SIGNAL(valueChanged(int)),
		this, SLOT(slotTrackPositionSliderUpdate(int)));
        connect(m_sliderAction->getTrackPositionSlider(), SIGNAL(sliderPressed()),
		this, SLOT(slotTrackPositionSliderClicked()));
        connect(m_sliderAction->getTrackPositionSlider(), SIGNAL(sliderReleased()),
		this, SLOT(slotTrackPositionSliderReleased()));
535
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
536

Scott Wheeler's avatar
Scott Wheeler committed
537 538
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)),
		this, SLOT(slotSetVolume(int)));
539
    }
540

541
    int playerType = 0;
542
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
543 544
        playerType = m_outputSelectAction->currentItem();
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
545 546
    }

547
    m_player = Player::createPlayer(playerType);
548 549
}

550

551 552 553
void JuK::setupGlobalAccels()
{
    m_accel = new KGlobalAccel(this);
554 555 556 557 558 559 560 561 562
    KeyDialog::insert(m_accel, "PlayPause",   i18n("Play/Pause"),   this, SLOT(playPause()));
    KeyDialog::insert(m_accel, "Stop",        i18n("Stop Playing"), this, SLOT(stop()));
    KeyDialog::insert(m_accel, "Back",        i18n("Back"),         this, SLOT(back()));
    KeyDialog::insert(m_accel, "Forward",     i18n("Forward"),      this, SLOT(forward()));
    KeyDialog::insert(m_accel, "SeekBack",    i18n("Seek Back"),    this, SLOT(seekBack()));
    KeyDialog::insert(m_accel, "SeekForward", i18n("Seek Forward"), this, SLOT(seekForward()));
    KeyDialog::insert(m_accel, "VolumeUp",    i18n("Volume Up"),    this, SLOT(volumeUp()));
    KeyDialog::insert(m_accel, "VolumeDown",  i18n("Volume Down"),  this, SLOT(volumeDown()));
    KeyDialog::insert(m_accel, "Mute",        i18n("Mute"),         this, SLOT(volumeMute()));
563

564 565 566 567 568
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

569 570 571 572
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
573

574
    for(int i = 0; i < args->count(); i++)
Frerich Raabe's avatar
Frerich Raabe committed
575
        files.append(args->arg(i));
576

577
    m_splitter->open(files);
578 579
}

580 581 582
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
Frerich Raabe's avatar
Frerich Raabe committed
583
        e->accept();
584 585 586
    KMainWindow::keyPressEvent(e);
}

587 588 589 590 591 592 593 594 595
/**
 * These are settings that need to be know before setting up the GUI.
 */

void JuK::readSettings()
{
    KConfig *config = KGlobal::config();
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
Frerich Raabe's avatar
Frerich Raabe committed
596
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
597
        m_startDocked = config->readBoolEntry("StartDocked", false);
598 599 600
    }
}

601 602
void JuK::readConfig()
{
603
    // Automagically save and m_restore many window settings.
604 605
    setAutoSaveSettings();

606
    KConfig *config = KGlobal::config();
607
    { // player settings
608
        KConfigGroupSaver saver(config, "Player");
609
        if(m_sliderAction->getVolumeSlider()) {
Scott Wheeler's avatar
Scott Wheeler committed
610 611
	    int maxVolume = m_sliderAction->getVolumeSlider()->maxValue();
	    int volume = config->readNumEntry("Volume", maxVolume);
612
            m_sliderAction->getVolumeSlider()->setValue(volume);
613
        }
Frerich Raabe's avatar
Frerich Raabe committed
614 615
        bool randomPlay = config->readBoolEntry("RandomPlay", false);
        m_randomPlayAction->setChecked(randomPlay);
616

Frerich Raabe's avatar
Frerich Raabe committed
617 618
        bool loopPlaylist = config->readBoolEntry("LoopPlaylist", false);
        m_loopPlaylistAction->setChecked(loopPlaylist);
619
    }
620
    { // view settings
621
        KConfigGroupSaver saver(config, "View");
622 623 624 625 626

        bool showSearch = config->readBoolEntry("ShowSearch", true);
        m_showSearchAction->setChecked(showSearch);
        m_splitter->slotSetSearchVisible(showSearch);

Frerich Raabe's avatar
Frerich Raabe committed
627 628 629
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
630

631 632 633 634 635 636
	// The history list will actually be created by the playlist restoration
	// code, but we want to remember the checkbox's setting and hope that
	// it's in synch with the code that does the real work.
	
	bool showHistory = config->readBoolEntry("ShowHistory", false);
	m_showHistoryAction->setChecked(showHistory);
637
    }
638 639
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
640

Frerich Raabe's avatar
Frerich Raabe committed
641 642
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
643

Frerich Raabe's avatar
Frerich Raabe committed
644 645
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
646

Frerich Raabe's avatar
Frerich Raabe committed
647 648
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
649

Frerich Raabe's avatar
Frerich Raabe committed
650 651
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
652
    }
653

654
    m_toggleSplashAction->setChecked(m_showSplash);
655 656 657 658
}

void JuK::saveConfig()
{
659
    KConfig *config = KGlobal::config();
660
    { // m_player settings
661
        KConfigGroupSaver saver(config, "Player");
662 663
        if(m_sliderAction && m_sliderAction->getVolumeSlider())
            config->writeEntry("Volume", m_sliderAction->getVolumeSlider()->value());
Frerich Raabe's avatar
Frerich Raabe committed
664 665 666 667
        if(m_randomPlayAction)
            config->writeEntry("RandomPlay", m_randomPlayAction->isChecked());
        if(m_loopPlaylistAction)
            config->writeEntry("LoopPlaylist", m_loopPlaylistAction->isChecked());
668
    }
669 670
    { // view settings
        KConfigGroupSaver saver(config, "View");
671

Frerich Raabe's avatar
Frerich Raabe committed
672
        config->writeEntry("ShowEditor", m_showEditorAction->isChecked());
673
        config->writeEntry("ShowSearch", m_showSearchAction->isChecked());
674
	config->writeEntry("ShowHistory", m_showHistoryAction->isChecked());
675
    }
676 677
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
Frerich Raabe's avatar
Frerich Raabe committed
678
        config->writeEntry("ShowSplashScreen", m_toggleSplashAction->isChecked());
679
        config->writeEntry("StartDocked", m_startDocked);
Frerich Raabe's avatar
Frerich Raabe committed
680 681 682 683 684
        config->writeEntry("DockInSystemTray", m_toggleSystemTrayAction->isChecked());
        config->writeEntry("DockOnClose", m_toggleDockOnCloseAction->isChecked());
        config->writeEntry("TrackPopup", m_togglePopupsAction->isChecked());
        if(m_outputSelectAction)
            config->writeEntry("MediaSystem", m_outputSelectAction->currentItem());
685
    }
686
    config->sync();
687 688
}

689
bool JuK::queryExit()
690
{
691 692
    m_startDocked = !isVisible();

693 694 695 696
    hide();
    delete m_systemTray;
    m_systemTray = 0;

697
    stop();
698
    delete m_player;
699

700
    Cache::instance()->save();
701
    saveConfig();
702
    delete m_splitter;
703
    return true;
704 705
}

706 707
bool JuK::queryClose()
{
708
    if(!m_shuttingDown && m_systemTray && m_toggleDockOnCloseAction->isChecked()) {
709
	KMessageBox::information(this,
Scott Wheeler's avatar
Scott Wheeler committed
710 711 712
	    i18n("<qt>Closing the main window will keep JuK running in the system tray. "
		 "Use Quit from the File menu to quit the application.</qt>"),
	    i18n("Docking in System Tray"), "hideOnCloseInfo");
713 714 715 716 717 718 719
	hide();
	return false;
    }
    else
	return true;
}

720 721 722
QString JuK::playingString() const
{
    QString s;
723 724
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
725

726 727 728 729
    if(!m_splitter->playingArtist().isEmpty())
	s = m_splitter->playingArtist().simplifyWhiteSpace() + " - ";

    s += m_splitter->playingTrack().simplifyWhiteSpace();
730 731 732 733

    return s;
}

734 735
void JuK::updatePlaylistInfo()
{
Scott Wheeler's avatar
Scott Wheeler committed
736 737
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(),
				   m_splitter->selectedPlaylistCount());
738 739 740 741
}

void JuK::play(const QString &file)
{
742
    if(!m_player || !m_sliderAction || !m_sliderAction->getVolumeSlider())
Frerich Raabe's avatar
Frerich Raabe committed
743
        return;
744

Scott Wheeler's avatar
Scott Wheeler committed
745 746
    float volume = float(m_sliderAction->getVolumeSlider()->value()) /
	float(m_sliderAction->getVolumeSlider()->maxValue());
747 748

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
749
        m_player->stop();
750

751 752 753 754 755
    m_player->play(file, volume);

    // Make sure that the m_player actually starts before doing anything.

    if(m_player->playing()) {
756 757 758
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
759

Frerich Raabe's avatar
Frerich Raabe committed
760
        m_backAction->setEnabled(true);
761

Frerich Raabe's avatar
Frerich Raabe committed
762 763 764
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
765

Frerich Raabe's avatar
Frerich Raabe committed
766
        m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
767

Frerich Raabe's avatar
Frerich Raabe committed
768
        emit signalNewSong(playingString());
769

Frerich Raabe's avatar
Frerich Raabe committed
770 771
        if(m_systemTray)
            m_systemTray->slotPlay();
772 773
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
774
        stop();
775 776
}

Scott Wheeler's avatar
Scott Wheeler committed
777 778 779
KAction *JuK::createSplitterAction(const QString &text, const char *slot,
				   const char *name, const QString &pix,
				   const KShortcut &shortcut)
780 781 782 783 784 785 786 787 788 789 790 791 792
{
    KAction *action;

    if(pix.isNull())
	action = new KAction(text, shortcut, actionCollection(), name);
    else
	action = new KAction(text, pix, shortcut, actionCollection(), name);

    m_splitterConnections.append(SplitterConnection(action, slot));
    
    return action;
}

793 794 795 796
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

797
void JuK::slotPlaylistChanged()
798
{
799 800 801 802
    if(m_splitter->collectionListSelected() ||
       !m_splitter->hasListSelected() ||
       m_splitter->readOnlyListSelected())
    {
803 804 805
        actionCollection()->action("file_save")->setEnabled(false);
        actionCollection()->action("file_save_as")->setEnabled(false);
        actionCollection()->action("renamePlaylist")->setEnabled(false);
806
        actionCollection()->action("reloadPlaylist")->setEnabled(false);
807
        actionCollection()->action("deleteItemPlaylist")->setEnabled(false);
808 809
    }
    else {
810 811 812 813
        actionCollection()->action("file_save")->setEnabled(true);
        actionCollection()->action("file_save_as")->setEnabled(true);
        actionCollection()->action("renamePlaylist")->setEnabled(true);
        actionCollection()->action("deleteItemPlaylist")->setEnabled(true);
814 815 816 817 818

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
819
    }
820 821 822 823 824
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
    
825 826 827
    updatePlaylistInfo();
}

828 829 830 831
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

832
void JuK::slotShowGenreListEditor()
833 834 835 836
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
837

838
void JuK::slotToggleSystemTray(bool enabled)
839
{
840
    if(enabled && !m_systemTray)
841
	setupSystemTray();
842 843 844
    else if(!enabled && m_systemTray) {
	delete m_systemTray;
	m_systemTray = 0;
845
	m_toggleDockOnCloseAction->setEnabled(false);
846 847 848
    }
}

849
void JuK::slotSetOutput(int output)
850
{
851
    stop();
852 853
    delete m_player;
    m_player = Player::createPlayer(output);
854 855
}

856 857 858 859 860
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

861
////////////////////////////////////////////////////////////////////////////////
862
// additional player slots
863 864
////////////////////////////////////////////////////////////////////////////////

865
void JuK::slotTrackPositionSliderClicked()
866
{
867
    m_trackPositionDragging = true;
868 869
}

870
void JuK::slotTrackPositionSliderReleased()
871
{
872
    if(!m_player)
873 874
	return;

875 876
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
877 878
}

879
void JuK::slotTrackPositionSliderUpdate(int position)
880
{
881
    if(!m_player)
882 883
	return;

884 885
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
886 887 888 889

    // The dragging flag is set, so just update the status label, rather than seeking
    if(m_player->playing() && m_trackPositionDragging && !m_noSeek) {
	// position from 0 to 1
Scott Wheeler's avatar
Scott Wheeler committed
890 891
	float positionFraction = float(position) /
	    m_sliderAction->getTrackPositionSlider()->maxValue();
892 893 894 895 896
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

	m_statusLabel->setItemCurrentTime(seekTime);
    }
897 898
}

899 900
// This method is called when the play timer has expired.

901
void JuK::slotPollPlay()
902
{
903
    if(!m_player)
904 905
	return;

906
    // Our locking mechanism.  Since this method adjusts the play slider, we
907
    // want to make sure that our adjustments
908
    m_noSeek = true;
909

910
    if(!m_player->playing()) {
911

912
        m_playTimer->stop();
913

Scott Wheeler's avatar
Scott Wheeler committed
914 915
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				      m_loopPlaylistAction->isChecked()));
916
    }
917 918 919 920
    else if(!m_trackPositionDragging) {
        m_sliderAction->getTrackPositionSlider()->setValue(m_player->position());
	m_statusLabel->setItemTotalTime(m_player->totalTime());
	m_statusLabel->setItemCurrentTime(m_player->currentTime());
921 922 923
    }

    // Ok, this is weird stuff, but it works pretty well.  Ordinarily we don't
924
    // need to check up on our playing time very often, but in the span of the
925 926
    // last interval, we want to check a lot -- to figure out that we've hit the
    // end of the song as soon as possible.
927

Scott Wheeler's avatar
Scott Wheeler committed
928 929 930 931
    if(m_player->playing() &&
       m_player->totalTime() > 0 &&
       float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
    {
932
        m_playTimer->changeInterval(50);
Scott Wheeler's avatar
Scott Wheeler committed
933
    }
934

935
    m_noSeek = false;
936 937
}

938
void JuK::slotSetVolume(int volume)
939
{
940 941 942
    if(m_player && m_sliderAction && m_sliderAction->getVolumeSlider() &&
       m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
943
    {
Scott Wheeler's avatar
Scott Wheeler committed
944 945
        m_player->setVolume(float(volume) /
			    float(m_sliderAction->getVolumeSlider()->maxValue()));
946 947
    }
}
948

949 950 951 952 953 954
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

955 956 957 958 959 960
void JuK::slotConfigureFileRenamer()
{
    FileRenamerConfigDlg dlg(this);
    dlg.exec();
}

961
#include "juk.moc"