juk.cpp 30 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
#include <kcmdlineargs.h>
19
#include <kstatusbar.h>
20
#include <kdebug.h>
21
#include <kmessagebox.h>
22

Frerich Raabe's avatar
Frerich Raabe committed
23
#include <qmetaobject.h>
24
#include <qslider.h>
25
#include <qmime.h>
26

27
#include "juk.h"
28
#include "slideraction.h"
29
#include "statuslabel.h"
30 31
#include "splashscreen.h"
#include "genrelisteditor.h"
32
#include "systemtray.h"
33
#include "keydialog.h"
34
#include "tagguesserconfigdlg.h"
35 36 37 38 39

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

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

47
    readSettings();
48 49 50 51 52 53

    if(m_showSplash) {
	SplashScreen::instance()->show();
	kapp->processEvents();
    }

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

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

JuK::~JuK()
{
70

71 72
}

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

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

94 95 96 97 98 99
void JuK::slotGuessTagInfo(int i)
{
    int index = m_guessMenu->popupMenu()->indexOf(i);
    m_splitter->slotGuessTagInfo(TagGuesser::Type(index));
}

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

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

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

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

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

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

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

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

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

256 257 258 259
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

280
    m_splitter->setFocus();
281

282
    resize(750, 500);
283 284
}

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

Scott Wheeler's avatar
Scott Wheeler committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
    KActionMenu *newMenu = new KActionMenu(i18n("&New"), "filenew",
					   actionCollection(), "file_new");

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

319
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
320

321
    //////////////////////////////////////////////////
322
    // edit menu
323 324 325 326
    //////////////////////////////////////////////////

    KStdAction::cut(this,   SLOT(cut()),   actionCollection());
    KStdAction::copy(this,  SLOT(copy()),  actionCollection());
327
    KStdAction::paste(this, SLOT(paste()), actionCollection());
328

Scott Wheeler's avatar
Scott Wheeler committed
329 330
    new KAction(i18n("C&lear"), "editclear", 0, this, SLOT(clear()),
		actionCollection(), "clear");
331

332
    KStdAction::selectAll(this, SLOT(selectAll()), actionCollection());
333

334
    //////////////////////////////////////////////////
335
    // view menu
336
    //////////////////////////////////////////////////
337

Scott Wheeler's avatar
Scott Wheeler committed
338 339 340 341
    m_showSearchAction = new KToggleAction(i18n("Show &Search Bar"), "filefind",
					   0, actionCollection(), "showSearch");
    m_showEditorAction = new KToggleAction(i18n("Show &Tag Editor"), "edit",
					   0, actionCollection(), "showEditor");
342

343
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
344

345
    //////////////////////////////////////////////////
346
    // play menu
347 348
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362
    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");
363

Scott Wheeler's avatar
Scott Wheeler committed
364 365
    connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),
	    this, SLOT(slotPopulateBackMenu()));
366

Scott Wheeler's avatar
Scott Wheeler committed
367 368
    connect(m_backAction->popupMenu(), SIGNAL(activated(int)),
	    this, SLOT(back(int)));
369

Scott Wheeler's avatar
Scott Wheeler committed
370 371
    new KAction(i18n("Skip &Forward"), "player_end", 0, this, SLOT(forward()),
		actionCollection(), "forward");
372

Scott Wheeler's avatar
Scott Wheeler committed
373 374
    m_loopPlaylistAction = new KToggleAction(i18n("&Loop Playlist"), 0, 0,
					     actionCollection(), "loopPlaylist");
375

376
    //////////////////////////////////////////////////
377
    // tagger menu
378 379
    //////////////////////////////////////////////////

380
    createSplitterAction(i18n("&Save"), SLOT(slotSaveTag()),
Scott Wheeler's avatar
Scott Wheeler committed
381 382 383
			 "saveItem",   "filesave", "CTRL+t");
    createSplitterAction(i18n("&Delete"), SLOT(slotDeleteSelectedItems()),
			 "removeItem", "editdelete");
384

385
    m_guessMenu = new KActionMenu(i18n("&Guess Tag Information"),
Scott Wheeler's avatar
Scott Wheeler committed
386 387
					     QString::null, actionCollection(),
					     "guessTag");
388

Scott Wheeler's avatar
Scott Wheeler committed
389
    KAction *a;
390

391 392 393 394 395
    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
396

