juk.cpp 29.8 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 94 95 96
    }
}

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

void JuK::play()
{
    if(!m_player)
Scott Wheeler's avatar
Scott Wheeler committed
97
	return;
98 99 100

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

103 104 105
	// Here, before doing anything, we want to make sure that the m_player did
	// in fact start.

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

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

    m_playTimer->stop();
    m_player->pause();
128
    m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
129 130 131 132 133 134 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
    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
173 174
    m_splitter->populatePlayHistoryMenu(m_backAction->popupMenu(),
					m_randomPlayAction->isChecked());
175 176 177 178
}

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

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
212
	    m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% up
213 214 215 216 217 218 219 220 221
	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
222
	    m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% down
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
	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);
}

244 245 246 247 248 249
void JuK::openFile(const QStringList &files)
{
    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
	openFile(*it);
}

250 251 252 253
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

254 255
void JuK::setupLayout()
{
256
    m_splitter = new PlaylistSplitter(this, "playlistSplitter");
257
    setCentralWidget(m_splitter);
258 259

    // playlist item activation connection
Scott Wheeler's avatar
Scott Wheeler committed
260 261 262 263
    connect(m_splitter, SIGNAL(signalActivated()),
	    this, SLOT(slotPlaySelectedFile()));
    connect(m_splitter, SIGNAL(signalListBoxDoubleClicked()),
	    this, SLOT(startPlayingPlaylist()));
264

265
    // create status bar
266 267
    m_statusLabel = new StatusLabel(statusBar());
    statusBar()->addWidget(m_statusLabel, 1);
268

Scott Wheeler's avatar
Scott Wheeler committed
269 270 271 272
    connect(m_splitter, SIGNAL(signalSelectedPlaylistCountChanged(int)),
	    m_statusLabel, SLOT(setPlaylistCount(int)));
    connect(m_statusLabel, SIGNAL(jumpButtonClicked()),
	    m_splitter, SLOT(slotSelectPlaying()));
273

274
    m_splitter->setFocus();
275

276
    resize(750, 500);
277 278
}

279 280
void JuK::setupActions()
{
281
    //////////////////////////////////////////////////
282
    // file menu
283
    //////////////////////////////////////////////////
284

Scott Wheeler's avatar
Scott Wheeler committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    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");
312

313
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
314

315
    //////////////////////////////////////////////////
316
    // edit menu
317 318 319 320
    //////////////////////////////////////////////////

    KStdAction::cut(this,   SLOT(cut()),   actionCollection());
    KStdAction::copy(this,  SLOT(copy()),  actionCollection());
321
    KStdAction::paste(this, SLOT(paste()), actionCollection());
322

Scott Wheeler's avatar
Scott Wheeler committed
323 324
    new KAction(i18n("C&lear"), "editclear", 0, this, SLOT(clear()),
		actionCollection(), "clear");
325

326
    KStdAction::selectAll(this, SLOT(selectAll()), actionCollection());
327

328
    //////////////////////////////////////////////////
329
    // view menu
330
    //////////////////////////////////////////////////
331

Scott Wheeler's avatar
Scott Wheeler committed
332 333 334 335
    m_showSearchAction = new KToggleAction(i18n("Show &Search Bar"), "filefind",
					   0, actionCollection(), "showSearch");
    m_showEditorAction = new KToggleAction(i18n("Show &Tag Editor"), "edit",
					   0, actionCollection(), "showEditor");
336

337
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
338

339
    //////////////////////////////////////////////////
340
    // play menu
341 342
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356
    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");
357

Scott Wheeler's avatar
Scott Wheeler committed
358 359
    connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),
	    this, SLOT(slotPopulateBackMenu()));
360

Scott Wheeler's avatar
Scott Wheeler committed
361 362
    connect(m_backAction->popupMenu(), SIGNAL(activated(int)),
	    this, SLOT(back(int)));
363

Scott Wheeler's avatar
Scott Wheeler committed
364 365
    new KAction(i18n("Skip &Forward"), "player_end", 0, this, SLOT(forward()),
		actionCollection(), "forward");
366

Scott Wheeler's avatar
Scott Wheeler committed
367 368
    m_loopPlaylistAction = new KToggleAction(i18n("&Loop Playlist"), 0, 0,
					     actionCollection(), "loopPlaylist");
369

370
    //////////////////////////////////////////////////
371
    // tagger menu
372 373
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
374 375 376 377
    createSplitterAction(i18n("&Save"),   SLOT(slotSaveTag()),
			 "saveItem",   "filesave", "CTRL+t");
    createSplitterAction(i18n("&Delete"), SLOT(slotDeleteSelectedItems()),
			 "removeItem", "editdelete");
378

