juk.cpp 30.4 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
#include "juk.h"
#include "slideraction.h"
#include "statuslabel.h"
#include "splashscreen.h"
#include "systemtray.h"
#include "keydialog.h"
#include "tagguesserconfigdlg.h"
35
#include "filerenamerconfigdlg.h"
36

37 38 39 40
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

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

48
    readSettings();
49

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

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

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

JuK::~JuK()
{
71

72 73
}

74 75 76 77 78 79 80 81 82
////////////////////////////////////////////////////////////////////////////////
// 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
83
        slotSetVolume(int(volume / 100) * m_sliderAction->getVolumeSlider()->maxValue());
84 85 86 87 88 89 90 91 92 93 94
    }
}

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

95
void JuK::slotGuessTagInfoFromFile()
96
{
97 98 99 100 101 102
    m_splitter->slotGuessTagInfo(TagGuesser::FileName);
}

void JuK::slotGuessTagInfoFromInternet()
{
    m_splitter->slotGuessTagInfo(TagGuesser::MusicBrainz);
103 104
}

105 106 107
void JuK::play()
{
    if(!m_player)
Scott Wheeler's avatar
Scott Wheeler committed
108
	return;
109 110 111

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

114 115 116
	// Here, before doing anything, we want to make sure that the m_player did
	// in fact start.

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

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

    m_playTimer->stop();
    m_player->pause();
139
    m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
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 181 182 183
    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
184 185
    m_splitter->populatePlayHistoryMenu(m_backAction->popupMenu(),
					m_randomPlayAction->isChecked());
186 187 188 189
}

void JuK::forward()
{
Scott Wheeler's avatar
Scott Wheeler committed
190 191
    play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				  m_loopPlaylistAction->isChecked()));
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 220 221 222
}

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
223
	    m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% up
224 225 226 227 228 229 230 231 232
	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
233
	    m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% down
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	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);
}

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

261 262 263 264
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

265 266
void JuK::setupLayout()
{
267
    m_splitter = new PlaylistSplitter(this, "playlistSplitter");
268
    setCentralWidget(m_splitter);
269 270

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

276
    // create status bar
277 278
    m_statusLabel = new StatusLabel(statusBar());
    statusBar()->addWidget(m_statusLabel, 1);
279

Scott Wheeler's avatar
Scott Wheeler committed
280 281 282 283
    connect(m_splitter, SIGNAL(signalSelectedPlaylistCountChanged(int)),
	    m_statusLabel, SLOT(setPlaylistCount(int)));
    connect(m_statusLabel, SIGNAL(jumpButtonClicked()),
	    m_splitter, SLOT(slotSelectPlaying()));
284

285 286 287 288 289
    // 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

290
    m_splitter->setFocus();
291

292
    resize(750, 500);
293 294
}

