juk.cpp 30.6 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
void JuK::slotGuessTagInfoFromFile()
97
{
98 99 100 101 102 103
    m_splitter->slotGuessTagInfo(TagGuesser::FileName);
}

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

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

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

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

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

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

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

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

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

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

262 263 264 265
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

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

291
    m_splitter->setFocus();
292

293
    resize(750, 500);
294 295
}

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

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

305 306 307 308 309 310
    // 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
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    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");
333

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

336
    //////////////////////////////////////////////////
337
    // edit menu
338 339
    //////////////////////////////////////////////////

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

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

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

350
    //////////////////////////////////////////////////
351
    // view menu
352
    //////////////////////////////////////////////////
353

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

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

363
    //////////////////////////////////////////////////
364
    // play menu
365 366
    //////////////////////////////////////////////////

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

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

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

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

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

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

394
    //////////////////////////////////////////////////
395
    // tagger menu
396 397
    //////////////////////////////////////////////////

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

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

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

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

418
    //////////////////////////////////////////////////
419
    // settings menu
420 421
    //////////////////////////////////////////////////

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

425
    setStandardToolBarMenuEnabled(true);
426

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

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

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

Scott Wheeler's avatar
Scott Wheeler committed
442 443 444

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

446
    m_outputSelectAction = Player::playerSelectAction(actionCollection());
447

448
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
449 450
        m_outputSelectAction->setCurrentItem(0);
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
451
    }
452

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

456 457 458
    new KAction(i18n("&File Renamer..."), 0, 0, this, SLOT(slotConfigureFileRenamer()),
		actionCollection(), "fileRenamerConfig");

459 460
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

461
    //////////////////////////////////////////////////
462
    // just in the toolbar
463 464
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
465 466
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(),
				      "trackPositionAction");
467 468

    createGUI();
469 470

    // set the slider to the proper orientation and make it stay that way
471
    m_sliderAction->slotUpdateOrientation();
472 473
}

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

492
void JuK::setupSystemTray()
493
{
494
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
Frerich Raabe's avatar
Frerich Raabe committed
495 496
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
497

498
        connect(this, SIGNAL(signalNewSong(const QString&)),
Scott Wheeler's avatar
Scott Wheeler committed
499
		m_systemTray, SLOT(slotNewSong(const QString&)));
500

Frerich Raabe's avatar
Frerich Raabe committed
501 502 503 504
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
505

Frerich Raabe's avatar
Frerich Raabe committed
506
        m_toggleDockOnCloseAction->setEnabled(true);
507

Frerich Raabe's avatar
Frerich Raabe committed
508
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
509
    }
510
    else {
Frerich Raabe's avatar
Frerich Raabe committed
511 512
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
513
    }
514 515
}

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

526
    m_playTimer = new QTimer(this);
527
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
528

Scott Wheeler's avatar
Scott Wheeler committed
529 530 531 532 533 534 535 536 537 538
    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()));
539
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
540

Scott Wheeler's avatar
Scott Wheeler committed
541 542
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)),
		this, SLOT(slotSetVolume(int)));
543
    }
544

545
    int playerType = 0;
546
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
547 548
        playerType = m_outputSelectAction->currentItem();
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
549 550
    }

551
    m_player = Player::createPlayer(playerType);
552 553
}

554

555 556 557
void JuK::setupGlobalAccels()
{
    m_accel = new KGlobalAccel(this);
558 559 560 561 562 563 564 565 566
    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()));
567

568 569 570 571 572
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

573 574 575 576
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
577

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

581
    m_splitter->open(files);
582 583
}

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

591 592 593 594 595 596 597 598 599
/**
 * 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
600
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
601
        m_startDocked = config->readBoolEntry("StartDocked", false);
602 603 604
    }
}

605 606
void JuK::readConfig()
{
607
    // Automagically save and m_restore many window settings.
608 609
    setAutoSaveSettings();

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

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

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

Frerich Raabe's avatar
Frerich Raabe committed
631 632 633
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
634

635 636 637
	// 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.
638

639 640
	bool showHistory = config->readBoolEntry("ShowHistory", false);
	m_showHistoryAction->setChecked(showHistory);
641
    }
642 643
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
644

Frerich Raabe's avatar
Frerich Raabe committed
645 646
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
647

Frerich Raabe's avatar
Frerich Raabe committed
648 649
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
650

Frerich Raabe's avatar
Frerich Raabe committed
651 652
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
653

Frerich Raabe's avatar
Frerich Raabe committed
654 655
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
656
    }
657

658
    m_toggleSplashAction->setChecked(m_showSplash);
659 660 661 662
}

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

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

693
bool JuK::queryExit()
694
{
695 696
    m_startDocked = !isVisible();

697 698 699 700
    hide();
    delete m_systemTray;
    m_systemTray = 0;

701
    stop();
702
    delete m_player;
703

704
    Cache::instance()->save();
705
    saveConfig();
706
    delete m_splitter;
707
    return true;
708 709
}

710 711
bool JuK::queryClose()
{
712
    if(!m_shuttingDown && 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())
Frerich Raabe's avatar
Frerich Raabe committed
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())
Frerich Raabe's avatar
Frerich Raabe committed
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

Frerich Raabe's avatar
Frerich Raabe committed
764
        m_backAction->setEnabled(true);
765

Frerich Raabe's avatar
Frerich Raabe committed
766 767 768
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
769

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

Frerich Raabe's avatar
Frerich Raabe committed
772
        emit signalNewSong(playingString());
773

Frerich Raabe's avatar
Frerich Raabe committed
774 775
        if(m_systemTray)
            m_systemTray->slotPlay();
776 777
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
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::slotShowGenreListEditor()
837 838 839 840
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
841

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

853
void JuK::slotSetOutput(int output)
854
{
855
    stop();
856 857
    delete m_player;
    m_player = Player::createPlayer(output);
858 859
}

860 861 862 863 864
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

865
////////////////////////////////////////////////////////////////////////////////
866
// additional player slots
867 868
////////////////////////////////////////////////////////////////////////////////

869
void JuK::slotTrackPositionSliderClicked()
870
{
871
    m_trackPositionDragging = true;
872 873
}

874
void JuK::slotTrackPositionSliderReleased()
875
{
876
    if(!m_player)
877 878
	return;

879 880
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
881 882
}

883
void JuK::slotTrackPositionSliderUpdate(int position)
884
{
885
    if(!m_player)
886 887
	return;

888 889
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
890 891 892 893

    // 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
894 895
	float positionFraction = float(position) /
	    m_sliderAction->getTrackPositionSlider()->maxValue();
896 897 898 899 900
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

	m_statusLabel->setItemCurrentTime(seekTime);
    }
901 902
}

903 904
// This method is called when the play timer has expired.

905
void JuK::slotPollPlay()
906
{
907
    if(!m_player)
908 909
	return;

910
    // Our locking mechanism.  Since this method adjusts the play slider, we
911
    // want to make sure that our adjustments
912
    m_noSeek = true;
913

914
    if(!m_player->playing()) {
915

916
        m_playTimer->stop();
917

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

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

Scott Wheeler's avatar
Scott Wheeler committed
932 933 934 935
    if(m_player->playing() &&
       m_player->totalTime() > 0 &&
       float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
    {
936
        m_playTimer->changeInterval(50);
Scott Wheeler's avatar
Scott Wheeler committed
937
    }
938

939
    m_noSeek = false;
940 941
}

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

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

959 960 961 962 963 964
void JuK::slotConfigureFileRenamer()
{
    FileRenamerConfigDlg dlg(this);
    dlg.exec();
}

965
#include "juk.moc"