Scott Wheeler's avatar
Scott Wheeler committed
379 380 381
    KActionMenu *guessMenu = new KActionMenu(i18n("&Guess Tag Information"),
					     QString::null, actionCollection(),
					     "guessTag");
382

Scott Wheeler's avatar
Scott Wheeler committed
383
    KAction *a;
384

Scott Wheeler's avatar
Scott Wheeler committed
385 386 387 388 389 390 391 392 393 394 395
    a = createSplitterAction(i18n("From &Filename"),
			     SLOT(slotGuessTagInfoFile()),
			     "guessTagFile", 0, "CTRL+f");
    guessMenu->insert(a);

    a = createSplitterAction(i18n("From &Internet"), SLOT(slotGuessTagInfoInternet()),
			     "guessTagInternet", 0, "CTRL+i");
    guessMenu->insert(a);

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

    //////////////////////////////////////////////////
398
    // settings menu
399 400
    //////////////////////////////////////////////////

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

404
    setStandardToolBarMenuEnabled(true);
405

Scott Wheeler's avatar
Scott Wheeler committed
406 407 408 409 410 411 412 413
    m_toggleSplashAction = new KToggleAction(i18n("Show Splash Screen on Startup"),
					     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");
414

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

Scott Wheeler's avatar
Scott Wheeler committed
418 419
    connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
	    this, SLOT(slotToggleSystemTray(bool)));
420

Scott Wheeler's avatar
Scott Wheeler committed
421 422 423

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

425
    m_outputSelectAction = Player::playerSelectAction(actionCollection());
426

427
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
428 429
        m_outputSelectAction->setCurrentItem(0);
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
430
    }
431

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

435 436
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

437
    //////////////////////////////////////////////////
438
    // just in the toolbar
439 440
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
441 442
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(),
				      "trackPositionAction");
443 444

    createGUI();
445 446

    // set the slider to the proper orientation and make it stay that way
447
    m_sliderAction->slotUpdateOrientation();
448 449
}

450 451 452 453 454 455
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
456 457 458 459 460 461 462 463
    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()));
464 465
}

466
void JuK::setupSystemTray()
467
{
468
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
Frerich Raabe's avatar
Frerich Raabe committed
469 470
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
471

Scott Wheeler's avatar
Scott Wheeler committed
472 473
        connect(this, SIGNAL(signalNewSong(const QString&)), 
		m_systemTray, SLOT(slotNewSong(const QString&)));
474

Frerich Raabe's avatar
Frerich Raabe committed
475 476 477 478
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
479

Frerich Raabe's avatar
Frerich Raabe committed
480
        m_toggleDockOnCloseAction->setEnabled(true);
481

Frerich Raabe's avatar
Frerich Raabe committed
482
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
483
    }
484
    else {
Frerich Raabe's avatar
Frerich Raabe committed
485 486
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
487
    }
488 489
}

490 491
void JuK::setupPlayer()
{
492 493
    m_trackPositionDragging = false;
    m_noSeek = false;
Scott Wheeler's avatar
Scott Wheeler committed
494
    m_muted = false;
495 496 497 498
    actionCollection()->action("pause")->setEnabled(false);
    actionCollection()->action("stop")->setEnabled(false);
    actionCollection()->action("back")->setEnabled(false);
    actionCollection()->action("forward")->setEnabled(false);
499

500
    m_playTimer = new QTimer(this);
501
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
502

Scott Wheeler's avatar
Scott Wheeler committed
503 504 505 506 507 508 509 510 511 512
    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()));
513
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
514

Scott Wheeler's avatar
Scott Wheeler committed
515 516
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)),
		this, SLOT(slotSetVolume(int)));
517
    }
518

519
    int playerType = 0;
520
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
521 522
        playerType = m_outputSelectAction->currentItem();
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
523 524
    }

525
    m_player = Player::createPlayer(playerType);
526 527
}

528

529 530 531
void JuK::setupGlobalAccels()
{
    m_accel = new KGlobalAccel(this);
532 533 534 535 536 537 538 539 540
    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()));
541

542 543 544 545 546
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

547 548 549 550
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
551

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

555
    m_splitter->open(files);
556 557
}

558 559 560
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
Frerich Raabe's avatar
Frerich Raabe committed
561
        e->accept();
562 563 564
    KMainWindow::keyPressEvent(e);
}

