juk.cpp 29.9 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 51 52 53 54

    if(m_showSplash) {
	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
    /* 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
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    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("R&emove"), SLOT(slotDeletePlaylist()), "deleteItemPlaylist", "edittrash");
    createSplitterAction(
	i18n("Reload"), SLOT(slotReloadPlaylist()), "reloadPlaylist", "reload");
324

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

327
    //////////////////////////////////////////////////
328
    // edit menu
329 330
    //////////////////////////////////////////////////

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

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

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

341
    //////////////////////////////////////////////////
342
    // view menu
343
    //////////////////////////////////////////////////
344

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

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

354
    //////////////////////////////////////////////////
355
    // play menu
356 357
    //////////////////////////////////////////////////

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

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

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

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

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

385
    //////////////////////////////////////////////////
386
    // tagger menu
387 388
    //////////////////////////////////////////////////

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

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

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

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

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

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

    //////////////////////////////////////////////////
412
    // settings menu
413 414
    //////////////////////////////////////////////////

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

418
    setStandardToolBarMenuEnabled(true);
419

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

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

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

Scott Wheeler's avatar
Scott Wheeler committed
435 436 437

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

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

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

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

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

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

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

    createGUI();
459 460

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

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

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

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

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

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

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

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

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

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

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

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

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

544

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

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

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

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

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

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

581 582 583 584 585 586 587 588 589
/**
 * 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
590
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
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

        bool showHistory = config->readBoolEntry("ShowHistory", false);
        m_showHistoryAction->setChecked(showHistory);
        m_splitter->slotSetHistoryVisible(showHistory);
627
    }
628 629
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
630

Frerich Raabe's avatar
Frerich Raabe committed
631 632
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
633

Frerich Raabe's avatar
Frerich Raabe committed
634 635
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
636

Frerich Raabe's avatar
Frerich Raabe committed
637 638
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
639

Frerich Raabe's avatar
Frerich Raabe committed
640 641
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
642
    }
643

644
    m_toggleSplashAction->setChecked(m_showSplash);
645 646 647 648
}

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

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

678
bool JuK::queryExit()
679
{
680 681 682 683
    hide();
    delete m_systemTray;
    m_systemTray = 0;

684
    stop();
685
    delete m_player;
686

687
    Cache::instance()->save();
688
    saveConfig();
689
    delete m_splitter;
690
    return true;
691 692
}

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

707 708 709
QString JuK::playingString() const
{
    QString s;
710 711
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
712

713 714 715 716
    if(!m_splitter->playingArtist().isEmpty())
	s = m_splitter->playingArtist().simplifyWhiteSpace() + " - ";

    s += m_splitter->playingTrack().simplifyWhiteSpace();
717 718 719 720

    return s;
}

721 722
void JuK::updatePlaylistInfo()
{
Scott Wheeler's avatar
Scott Wheeler committed
723 724
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(),
				   m_splitter->selectedPlaylistCount());
725 726 727 728
}

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

Scott Wheeler's avatar
Scott Wheeler committed
732 733
    float volume = float(m_sliderAction->getVolumeSlider()->value()) /
	float(m_sliderAction->getVolumeSlider()->maxValue());
734 735

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
736
        m_player->stop();
737

738 739 740 741 742
    m_player->play(file, volume);

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

    if(m_player->playing()) {
743 744 745
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
746

Frerich Raabe's avatar
Frerich Raabe committed
747
        m_backAction->setEnabled(true);
748

Frerich Raabe's avatar
Frerich Raabe committed
749 750 751
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
752

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

Frerich Raabe's avatar
Frerich Raabe committed
755
        emit signalNewSong(playingString());
756

Frerich Raabe's avatar
Frerich Raabe committed
757 758
        if(m_systemTray)
            m_systemTray->slotPlay();
759 760
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
761
        stop();
762 763
}

Scott Wheeler's avatar
Scott Wheeler committed
764 765 766
KAction *JuK::createSplitterAction(const QString &text, const char *slot,
				   const char *name, const QString &pix,
				   const KShortcut &shortcut)
767 768 769 770 771 772 773 774 775 776 777 778 779
{
    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;
}

780 781 782 783
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

784
void JuK::slotPlaylistChanged()
785
{
786 787 788 789
    if(m_splitter->collectionListSelected() ||
       !m_splitter->hasListSelected() ||
       m_splitter->readOnlyListSelected())
    {
790 791 792
        actionCollection()->action("file_save")->setEnabled(false);
        actionCollection()->action("file_save_as")->setEnabled(false);
        actionCollection()->action("renamePlaylist")->setEnabled(false);
793
        actionCollection()->action("reloadPlaylist")->setEnabled(false);
794
        actionCollection()->action("deleteItemPlaylist")->setEnabled(false);
795 796
    }
    else {
797 798 799 800
        actionCollection()->action("file_save")->setEnabled(true);
        actionCollection()->action("file_save_as")->setEnabled(true);
        actionCollection()->action("renamePlaylist")->setEnabled(true);
        actionCollection()->action("deleteItemPlaylist")->setEnabled(true);
801 802 803 804 805

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
806
    }
807 808 809 810 811
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
    
812 813 814
    updatePlaylistInfo();
}

815 816 817 818
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

819
void JuK::slotShowGenreListEditor()
820 821 822 823
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
824

825
void JuK::slotToggleSystemTray(bool enabled)
826
{
827
    if(enabled && !m_systemTray)
828
	setupSystemTray();
829 830 831
    else if(!enabled && m_systemTray) {
	delete m_systemTray;
	m_systemTray = 0;
832
	m_toggleDockOnCloseAction->setEnabled(false);
833 834 835
    }
}

836
void JuK::slotSetOutput(int output)
837
{
838
    stop();
839 840
    delete m_player;
    m_player = Player::createPlayer(output);
841 842
}

843 844 845 846 847
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

848
////////////////////////////////////////////////////////////////////////////////
849
// additional player slots
850 851
////////////////////////////////////////////////////////////////////////////////

852
void JuK::slotTrackPositionSliderClicked()
853
{
854
    m_trackPositionDragging = true;
855 856
}

857
void JuK::slotTrackPositionSliderReleased()
858
{
859
    if(!m_player)
860 861
	return;

862 863
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
864 865
}

866
void JuK::slotTrackPositionSliderUpdate(int position)
867
{
868
    if(!m_player)
869 870
	return;

871 872
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
873 874 875 876

    // 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
877 878
	float positionFraction = float(position) /
	    m_sliderAction->getTrackPositionSlider()->maxValue();
879 880 881 882 883
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

	m_statusLabel->setItemCurrentTime(seekTime);
    }
884 885
}

886 887
// This method is called when the play timer has expired.

888
void JuK::slotPollPlay()
889
{
890
    if(!m_player)
891 892
	return;

893
    // Our locking mechanism.  Since this method adjusts the play slider, we
894
    // want to make sure that our adjustments
895
    m_noSeek = true;
896

897
    if(!m_player->playing()) {
898

899
        m_playTimer->stop();
900

Scott Wheeler's avatar
Scott Wheeler committed
901 902
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				      m_loopPlaylistAction->isChecked()));
903
    }
904 905 906 907
    else if(!m_trackPositionDragging) {
        m_sliderAction->getTrackPositionSlider()->setValue(m_player->position());
	m_statusLabel->setItemTotalTime(m_player->totalTime());
	m_statusLabel->setItemCurrentTime(m_player->currentTime());
908 909 910
    }

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

Scott Wheeler's avatar
Scott Wheeler committed
915 916 917 918
    if(m_player->playing() &&
       m_player->totalTime() > 0 &&
       float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
    {
919
        m_playTimer->changeInterval(50);
Scott Wheeler's avatar
Scott Wheeler committed
920
    }
921

922
    m_noSeek = false;
923 924
}

925
void JuK::slotSetVolume(int volume)
926
{
927 928 929
    if(m_player && m_sliderAction && m_sliderAction->getVolumeSlider() &&
       m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
930
    {
Scott Wheeler's avatar
Scott Wheeler committed
931 932
        m_player->setVolume(float(volume) /
			    float(m_sliderAction->getVolumeSlider()->maxValue()));
933 934
    }
}
935

936 937 938 939 940 941
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

942
#include "juk.moc"