juk.cpp 30.6 KB
Newer Older
1 2 3 4 5
/***************************************************************************
                          juk.cpp  -  description
                             -------------------
    begin                : Mon Feb  4 23:40:41 EST 2002
    copyright            : (C) 2002 by Scott Wheeler
6
    email                : wheeler@kde.org
7
***************************************************************************/
8 9 10 11 12 13 14 15 16 17

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

18 19 20 21 22 23 24 25 26 27
#include <kaction.h>
#include <kcmdlineargs.h>
#include <kstatusbar.h>
#include <kdebug.h>
#include <kmessagebox.h>

#include <qmetaobject.h>
#include <qslider.h>
#include <qmime.h>

28 29 30 31 32 33 34
#include "juk.h"
#include "slideraction.h"
#include "statuslabel.h"
#include "splashscreen.h"
#include "systemtray.h"
#include "keydialog.h"
#include "tagguesserconfigdlg.h"
35
#include "filerenamerconfigdlg.h"
36

37 38 39 40
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

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

48
    readSettings();
49

50
    if(m_showSplash && !m_startDocked) {
51 52 53 54
	SplashScreen::instance()->show();
	kapp->processEvents();
    }

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

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

JuK::~JuK()
{
71

72 73
}

74 75 76 77 78 79
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

void JuK::setVolume(float volume)
{
80 81
    if(m_sliderAction->volumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->volumeSlider()->maxValue() >= volume)
82
    {
83 84 85
	int v = int(volume / 100 * m_sliderAction->volumeSlider()->maxValue());
	slotSetVolume(v);
	m_sliderAction->volumeSlider()->setValue(v);
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());
}

97
void JuK::slotGuessTagInfoFromFile()
98
{
99 100 101 102 103 104
    m_splitter->slotGuessTagInfo(TagGuesser::FileName);
}

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

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

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

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

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

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

    m_playTimer->stop();
    m_player->pause();
141
    m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
142 143 144 145 146 147 148
    actionCollection()->action("pause")->setEnabled(false);
    if(m_systemTray)
	m_systemTray->slotPause();
}

void JuK::stop()
{
149
    if(!m_player || !m_sliderAction || !m_sliderAction->volumeSlider())
150 151 152 153 154 155 156 157 158 159
	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);

160 161
    m_sliderAction->trackPositionSlider()->setValue(0);
    m_sliderAction->trackPositionSlider()->setEnabled(false);
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

    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
186 187
    m_splitter->populatePlayHistoryMenu(m_backAction->popupMenu(),
					m_randomPlayAction->isChecked());
188 189 190 191
}

void JuK::forward()
{
Scott Wheeler's avatar
Scott Wheeler committed
192 193
    play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				  m_loopPlaylistAction->isChecked()));
194 195 196 197
}

void JuK::seekBack()
{
198 199 200
    int position = m_sliderAction->trackPositionSlider()->value();
    position = QMAX(m_sliderAction->trackPositionSlider()->minValue(), position - 10);
    emit m_sliderAction->trackPositionSlider()->setValue(position);
201 202 203 204
}

void JuK::seekForward()
{
205 206 207
    int position = m_sliderAction->trackPositionSlider()->value();
    position = QMIN(m_sliderAction->trackPositionSlider()->maxValue(), position + 10);
    emit m_sliderAction->trackPositionSlider()->setValue(position);
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
}

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

    if(m_player->playing())
	pause();
    else
	play();
}

void JuK::volumeUp()
{
223 224 225
    if(m_sliderAction && m_sliderAction->volumeSlider()) {
	int volume = m_sliderAction->volumeSlider()->value() +
	    m_sliderAction->volumeSlider()->maxValue() / 25; // 4% up
226
	slotSetVolume(volume);
227
	m_sliderAction->volumeSlider()->setValue(volume);
228 229 230 231 232
    }
}

void JuK::volumeDown()
{
233 234 235
    if(m_sliderAction && m_sliderAction->volumeSlider()) {
	int volume = m_sliderAction->volumeSlider()->value() -
	    m_sliderAction->volumeSlider()->maxValue() / 25; // 4% down
236
	slotSetVolume(volume);
237
	m_sliderAction->volumeSlider()->setValue(volume);
238 239 240 241 242
    }
}

void JuK::volumeMute()
{
243
    if(m_sliderAction && m_sliderAction->volumeSlider()) {
244
	if(m_muted)
245
	    slotSetVolume(m_sliderAction->volumeSlider()->value());
246 247 248 249 250 251 252 253 254 255 256
	else
	    slotSetVolume(0);
	    m_muted = !m_muted;
    }
}

void JuK::openFile(const QString &file)
{
    m_splitter->open(file);
}

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