565 566 567 568 569 570 571 572 573
/**
 * 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
574
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
575 576 577
    }
}

578 579
void JuK::readConfig()
{
580
    // Automagically save and m_restore many window settings.
581 582
    setAutoSaveSettings();

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

Frerich Raabe's avatar
Frerich Raabe committed
594 595
        bool loopPlaylist = config->readBoolEntry("LoopPlaylist", false);
        m_loopPlaylistAction->setChecked(loopPlaylist);
596
    }
597
    { // view settings
598
        KConfigGroupSaver saver(config, "View");
599 600 601 602 603

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

Frerich Raabe's avatar
Frerich Raabe committed
604 605 606
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
607
    }
608 609
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
610

Frerich Raabe's avatar
Frerich Raabe committed
611 612
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
613

Frerich Raabe's avatar
Frerich Raabe committed
614 615
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
616

Frerich Raabe's avatar
Frerich Raabe committed
617 618
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
619

Frerich Raabe's avatar
Frerich Raabe committed
620 621
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
622
    }
623

624
    m_toggleSplashAction->setChecked(m_showSplash);
625 626 627 628
}

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

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

657
bool JuK::queryExit()
658
{
659
    stop();
660
    delete m_player;
661
    Cache::instance()->save();
662
    saveConfig();
663
    delete m_splitter;
664
    return true;
665 666
}

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

681
void JuK::invokeEditSlot(const char *slotName, const char *slot)
682 683
{
    QObject *object = focusWidget();
684

685
    if(!object || !slotName || !slot)
Frerich Raabe's avatar
Frerich Raabe committed
686
        return;
687

688 689
    QMetaObject *meta = object->metaObject();
    QStrList l = meta->slotNames(true);
690

691
    if(l.find(slotName) == -1)
Frerich Raabe's avatar
Frerich Raabe committed
692
        return;
693

694 695 696
    connect(this, SIGNAL(signalEdit()), object, slot);
    emit signalEdit();
    disconnect(this, SIGNAL(signalEdit()), object, slot);
697 698
}

699 700 701
QString JuK::playingString() const
{
    QString s;
702 703
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
704

705
    if(m_splitter->playingArtist().isEmpty())
Frerich Raabe's avatar
Frerich Raabe committed
706
        s = m_splitter->playingTrack().simplifyWhiteSpace();
707
    else
Scott Wheeler's avatar
Scott Wheeler committed
708 709
        s = m_splitter->playingArtist().simplifyWhiteSpace() + " - " +
	    m_splitter->playingTrack().simplifyWhiteSpace();
710 711 712 713

    return s;
}

714 715
void JuK::updatePlaylistInfo()
{
Scott Wheeler's avatar
Scott Wheeler committed
716 717
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(),
				   m_splitter->selectedPlaylistCount());
718 719 720 721
}

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

Scott Wheeler's avatar
Scott Wheeler committed
725 726
    float volume = float(m_sliderAction->getVolumeSlider()->value()) /
	float(m_sliderAction->getVolumeSlider()->maxValue());
727 728

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
729
        m_player->stop();
730

731 732 733 734 735
    m_player->play(file, volume);

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

    if(m_player->playing()) {
736 737 738
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
739

Frerich Raabe's avatar
Frerich Raabe committed
740
        m_backAction->setEnabled(true);
741

Frerich Raabe's avatar
Frerich Raabe committed
742 743 744
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
745

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

Frerich Raabe's avatar
Frerich Raabe committed
748
        emit signalNewSong(playingString());
749

Frerich Raabe's avatar
Frerich Raabe committed
750 751
        if(m_systemTray)
            m_systemTray->slotPlay();
752 753
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
754
        stop();
755 756
}

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

773 774 775 776
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

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

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
796
    }
797 798 799 800 801
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
    
802 803 804 805

    updatePlaylistInfo();
}

806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
////////////////////////////////////////////////////////////////////////////////
// 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()));
}

835 836 837 838
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

839
void JuK::slotShowGenreListEditor()
840 841 842 843
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
844

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

856
void JuK::slotSetOutput(int output)
857
{
858
    stop();
859 860
    delete m_player;
    m_player = Player::createPlayer(output);
861 862
}

863 864 865 866 867
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

868
////////////////////////////////////////////////////////////////////////////////
869
// additional player slots
870 871
////////////////////////////////////////////////////////////////////////////////

872
void JuK::slotTrackPositionSliderClicked()
873
{
874
    m_trackPositionDragging = true;
875 876
}

877
void JuK::slotTrackPositionSliderReleased()
878
{
879
    if(!m_player)
880 881
	return;

882 883
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
884 885
}

886
void JuK::slotTrackPositionSliderUpdate(int position)
887
{
888
    if(!m_player)
889 890
	return;

891 892
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
893 894 895 896

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

	m_statusLabel->setItemCurrentTime(seekTime);
    }
904 905
}

906 907
// This method is called when the play timer has expired.

908
void JuK::slotPollPlay()
909
{
910
    if(!m_player)
911 912
	return;

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

917
    if(!m_player->playing()) {
918

919
        m_playTimer->stop();
920

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

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

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

942
    m_noSeek = false;
943 944
}

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

956 957 958 959 960 961
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

962
#include "juk.moc"