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 407 408 409
    a = new KAction(i18n("From &Filename"), "CTRL+f", actionCollection(), "guessTagFile");
    m_guessMenu->insert(a);

    a = new KAction(i18n("From &Internet"), "CTRL+i", actionCollection(), "guessTagInternet");
    m_guessMenu->insert(a);
Scott Wheeler's avatar
Scott Wheeler committed
410

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

413
    //////////////////////////////////////////////////
414
    // settings menu
415 416
    //////////////////////////////////////////////////

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

420
    setStandardToolBarMenuEnabled(true);
421

422
    m_toggleSplashAction = new KToggleAction(i18n("Show Splash Screen on Startp"),
Scott Wheeler's avatar
Scott Wheeler committed
423 424 425 426 427 428 429
					     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");
430

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

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

Scott Wheeler's avatar
Scott Wheeler committed
437 438 439

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

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

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

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

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

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

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

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

    createGUI();
464 465

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

469 470 471 472 473 474
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
475 476 477 478
    connect(m_showSearchAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetSearchVisible(bool)));
    connect(m_showEditorAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetEditorVisible(bool)));
479 480
    connect(m_showHistoryAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetHistoryVisible(bool)));
Scott Wheeler's avatar
Scott Wheeler committed
481 482 483 484
    connect(this, SIGNAL(dockWindowPositionChanged(QDockWindow *)), 
	    m_sliderAction, SLOT(slotUpdateOrientation(QDockWindow *)));
    connect(m_splitter, SIGNAL(signalPlaylistChanged()),
	    this, SLOT(slotPlaylistChanged()));
485 486
}

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

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

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

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

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

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

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

Scott Wheeler's avatar
Scott Wheeler committed
524 525 526 527 528 529 530 531 532 533
    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()));
534
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
535

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

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

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

549

550 551 552
void JuK::setupGlobalAccels()
{
    m_accel = new KGlobalAccel(this);
553 554 555 556 557 558 559 560 561
    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()));
562

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

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

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

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

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

586 587 588 589 590 591 592 593 594
/**
 * 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
595
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
596
        m_startDocked = config->readBoolEntry("StartDocked", false);
597 598 599
    }
}

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

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

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

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

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

630 631 632 633 634 635
	// 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);
636
    }
637 638
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
639

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

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

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

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

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

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

Frerich Raabe's avatar
Frerich Raabe committed
671
        config->writeEntry("ShowEditor", m_showEditorAction->isChecked());
672
        config->writeEntry("ShowSearch", m_showSearchAction->isChecked());
673
	config->writeEntry("ShowHistory", m_showHistoryAction->isChecked());
674
    }
675 676
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
Frerich Raabe's avatar
Frerich Raabe committed
677
        config->writeEntry("ShowSplashScreen", m_toggleSplashAction->isChecked());
678
        config->writeEntry("StartDocked", m_startDocked);
Frerich Raabe's avatar
Frerich Raabe committed
679 680 681 682 683
        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());
684
    }
685
    config->sync();
686 687
}

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

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

696
    stop();
697
    delete m_player;
698

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

705 706
bool JuK::queryClose()
{
707
    if(!m_shuttingDown && m_systemTray && m_toggleDockOnCloseAction->isChecked()) {
708
	KMessageBox::information(this,
Scott Wheeler's avatar
Scott Wheeler committed
709 710 711
	    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");
712 713 714 715 716 717 718
	hide();
	return false;
    }
    else
	return true;
}

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

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

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

    return s;
}

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

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

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

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

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

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

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

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

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

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

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

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

Scott Wheeler's avatar
Scott Wheeler committed
776 777 778
KAction *JuK::createSplitterAction(const QString &text, const char *slot,
				   const char *name, const QString &pix,
				   const KShortcut &shortcut)
779 780 781 782 783 784 785 786 787 788 789 790 791
{
    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;
}

792 793 794 795
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

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

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

827 828 829 830
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

860
////////////////////////////////////////////////////////////////////////////////
861
// additional player slots
862 863
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

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

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

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

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

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

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

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

911
        m_playTimer->stop();
912

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

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

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

934
    m_noSeek = false;
935 936
}

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

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

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

960
#include "juk.moc"