295 296
void JuK::setupActions()
{
297
    //////////////////////////////////////////////////
298
    // file menu
299
    //////////////////////////////////////////////////
300

Scott Wheeler's avatar
Scott Wheeler committed
301 302 303
    KActionMenu *newMenu = new KActionMenu(i18n("&New"), "filenew",
					   actionCollection(), "file_new");

304 305 306 307 308 309
    // 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
310 311 312
    newMenu->insert(createSplitterAction(i18n("Empty Playlist..."),
					 SLOT(slotCreatePlaylist()), 0, 0, 0));

313
    newMenu->insert(createSplitterAction(i18n("Playlist From Folder..."),
Scott Wheeler's avatar
Scott Wheeler committed
314 315 316 317 318
					 SLOT(slotCreatePlaylistFromDir()), 0, 0, 0));

    createSplitterAction(
	i18n("Open..."), SLOT(slotOpen()), "file_open", "fileopen", "CTRL+o");
    createSplitterAction(
319
	i18n("Open &Folder..."), SLOT(slotOpenDirectory()), "openDirectory", "fileopen");
Scott Wheeler's avatar
Scott Wheeler committed
320 321 322 323 324 325 326 327 328 329 330 331
    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");
332

333
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
334

335
    //////////////////////////////////////////////////
336
    // edit menu
337 338
    //////////////////////////////////////////////////

339 340 341 342
    KStdAction::cut(kapp,   SLOT(cut()),   actionCollection());
    KStdAction::copy(kapp,  SLOT(copy()),  actionCollection());
    KStdAction::paste(kapp, SLOT(paste()), actionCollection());
    KStdAction::clear(kapp, SLOT(clear()), actionCollection());
343

344
    KStdAction::selectAll(kapp, SLOT(selectAll()), actionCollection());
345

346
    createSplitterAction(
347
	i18n("Advanced Search"), SLOT(slotAdvancedSearch()), "advancedSearch", "find", "s");
348

349
    //////////////////////////////////////////////////
350
    // view menu
351
    //////////////////////////////////////////////////
352

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

360
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
361

362
    //////////////////////////////////////////////////
363
    // play menu
364 365
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
366 367 368 369 370 371 372 373 374 375 376 377
    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");

378
    m_backAction = new KToolBarPopupAction(i18n("Previous &Track"), "player_start", 0,
Scott Wheeler's avatar
Scott Wheeler committed
379
					   this, SLOT(back()), actionCollection(), "back");
380

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

Scott Wheeler's avatar
Scott Wheeler committed
384 385
    connect(m_backAction->popupMenu(), SIGNAL(activated(int)),
	    this, SLOT(back(int)));
386

387
    new KAction(i18n("&Next Track"), "player_end", 0, this, SLOT(forward()),
Scott Wheeler's avatar
Scott Wheeler committed
388
		actionCollection(), "forward");
389

Scott Wheeler's avatar
Scott Wheeler committed
390 391
    m_loopPlaylistAction = new KToggleAction(i18n("&Loop Playlist"), 0, 0,
					     actionCollection(), "loopPlaylist");
392

393
    //////////////////////////////////////////////////
394
    // tagger menu
395 396
    //////////////////////////////////////////////////

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

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

Scott Wheeler's avatar
Scott Wheeler committed
406
    KAction *a;
407

408 409
    a = new KAction(i18n("From &Filename"), 0, "CTRL+f", this, SLOT(slotGuessTagInfoFromFile()),
                actionCollection(), "guessTagFile");
410
    m_guessMenu->insert(a);
411
#if HAVE_MUSICBRAINZ
412 413
    a = new KAction(i18n("From &Internet"), 0, "CTRL+i", this, SLOT(slotGuessTagInfoFromInternet()),
                actionCollection(), "guessTagInternet");
414
    m_guessMenu->insert(a);
415
#endif
Scott Wheeler's avatar
Scott Wheeler committed
416

417
    //////////////////////////////////////////////////
418
    // settings menu
419 420
    //////////////////////////////////////////////////

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

424
    setStandardToolBarMenuEnabled(true);
425

Stephan Binner's avatar
Stephan Binner committed
426
    m_toggleSplashAction = new KToggleAction(i18n("Show Splash Screen on Startup"),
Scott Wheeler's avatar
Scott Wheeler committed
427 428 429 430 431 432 433
					     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");
434

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

Scott Wheeler's avatar
Scott Wheeler committed
438 439
    connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
	    this, SLOT(slotToggleSystemTray(bool)));
440

Scott Wheeler's avatar
Scott Wheeler committed
441

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

444
    if(m_outputSelectAction) {
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)));
482
    connect(this, SIGNAL(dockWindowPositionChanged(QDockWindow *)),
Scott Wheeler's avatar
Scott Wheeler committed
483 484 485
	    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()) {
491 492
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
493

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

497 498 499 500
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
501

502
        m_toggleDockOnCloseAction->setEnabled(true);
503

504
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
505
    }
