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

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 96 97 98 99 100
void JuK::slotGuessTagInfo(int i)
{
    int index = m_guessMenu->popupMenu()->indexOf(i);
    m_splitter->slotGuessTagInfo(TagGuesser::Type(index));
}

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

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

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

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

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

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

void JuK::forward()
{
Scott Wheeler's avatar
Scott Wheeler committed
186 187
    play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				  m_loopPlaylistAction->isChecked()));
188 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
}

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

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

257 258 259 260
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

281
    m_splitter->setFocus();
282

283
    resize(750, 500);
284 285
}

286 287
void JuK::setupActions()
{
288
    //////////////////////////////////////////////////
289
    // file menu
290
    //////////////////////////////////////////////////
291

Scott Wheeler's avatar
Scott Wheeler committed
292 293 294
    KActionMenu *newMenu = new KActionMenu(i18n("&New"), "filenew",
					   actionCollection(), "file_new");

295 296 297 298 299 300
    // 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
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
    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");
323

324
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
325

326
    //////////////////////////////////////////////////
327
    // edit menu
328 329
    //////////////////////////////////////////////////

330 331 332 333
    KStdAction::cut(kapp,   SLOT(cut()),   actionCollection());
    KStdAction::copy(kapp,  SLOT(copy()),  actionCollection());
    KStdAction::paste(kapp, SLOT(paste()), actionCollection());
    KStdAction::clear(kapp, SLOT(clear()), actionCollection());
334

335
    KStdAction::selectAll(kapp, SLOT(selectAll()), actionCollection());
336

337
    createSplitterAction(
338
	i18n("Advanced Search"), SLOT(slotAdvancedSearch()), "advancedSearch", "find", "s");
339

340
    //////////////////////////////////////////////////
341
    // view menu
342
    //////////////////////////////////////////////////
343

Scott Wheeler's avatar
Scott Wheeler committed
344 345 346 347
    m_showSearchAction = new KToggleAction(i18n("Show &Search Bar"), "filefind",
					   0, actionCollection(), "showSearch");
    m_showEditorAction = new KToggleAction(i18n("Show &Tag Editor"), "edit",
					   0, actionCollection(), "showEditor");
348
    m_showHistoryAction = new KToggleAction(i18n("Show &History"), "history",
349
					   0, actionCollection(), "showHistory");
350

351
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
352

353
    //////////////////////////////////////////////////
354
    // play menu
355 356
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
357 358 359 360 361 362 363 364 365 366 367 368 369 370
    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");
371

Scott Wheeler's avatar
Scott Wheeler committed
372 373
    connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),
	    this, SLOT(slotPopulateBackMenu()));
374

Scott Wheeler's avatar
Scott Wheeler committed
375 376
    connect(m_backAction->popupMenu(), SIGNAL(activated(int)),
	    this, SLOT(back(int)));
377

Scott Wheeler's avatar
Scott Wheeler committed
378 379
    new KAction(i18n("Skip &Forward"), "player_end", 0, this, SLOT(forward()),
		actionCollection(), "forward");
380

Scott Wheeler's avatar
Scott Wheeler committed
381 382
    m_loopPlaylistAction = new KToggleAction(i18n("&Loop Playlist"), 0, 0,
					     actionCollection(), "loopPlaylist");
383

384
    //////////////////////////////////////////////////
385
    // tagger menu
386 387
    //////////////////////////////////////////////////

388
    createSplitterAction(i18n("&Save"), SLOT(slotSaveTag()),
Scott Wheeler's avatar
Scott Wheeler committed
389 390 391
			 "saveItem",   "filesave", "CTRL+t");
    createSplitterAction(i18n("&Delete"), SLOT(slotDeleteSelectedItems()),
			 "removeItem", "editdelete");
392

393
    m_guessMenu = new KActionMenu(i18n("&Guess Tag Information"),
Scott Wheeler's avatar
Scott Wheeler committed
394 395
					     QString::null, actionCollection(),
					     "guessTag");
396

Scott Wheeler's avatar
Scott Wheeler committed
397
    KAction *a;
398

399 400 401 402 403
    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
404

405
    connect(m_guessMenu->popupMenu(), SIGNAL(activated(int)), this, SLOT(slotGuessTagInfo(int)));
Scott Wheeler's avatar
Scott Wheeler committed
406 407 408

    // new KAction(i18n("&Rename File"), 0, "CTRL+r", m_splitter, SLOT(slotRenameFile()),
    //   actionCollection(), "renameFile"); // 4
409 410

    //////////////////////////////////////////////////
411
    // settings menu
412 413
    //////////////////////////////////////////////////

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

417
    setStandardToolBarMenuEnabled(true);
418

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

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

Scott Wheeler's avatar
Scott Wheeler committed
431 432
    connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
	    this, SLOT(slotToggleSystemTray(bool)));
433

Scott Wheeler's avatar
Scott Wheeler committed
434 435 436

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

438
    m_outputSelectAction = Player::playerSelectAction(actionCollection());
439

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

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

448 449
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

450
    //////////////////////////////////////////////////