397
    connect(m_guessMenu->popupMenu(), SIGNAL(activated(int)), this, SLOT(slotGuessTagInfo(int)));
Scott Wheeler's avatar
Scott Wheeler committed
398 399 400

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

    //////////////////////////////////////////////////
403
    // settings menu
404 405
    //////////////////////////////////////////////////

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

409
    setStandardToolBarMenuEnabled(true);
410

411
    m_toggleSplashAction = new KToggleAction(i18n("Show Splash Screen on Startp"),
Scott Wheeler's avatar
Scott Wheeler committed
412 413 414 415 416 417 418
					     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");
419

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

Scott Wheeler's avatar
Scott Wheeler committed
423 424
    connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
	    this, SLOT(slotToggleSystemTray(bool)));
425

Scott Wheeler's avatar
Scott Wheeler committed
426 427 428

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

430
    m_outputSelectAction = Player::playerSelectAction(actionCollection());
431

432
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
433 434
        m_outputSelectAction->setCurrentItem(0);
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
435
    }
436

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

440 441
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

442
    //////////////////////////////////////////////////
443
    // just in the toolbar
444 445
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
446 447
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(),
				      "trackPositionAction");
448 449

    createGUI();
450 451

    // set the slider to the proper orientation and make it stay that way
452
    m_sliderAction->slotUpdateOrientation();
453 454
}

455 456 457 458 459 460
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
461 462 463 464 465 466 467 468
    connect(m_showSearchAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetSearchVisible(bool)));
    connect(m_showEditorAction, SIGNAL(toggled(bool)),
	    m_splitter, SLOT(slotSetEditorVisible(bool)));
    connect(this, SIGNAL(dockWindowPositionChanged(QDockWindow *)), 
	    m_sliderAction, SLOT(slotUpdateOrientation(QDockWindow *)));
    connect(m_splitter, SIGNAL(signalPlaylistChanged()),
	    this, SLOT(slotPlaylistChanged()));
469 470
}

471
void JuK::setupSystemTray()
472
{
473
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
Frerich Raabe's avatar
Frerich Raabe committed
474 475
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
476

Scott Wheeler's avatar
Scott Wheeler committed
477 478
        connect(this, SIGNAL(signalNewSong(const QString&)), 
		m_systemTray, SLOT(slotNewSong(const QString&)));
479

Frerich Raabe's avatar
Frerich Raabe committed
480 481 482 483
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
484

Frerich Raabe's avatar
Frerich Raabe committed
485
        m_toggleDockOnCloseAction->setEnabled(true);
486

Frerich Raabe's avatar
Frerich Raabe committed
487
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
488
    }
489
    else {
Frerich Raabe's avatar
Frerich Raabe committed
490 491
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
492
    }
493 494
}

495 496
void JuK::setupPlayer()
{
497 498
    m_trackPositionDragging = false;
    m_noSeek = false;
Scott Wheeler's avatar
Scott Wheeler committed
499
    m_muted = false;
500 501 502 503
    actionCollection()->action("pause")->setEnabled(false);
    actionCollection()->action("stop")->setEnabled(false);
    actionCollection()->action("back")->setEnabled(false);
    actionCollection()->action("forward")->setEnabled(false);
504

505
    m_playTimer = new QTimer(this);
506
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
507

Scott Wheeler's avatar
Scott Wheeler committed
508 509 510 511 512 513 514 515 516 517
    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()));
518
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
519

Scott Wheeler's avatar
Scott Wheeler committed
520 521
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)),
		this, SLOT(slotSetVolume(int)));
522
    }
523

524
    int playerType = 0;
525
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
526 527
        playerType = m_outputSelectAction->currentItem();
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
528 529
    }

530
    m_player = Player::createPlayer(playerType);
531 532
}

533

534 535 536
void JuK::setupGlobalAccels()
{
    m_accel = new KGlobalAccel(this);
537 538 539 540 541 542 543 544 545
    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()));
546

547 548 549 550 551
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

552 553 554 555
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
556

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

560
    m_splitter->open(files);
561 562
}

563 564 565
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
Frerich Raabe's avatar
Frerich Raabe committed
566
        e->accept();
567 568 569
    KMainWindow::keyPressEvent(e);
}