506
    else {
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) {
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++)
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)
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");
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
        }
614 615
        bool randomPlay = config->readBoolEntry("RandomPlay", false);
        m_randomPlayAction->setChecked(randomPlay);
616

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);

627 628 629
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
630

631 632 633
	// 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.
634

635 636
	bool showHistory = config->readBoolEntry("ShowHistory", false);
	m_showHistoryAction->setChecked(showHistory);
637
    }
638 639
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
640

641 642
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
643

644 645
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
646

647 648
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
649

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());
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

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");
678
        config->writeEntry("ShowSplashScreen", m_toggleSplashAction->isChecked());
679
        config->writeEntry("StartDocked", m_startDocked);
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 709 710 711 712
    if(!m_shuttingDown &&
       !kapp->sessionSaving() &&
       m_systemTray &&
       m_toggleDockOnCloseAction->isChecked())
    {
713
	KMessageBox::information(this,
Scott Wheeler's avatar
Scott Wheeler committed
714 715 716
	    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");
717 718 719 720 721 722 723
	hide();
	return false;
    }
    else
	return true;
}

724 725 726
QString JuK::playingString() const
{
    QString s;
727 728
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
729

730 731 732 733
    if(!m_splitter->playingArtist().isEmpty())
	s = m_splitter->playingArtist().simplifyWhiteSpace() + " - ";

    s += m_splitter->playingTrack().simplifyWhiteSpace();
734 735 736 737

    return s;
}

738 739
void JuK::updatePlaylistInfo()
{
Scott Wheeler's avatar
Scott Wheeler committed
740 741
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(),
				   m_splitter->selectedPlaylistCount());
742 743 744 745
}

void JuK::play(const QString &file)
{
746
    if(!m_player || !m_sliderAction || !m_sliderAction->getVolumeSlider())
747
        return;
748

Scott Wheeler's avatar
Scott Wheeler committed
749 750
    float volume = float(m_sliderAction->getVolumeSlider()->value()) /
	float(m_sliderAction->getVolumeSlider()->maxValue());
751 752

    if(m_player->paused())
753
        m_player->stop();
754

755 756 757 758 759
    m_player->play(file, volume);

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

    if(m_player->playing()) {
760 761 762
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
763

764
        m_backAction->setEnabled(true);
765

766 767 768
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
769

770
        m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
771

772
        emit signalNewSong(playingString());
773

774 775
        if(m_systemTray)
            m_systemTray->slotPlay();
776 777
    }
    else
778
        stop();
779 780
}

Scott Wheeler's avatar
Scott Wheeler committed
781 782 783
KAction *JuK::createSplitterAction(const QString &text, const char *slot,
				   const char *name, const QString &pix,
				   const KShortcut &shortcut)
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));
793

794 795 796
    return action;
}

797 798 799 800
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

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

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

829 830 831
    updatePlaylistInfo();
}

832 833 834 835
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

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

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

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

859
////////////////////////////////////////////////////////////////////////////////
860
// additional player slots
861 862
////////////////////////////////////////////////////////////////////////////////

863
void JuK::slotTrackPositionSliderClicked()
864
{
865
    m_trackPositionDragging = true;
866 867
}

868
void JuK::slotTrackPositionSliderReleased()
869
{
870
    if(!m_player)
871 872
	return;

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

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

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

    // 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
888 889
	float positionFraction = float(position) /
	    m_sliderAction->getTrackPositionSlider()->maxValue();
890 891 892 893 894
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

	m_statusLabel->setItemCurrentTime(seekTime);
    }
895 896
}

897 898
// This method is called when the play timer has expired.

899
void JuK::slotPollPlay()
900
{
901
    if(!m_player)
902 903
	return;

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

908
    if(!m_player->playing()) {
909

910
        m_playTimer->stop();
911

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

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

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

933
    m_noSeek = false;
934 935
}

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

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

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

959
#include "juk.moc"