451
    // just in the toolbar
452 453
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
454 455
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(),
				      "trackPositionAction");
456 457

    createGUI();
458 459

    // set the slider to the proper orientation and make it stay that way
460
    m_sliderAction->slotUpdateOrientation();
461 462
}

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

481
void JuK::setupSystemTray()
482
{
483
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
Frerich Raabe's avatar
Frerich Raabe committed
484 485
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
486

Scott Wheeler's avatar
Scott Wheeler committed
487 488
        connect(this, SIGNAL(signalNewSong(const QString&)), 
		m_systemTray, SLOT(slotNewSong(const QString&)));
489

Frerich Raabe's avatar
Frerich Raabe committed
490 491 492 493
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
494

Frerich Raabe's avatar
Frerich Raabe committed
495
        m_toggleDockOnCloseAction->setEnabled(true);
496

Frerich Raabe's avatar
Frerich Raabe committed
497
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
498
    }
499
    else {
Frerich Raabe's avatar
Frerich Raabe committed
500 501
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
502
    }
503 504
}

505 506
void JuK::setupPlayer()
{
507 508
    m_trackPositionDragging = false;
    m_noSeek = false;
Scott Wheeler's avatar
Scott Wheeler committed
509
    m_muted = false;
510 511 512 513
    actionCollection()->action("pause")->setEnabled(false);
    actionCollection()->action("stop")->setEnabled(false);
    actionCollection()->action("back")->setEnabled(false);
    actionCollection()->action("forward")->setEnabled(false);
514

515
    m_playTimer = new QTimer(this);
516
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
517

Scott Wheeler's avatar
Scott Wheeler committed
518 519 520 521 522 523 524 525 526 527
    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()));
528
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
529

Scott Wheeler's avatar
Scott Wheeler committed
530 531
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)),
		this, SLOT(slotSetVolume(int)));
532
    }
533

534
    int playerType = 0;
535
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
536 537
        playerType = m_outputSelectAction->currentItem();
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
538 539
    }

540
    m_player = Player::createPlayer(playerType);
541 542
}

543

544 545 546
void JuK::setupGlobalAccels()
{
    m_accel = new KGlobalAccel(this);
547 548 549 550 551 552 553 554 555
    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()));
556

557 558 559 560 561
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

562 563 564 565
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
566

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

570
    m_splitter->open(files);
571 572
}

573 574 575
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
Frerich Raabe's avatar
Frerich Raabe committed
576
        e->accept();
577 578 579
    KMainWindow::keyPressEvent(e);
}