263 264 265 266
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

287 288
    // Needs to be here because m_splitter is not called before setupActions
    // (because PlaylistSplitter itself accesses the actionCollection)
289

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

293
    m_splitter->setFocus();
294

295
    resize(750, 500);
296 297
}

298 299
void JuK::setupActions()
{
300
    //////////////////////////////////////////////////
301
    // file menu
302
    //////////////////////////////////////////////////
303

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

307 308 309 310 311 312
    // 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
313 314 315
    newMenu->insert(createSplitterAction(i18n("Empty Playlist..."),
					 SLOT(slotCreatePlaylist()), 0, 0, 0));

316
    newMenu->insert(createSplitterAction(i18n("Playlist From Folder..."),
Scott Wheeler's avatar
Scott Wheeler committed
317 318 319 320 321
					 SLOT(slotCreatePlaylistFromDir()), 0, 0, 0));

    createSplitterAction(
	i18n("Open..."), SLOT(slotOpen()), "file_open", "fileopen", "CTRL+o");
    createSplitterAction(
322
	i18n("Open &Folder..."), SLOT(slotOpenDirectory()), "openDirectory", "fileopen");
Scott Wheeler's avatar
Scott Wheeler committed
323 324 325 326 327 328 329 330 331 332 333 334
    createSplitterAction(
	i18n("&Rename..."), SLOT(slotRenamePlaylist()), "renamePlaylist");
    createSplitterAction(
	i18n("D&uplicate..."), SLOT(slotDuplicatePlaylist()), "duplicatePlaylist");
    createSplitterAction(
	i18n("Save"), SLOT(slotSavePlaylist()), "file_save", "filesave", "CTRL+s");
    createSplitterAction(
	i18n("Save As..."), SLOT(slotSaveAsPlaylist()), "file_save_as", "filesaveas");
    createSplitterAction(
	i18n("R&emove"), SLOT(slotDeletePlaylist()), "deleteItemPlaylist", "edittrash");
    createSplitterAction(
	i18n("Reload"), SLOT(slotReloadPlaylist()), "reloadPlaylist", "reload");
335

336
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
337

338
    //////////////////////////////////////////////////
339
    // edit menu
340 341
    //////////////////////////////////////////////////

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

347
    KStdAction::selectAll(kapp, SLOT(selectAll()), actionCollection());
348

349
    createSplitterAction(
350
	i18n("Advanced Search"), SLOT(slotAdvancedSearch()), "advancedSearch", "find", "s");
351

352
    //////////////////////////////////////////////////
353
    // view menu
354
    //////////////////////////////////////////////////
355

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

363
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
364

365
    //////////////////////////////////////////////////
366
    // play menu
367 368
    //////////////////////////////////////////////////

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

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

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

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

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

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

396
    //////////////////////////////////////////////////
397
    // tagger menu
398 399
    //////////////////////////////////////////////////

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

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

Scott Wheeler's avatar
Scott Wheeler committed
409
    KAction *a;
410

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

420
    //////////////////////////////////////////////////
421
    // settings menu
422 423
    //////////////////////////////////////////////////

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

427
    setStandardToolBarMenuEnabled(true);
428

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

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

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

Scott Wheeler's avatar
Scott Wheeler committed
444

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

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

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

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

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

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

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

    createGUI();
468 469

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

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

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

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

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

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

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

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

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

Scott Wheeler's avatar
Scott Wheeler committed
530
    if(m_sliderAction &&
531 532
       m_sliderAction->trackPositionSlider() &&
       m_sliderAction->volumeSlider())
Scott Wheeler's avatar
Scott Wheeler committed
533
    {
534
        connect(m_sliderAction->trackPositionSlider(), SIGNAL(valueChanged(int)),
Scott Wheeler's avatar
Scott Wheeler committed
535
		this, SLOT(slotTrackPositionSliderUpdate(int)));
536
        connect(m_sliderAction->trackPositionSlider(), SIGNAL(sliderPressed()),
Scott Wheeler's avatar
Scott Wheeler committed
537
		this, SLOT(slotTrackPositionSliderClicked()));
538
        connect(m_sliderAction->trackPositionSlider(), SIGNAL(sliderReleased()),
Scott Wheeler's avatar
Scott Wheeler committed
539
		this, SLOT(slotTrackPositionSliderReleased()));
540
        m_sliderAction->trackPositionSlider()->setEnabled(false);
541

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

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

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

555

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

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

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

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

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

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

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

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

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

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

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

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

636 637 638
	// The history list will actually be created by the playlist restoration
	// code, but we want to remember the checkbox's setting and hope that
	// it's in synch with the code that does the real work.
639

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

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

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

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

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

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

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

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

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

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

702
    stop();
703
    delete m_player;
704

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

711 712
bool JuK::queryClose()
{
713 714 715 716 717
    if(!m_shuttingDown &&
       !kapp->sessionSaving() &&
       m_systemTray &&
       m_toggleDockOnCloseAction->isChecked())
    {
718
	KMessageBox::information(this,
Scott Wheeler's avatar
Scott Wheeler committed
719 720 721
	    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");
722 723 724 725 726 727 728
	hide();
	return false;
    }
    else
	return true;
}

729 730 731
QString JuK::playingString() const
{
    QString s;
732 733
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
734

735 736 737 738
    if(!m_splitter->playingArtist().isEmpty())
	s = m_splitter->playingArtist().simplifyWhiteSpace() + " - ";

    s += m_splitter->playingTrack().simplifyWhiteSpace();
739 740 741 742

    return s;
}

743 744
void JuK::updatePlaylistInfo()
{
Scott Wheeler's avatar
Scott Wheeler committed
745 746
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(),
				   m_splitter->selectedPlaylistCount());
747 748 749 750
}

void JuK::play(const QString &file)
{
751
    if(!m_player || !m_sliderAction || !m_sliderAction->volumeSlider())
Frerich Raabe's avatar
Frerich Raabe committed
752
        return;
753

754 755
    float volume = float(m_sliderAction->volumeSlider()->value()) /
	float(m_sliderAction->volumeSlider()->maxValue());
756 757

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
758
        m_player->stop();
759

760 761 762 763 764
    m_player->play(file, volume);

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

    if(m_player->playing()) {
765 766 767
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
768

Frerich Raabe's avatar
Frerich Raabe committed
769
        m_backAction->setEnabled(true);
770

771 772
        m_sliderAction->trackPositionSlider()->setValue(0);
        m_sliderAction->trackPositionSlider()->setEnabled(true);
Frerich Raabe's avatar
Frerich Raabe committed
773
        m_playTimer->start(m_pollInterval);
774

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

Frerich Raabe's avatar
Frerich Raabe committed
777
        emit signalNewSong(playingString());
778

Frerich Raabe's avatar
Frerich Raabe committed
779 780
        if(m_systemTray)
            m_systemTray->slotPlay();
781 782
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
783
        stop();
784 785
}

Scott Wheeler's avatar
Scott Wheeler committed
786 787 788
KAction *JuK::createSplitterAction(const QString &text, const char *slot,
				   const char *name, const QString &pix,
				   const KShortcut &shortcut)
789 790 791 792 793 794 795 796 797
{
    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));
798

799 800 801
    return action;
}

802 803 804 805
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

806
void JuK::slotPlaylistChanged()
807
{
808 809 810 811
    if(m_splitter->collectionListSelected() ||
       !m_splitter->hasListSelected() ||
       m_splitter->readOnlyListSelected())
    {
812 813 814
        actionCollection()->action("file_save")->setEnabled(false);
        actionCollection()->action("file_save_as")->setEnabled(false);
        actionCollection()->action("renamePlaylist")->setEnabled(false);
815
        actionCollection()->action("reloadPlaylist")->setEnabled(false);
816
        actionCollection()->action("deleteItemPlaylist")->setEnabled(false);
817 818
    }
    else {
819 820 821 822
        actionCollection()->action("file_save")->setEnabled(true);
        actionCollection()->action("file_save_as")->setEnabled(true);
        actionCollection()->action("renamePlaylist")->setEnabled(true);
        actionCollection()->action("deleteItemPlaylist")->setEnabled(true);
823 824 825 826 827

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
828
    }
829 830 831 832
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
833

834 835 836
    updatePlaylistInfo();
}

837 838 839 840
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

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

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

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

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

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

    // The dragging flag is set, so just update the status label, rather than seeking
    if(m_player->playing() && m_trackPositionDragging && !m_noSeek) {
	// position from 0 to 1
Scott Wheeler's avatar
Scott Wheeler committed
894
	float positionFraction = float(position) /
895
	    m_sliderAction->trackPositionSlider()->maxValue();
896 897 898 899 900
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

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

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

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

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

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

916
        m_playTimer->stop();
917

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

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

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

939
    m_noSeek = false;
940 941
}

942 943 944 945 946 947 948
void JuK::slotPlaySelectedFile()
{
    QString file = m_splitter->playSelectedFile();
    if(!file.isNull())
	play(m_splitter->playSelectedFile());
}

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

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

966 967 968 969 970 971
void JuK::slotConfigureFileRenamer()
{
    FileRenamerConfigDlg dlg(this);
    dlg.exec();
}

972
#include "juk.moc"