570 571 572 573 574 575 576 577 578
/**
 * 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
579
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
580 581 582
    }
}

583 584
void JuK::readConfig()
{
585
    // Automagically save and m_restore many window settings.
586 587
    setAutoSaveSettings();

588
    KConfig *config = KGlobal::config();
589
    { // player settings
590
        KConfigGroupSaver saver(config, "Player");
591
        if(m_sliderAction->getVolumeSlider()) {
Scott Wheeler's avatar
Scott Wheeler committed
592 593
	    int maxVolume = m_sliderAction->getVolumeSlider()->maxValue();
	    int volume = config->readNumEntry("Volume", maxVolume);
594
            m_sliderAction->getVolumeSlider()->setValue(volume);
595
        }
Frerich Raabe's avatar
Frerich Raabe committed
596 597
        bool randomPlay = config->readBoolEntry("RandomPlay", false);
        m_randomPlayAction->setChecked(randomPlay);
598

Frerich Raabe's avatar
Frerich Raabe committed
599 600
        bool loopPlaylist = config->readBoolEntry("LoopPlaylist", false);
        m_loopPlaylistAction->setChecked(loopPlaylist);
601
    }
602
    { // view settings
603
        KConfigGroupSaver saver(config, "View");
604 605 606 607 608

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

Frerich Raabe's avatar
Frerich Raabe committed
609 610 611
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
612
    }
613 614
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
615

Frerich Raabe's avatar
Frerich Raabe committed
616 617
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
618

Frerich Raabe's avatar
Frerich Raabe committed
619 620
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
621

Frerich Raabe's avatar
Frerich Raabe committed
622 623
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
624

Frerich Raabe's avatar
Frerich Raabe committed
625 626
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
627
    }
628

629
    m_toggleSplashAction->setChecked(m_showSplash);
630 631 632 633
}

void JuK::saveConfig()
{
634
    KConfig *config = KGlobal::config();
635
    { // m_player settings
636
        KConfigGroupSaver saver(config, "Player");
637 638
        if(m_sliderAction && m_sliderAction->getVolumeSlider())
            config->writeEntry("Volume", m_sliderAction->getVolumeSlider()->value());
Frerich Raabe's avatar
Frerich Raabe committed
639 640 641 642
        if(m_randomPlayAction)
            config->writeEntry("RandomPlay", m_randomPlayAction->isChecked());
        if(m_loopPlaylistAction)
            config->writeEntry("LoopPlaylist", m_loopPlaylistAction->isChecked());
643
    }
644 645
    { // view settings
        KConfigGroupSaver saver(config, "View");
646

Frerich Raabe's avatar
Frerich Raabe committed
647
        config->writeEntry("ShowEditor", m_showEditorAction->isChecked());
648
        config->writeEntry("ShowSearch", m_showSearchAction->isChecked());
649
    }
650 651
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
Frerich Raabe's avatar
Frerich Raabe committed
652 653 654 655 656 657
        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());
658
    }
659
    config->sync();
660 661
}

662
bool JuK::queryExit()
663
{
664
    stop();
665
    delete m_player;
666
    Cache::instance()->save();
667
    saveConfig();
668
    delete m_splitter;
669
    return true;
670 671
}

672 673
bool JuK::queryClose()
{
674
    if(!m_shuttingDown && m_systemTray && m_toggleDockOnCloseAction->isChecked()) {
675
	KMessageBox::information(this,
Scott Wheeler's avatar
Scott Wheeler committed
676 677 678
	    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");
679 680 681 682 683 684 685
	hide();
	return false;
    }
    else
	return true;
}

686
void JuK::invokeEditSlot(const char *slotName, const char *slot)
687 688
{
    QObject *object = focusWidget();
689

690
    if(!object || !slotName || !slot)
Frerich Raabe's avatar
Frerich Raabe committed
691
        return;
692

693 694
    QMetaObject *meta = object->metaObject();
    QStrList l = meta->slotNames(true);
695

696
    if(l.find(slotName) == -1)
Frerich Raabe's avatar
Frerich Raabe committed
697
        return;
698

699 700 701
    connect(this, SIGNAL(signalEdit()), object, slot);
    emit signalEdit();
    disconnect(this, SIGNAL(signalEdit()), object, slot);
702 703
}

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

710
    if(m_splitter->playingArtist().isEmpty())
Frerich Raabe's avatar
Frerich Raabe committed
711
        s = m_splitter->playingTrack().simplifyWhiteSpace();
712
    else
Scott Wheeler's avatar
Scott Wheeler committed
713 714
        s = m_splitter->playingArtist().simplifyWhiteSpace() + " - " +
	    m_splitter->playingTrack().simplifyWhiteSpace();
715 716 717 718

    return s;
}

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

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

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

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
734
        m_player->stop();
735

736 737 738 739 740
    m_player->play(file, volume);

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

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

Frerich Raabe's avatar
Frerich Raabe committed
745
        m_backAction->setEnabled(true);
746

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

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

Frerich Raabe's avatar
Frerich Raabe committed
753
        emit signalNewSong(playingString());
754

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

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

778 779 780 781
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

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

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
801
    }
802 803 804 805 806
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
    
807 808 809 810

    updatePlaylistInfo();
}

811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
////////////////////////////////////////////////////////////////////////////////
// edit menu
////////////////////////////////////////////////////////////////////////////////

void JuK::cut()
{
    invokeEditSlot("cut()", SLOT(cut()));
}

void JuK::copy()
{
    invokeEditSlot("copy()", SLOT(copy()));
}

void JuK::paste()
{
    invokeEditSlot("paste()", SLOT(paste()));
}

void JuK::clear()
{
    invokeEditSlot("clear()", SLOT(clear()));
}

void JuK::selectAll()
{
    invokeEditSlot("selectAll()", SLOT(selectAll()));
}

840 841 842 843
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

844
void JuK::slotShowGenreListEditor()
845 846 847 848
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
849

850
void JuK::slotToggleSystemTray(bool enabled)
851
{
852
    if(enabled && !m_systemTray)
853
	setupSystemTray();
854 855 856
    else if(!enabled && m_systemTray) {
	delete m_systemTray;
	m_systemTray = 0;
857
	m_toggleDockOnCloseAction->setEnabled(false);
858 859 860
    }
}

861
void JuK::slotSetOutput(int output)
862
{
863
    stop();
864 865
    delete m_player;
    m_player = Player::createPlayer(output);
866 867
}

868 869 870 871 872
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

873
////////////////////////////////////////////////////////////////////////////////
874
// additional player slots
875 876
////////////////////////////////////////////////////////////////////////////////

877
void JuK::slotTrackPositionSliderClicked()
878
{
879
    m_trackPositionDragging = true;
880 881
}

882
void JuK::slotTrackPositionSliderReleased()
883
{
884
    if(!m_player)
885 886
	return;

887 888
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
889 890
}

891
void JuK::slotTrackPositionSliderUpdate(int position)
892
{
893
    if(!m_player)
894 895
	return;

896 897
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
898 899 900 901

    // 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
902 903
	float positionFraction = float(position) /
	    m_sliderAction->getTrackPositionSlider()->maxValue();
904 905 906 907 908
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

	m_statusLabel->setItemCurrentTime(seekTime);
    }
909 910
}

911 912
// This method is called when the play timer has expired.

913
void JuK::slotPollPlay()
914
{
915
    if(!m_player)
916 917
	return;

918
    // Our locking mechanism.  Since this method adjusts the play slider, we
919
    // want to make sure that our adjustments
920
    m_noSeek = true;
921

922
    if(!m_player->playing()) {
923

924
        m_playTimer->stop();
925

Scott Wheeler's avatar
Scott Wheeler committed
926 927
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				      m_loopPlaylistAction->isChecked()));
928
    }
929 930 931 932
    else if(!m_trackPositionDragging) {
        m_sliderAction->getTrackPositionSlider()->setValue(m_player->position());
	m_statusLabel->setItemTotalTime(m_player->totalTime());
	m_statusLabel->setItemCurrentTime(m_player->currentTime());
933 934 935
    }

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

Scott Wheeler's avatar
Scott Wheeler committed
940 941 942 943
    if(m_player->playing() &&
       m_player->totalTime() > 0 &&
       float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
    {
944
        m_playTimer->changeInterval(50);
Scott Wheeler's avatar
Scott Wheeler committed
945
    }
946

947
    m_noSeek = false;
948 949
}

950
void JuK::slotSetVolume(int volume)
951
{
952 953 954
    if(m_player && m_sliderAction && m_sliderAction->getVolumeSlider() &&
       m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
955
    {
Scott Wheeler's avatar
Scott Wheeler committed
956 957
        m_player->setVolume(float(volume) /
			    float(m_sliderAction->getVolumeSlider()->maxValue()));
958 959
    }
}
960

961 962 963 964 965 966
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

967
#include "juk.moc"