580 581 582 583 584 585 586 587 588
/**
 * 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
589
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
590
        m_startDocked = config->readBoolEntry("StartDocked", false);
591 592 593
    }
}

594 595
void JuK::readConfig()
{
596
    // Automagically save and m_restore many window settings.
597 598
    setAutoSaveSettings();

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

Frerich Raabe's avatar
Frerich Raabe committed
610 611
        bool loopPlaylist = config->readBoolEntry("LoopPlaylist", false);
        m_loopPlaylistAction->setChecked(loopPlaylist);
612
    }
613
    { // view settings
614
        KConfigGroupSaver saver(config, "View");
615 616 617 618 619

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

Frerich Raabe's avatar
Frerich Raabe committed
620 621 622
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
623

624 625 626 627 628 629
	// 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);
630
    }
631 632
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
633

Frerich Raabe's avatar
Frerich Raabe committed
634 635
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
636

Frerich Raabe's avatar
Frerich Raabe committed
637 638
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
639

Frerich Raabe's avatar
Frerich Raabe committed
640 641
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
642

Frerich Raabe's avatar
Frerich Raabe committed
643 644
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
645
    }
646

647
    m_toggleSplashAction->setChecked(m_showSplash);
648 649 650 651
}

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

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

682
bool JuK::queryExit()
683
{
684 685
    m_startDocked = !isVisible();

686 687 688 689
    hide();
    delete m_systemTray;
    m_systemTray = 0;

690
    stop();
691
    delete m_player;
692

693
    Cache::instance()->save();
694
    saveConfig();
695
    delete m_splitter;
696
    return true;
697 698
}

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

713 714 715
QString JuK::playingString() const
{
    QString s;
716 717
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
718

719 720 721 722
    if(!m_splitter->playingArtist().isEmpty())
	s = m_splitter->playingArtist().simplifyWhiteSpace() + " - ";

    s += m_splitter->playingTrack().simplifyWhiteSpace();
723 724 725 726

    return s;
}

727 728
void JuK::updatePlaylistInfo()
{
Scott Wheeler's avatar
Scott Wheeler committed
729 730
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(),
				   m_splitter->selectedPlaylistCount());
731 732 733 734
}

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

Scott Wheeler's avatar
Scott Wheeler committed
738 739
    float volume = float(m_sliderAction->getVolumeSlider()->value()) /
	float(m_sliderAction->getVolumeSlider()->maxValue());
740 741

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
742
        m_player->stop();
743

744 745 746 747 748
    m_player->play(file, volume);

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

    if(m_player->playing()) {
749 750 751
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
752

Frerich Raabe's avatar
Frerich Raabe committed
753
        m_backAction->setEnabled(true);
754

Frerich Raabe's avatar
Frerich Raabe committed
755 756 757
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
758

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

Frerich Raabe's avatar
Frerich Raabe committed
761
        emit signalNewSong(playingString());
762

Frerich Raabe's avatar
Frerich Raabe committed
763 764
        if(m_systemTray)
            m_systemTray->slotPlay();
765 766
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
767
        stop();
768 769
}

Scott Wheeler's avatar
Scott Wheeler committed
770 771 772
KAction *JuK::createSplitterAction(const QString &text, const char *slot,
				   const char *name, const QString &pix,
				   const KShortcut &shortcut)
773 774 775 776 777 778 779 780 781 782 783 784 785
{
    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;
}

786 787 788 789
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

790
void JuK::slotPlaylistChanged()
791
{
792 793 794 795
    if(m_splitter->collectionListSelected() ||
       !m_splitter->hasListSelected() ||
       m_splitter->readOnlyListSelected())
    {
796 797 798
        actionCollection()->action("file_save")->setEnabled(false);
        actionCollection()->action("file_save_as")->setEnabled(false);
        actionCollection()->action("renamePlaylist")->setEnabled(false);
799
        actionCollection()->action("reloadPlaylist")->setEnabled(false);
800
        actionCollection()->action("deleteItemPlaylist")->setEnabled(false);
801 802
    }
    else {
803 804 805 806
        actionCollection()->action("file_save")->setEnabled(true);
        actionCollection()->action("file_save_as")->setEnabled(true);
        actionCollection()->action("renamePlaylist")->setEnabled(true);
        actionCollection()->action("deleteItemPlaylist")->setEnabled(true);
807 808 809 810 811

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
812
    }
813 814 815 816 817
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
    
818 819 820
    updatePlaylistInfo();
}

821 822 823 824
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

825
void JuK::slotShowGenreListEditor()
826 827 828 829
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
830

831
void JuK::slotToggleSystemTray(bool enabled)
832
{
833
    if(enabled && !m_systemTray)
834
	setupSystemTray();
835 836 837
    else if(!enabled && m_systemTray) {
	delete m_systemTray;
	m_systemTray = 0;
838
	m_toggleDockOnCloseAction->setEnabled(false);
839 840 841
    }
}

842
void JuK::slotSetOutput(int output)
843
{
844
    stop();
845 846
    delete m_player;
    m_player = Player::createPlayer(output);
847 848
}

849 850 851 852 853
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

854
////////////////////////////////////////////////////////////////////////////////
855
// additional player slots
856 857
////////////////////////////////////////////////////////////////////////////////

858
void JuK::slotTrackPositionSliderClicked()
859
{
860
    m_trackPositionDragging = true;
861 862
}

863
void JuK::slotTrackPositionSliderReleased()
864
{
865
    if(!m_player)
866 867
	return;

868 869
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
870 871
}

872
void JuK::slotTrackPositionSliderUpdate(int position)
873
{
874
    if(!m_player)
875 876
	return;

877 878
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
879 880 881 882

    // 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
883 884
	float positionFraction = float(position) /
	    m_sliderAction->getTrackPositionSlider()->maxValue();
885 886 887 888 889
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

	m_statusLabel->setItemCurrentTime(seekTime);
    }
890 891
}

892 893
// This method is called when the play timer has expired.

894
void JuK::slotPollPlay()
895
{
896
    if(!m_player)
897 898
	return;

899
    // Our locking mechanism.  Since this method adjusts the play slider, we
900
    // want to make sure that our adjustments
901
    m_noSeek = true;
902

903
    if(!m_player->playing()) {
904

905
        m_playTimer->stop();
906

Scott Wheeler's avatar
Scott Wheeler committed
907 908
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				      m_loopPlaylistAction->isChecked()));
909
    }
910 911 912 913
    else if(!m_trackPositionDragging) {
        m_sliderAction->getTrackPositionSlider()->setValue(m_player->position());
	m_statusLabel->setItemTotalTime(m_player->totalTime());
	m_statusLabel->setItemCurrentTime(m_player->currentTime());
914 915 916
    }

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

Scott Wheeler's avatar
Scott Wheeler committed
921 922 923 924
    if(m_player->playing() &&
       m_player->totalTime() > 0 &&
       float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
    {
925
        m_playTimer->changeInterval(50);
Scott Wheeler's avatar
Scott Wheeler committed
926
    }
927

928
    m_noSeek = false;
929 930
}

931
void JuK::slotSetVolume(int volume)
932
{
933 934 935
    if(m_player && m_sliderAction && m_sliderAction->getVolumeSlider() &&
       m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
936
    {
Scott Wheeler's avatar
Scott Wheeler committed
937 938
        m_player->setVolume(float(volume) /
			    float(m_sliderAction->getVolumeSlider()->maxValue()));
939 940
    }
}
941

942 943 944 945 946 947
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

948
#include "juk.moc"