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

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

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

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

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

38 39 40 41
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

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

49
    readSettings();
50

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

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

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

JuK::~JuK()
{
72

73 74
}

75 76 77 78 79 80 81 82 83
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

void JuK::setVolume(float volume)
{
    if(m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
    {
Frerich Raabe's avatar
Frerich Raabe committed
84
        slotSetVolume(int(volume / 100) * m_sliderAction->getVolumeSlider()->maxValue());
85 86 87 88 89 90 91 92 93 94 95
    }
}

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

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

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

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

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

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

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

    m_playTimer->stop();
    m_player->pause();
136
    m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
    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
181 182
    m_splitter->populatePlayHistoryMenu(m_backAction->popupMenu(),
					m_randomPlayAction->isChecked());
183 184 185 186
}

void JuK::forward()
{
Scott Wheeler's avatar
Scott Wheeler committed
187 188
    play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				  m_loopPlaylistAction->isChecked()));
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
}

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

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

258 259 260 261
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

282
    m_splitter->setFocus();
283

284
    resize(750, 500);
285 286
}

287 288
void JuK::setupActions()
{
289
    //////////////////////////////////////////////////
290
    // file menu
291
    //////////////////////////////////////////////////
292

Scott Wheeler's avatar
Scott Wheeler committed
293 294 295
    KActionMenu *newMenu = new KActionMenu(i18n("&New"), "filenew",
					   actionCollection(), "file_new");

296 297 298 299 300 301
    // 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
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    newMenu->insert(createSplitterAction(i18n("Empty Playlist..."),
					 SLOT(slotCreatePlaylist()), 0, 0, 0));

    newMenu->insert(createSplitterAction(i18n("Playlist From Directory..."),
					 SLOT(slotCreatePlaylistFromDir()), 0, 0, 0));

    createSplitterAction(
	i18n("Open..."), SLOT(slotOpen()), "file_open", "fileopen", "CTRL+o");
    createSplitterAction(
	i18n("Open &Directory..."), SLOT(slotOpenDirectory()), "openDirectory", "fileopen");
    createSplitterAction(
	i18n("&Rename..."), SLOT(slotRenamePlaylist()), "renamePlaylist");
    createSplitterAction(
	i18n("D&uplicate..."), SLOT(slotDuplicatePlaylist()), "duplicatePlaylist");
    createSplitterAction(
	i18n("Save"), SLOT(slotSavePlaylist()), "file_save", "filesave", "CTRL+s");
    createSplitterAction(
	i18n("Save As..."), SLOT(slotSaveAsPlaylist()), "file_save_as", "filesaveas");
    createSplitterAction(
	i18n("R&emove"), SLOT(slotDeletePlaylist()), "deleteItemPlaylist", "edittrash");
    createSplitterAction(
	i18n("Reload"), SLOT(slotReloadPlaylist()), "reloadPlaylist", "reload");
324

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

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

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

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

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

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

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

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

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

Scott Wheeler's avatar
Scott Wheeler committed
358 359 360 361 362 363 364 365 366 367 368 369 370 371
    m_randomPlayAction = new KToggleAction(i18n("&Random Play"), 0,
					   actionCollection(), "randomPlay");

    new KAction(i18n("&Play"),  "player_play",  0, this, SLOT(play()),
		actionCollection(), "play");

    new KAction(i18n("P&ause"), "player_pause", 0, this, SLOT(pause()),
		actionCollection(), "pause");

    new KAction(i18n("&Stop"),  "player_stop",  0, this, SLOT(stop()),
		actionCollection(), "stop");

    m_backAction = new KToolBarPopupAction(i18n("Skip &Back"), "player_start", 0,
					   this, SLOT(back()), actionCollection(), "back");
372

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

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

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

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

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

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

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

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

400 401 402 403 404
    a = new KAction(i18n("From &Filename"), "CTRL+f", actionCollection(), "guessTagFile");
    m_guessMenu->insert(a);

    a = new KAction(i18n("From &Internet"), "CTRL+i", actionCollection(), "guessTagInternet");
    m_guessMenu->insert(a);
Scott Wheeler's avatar
Scott Wheeler committed
405

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

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

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

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

418
    setStandardToolBarMenuEnabled(true);
419

420
    m_toggleSplashAction = new KToggleAction(i18n("Show Splash Screen on Startp"),
Scott Wheeler's avatar
Scott Wheeler committed
421 422 423 424 425 426 427
					     0, actionCollection(), "showSplashScreen");

    m_toggleSystemTrayAction = new KToggleAction(i18n("&Dock in System Tray"),
						 0, actionCollection(), "toggleSystemTray");

    m_toggleDockOnCloseAction = new KToggleAction(i18n("&Stay in System Tray on Close"),
						  0, actionCollection(), "dockOnClose");
428

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

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

Scott Wheeler's avatar
Scott Wheeler committed
435 436 437

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

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

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

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

449 450 451
    new KAction(i18n("&File Renamer..."), 0, 0, this, SLOT(slotConfigureFileRenamer()),
		actionCollection(), "fileRenamerConfig");

452 453
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

454
    //////////////////////////////////////////////////
455
    // just in the toolbar
456 457
    //////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
458 459
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(),
				      "trackPositionAction");
460 461

    createGUI();
462 463

    // set the slider to the proper orientation and make it stay that way
464
    m_sliderAction->slotUpdateOrientation();
465 466
}

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

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

Scott Wheeler's avatar
Scott Wheeler committed
491 492
        connect(this, SIGNAL(signalNewSong(const QString&)), 
		m_systemTray, SLOT(slotNewSong(const QString&)));
493

Frerich Raabe's avatar
Frerich Raabe committed
494 495 496 497
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
498

Frerich Raabe's avatar
Frerich Raabe committed
499
        m_toggleDockOnCloseAction->setEnabled(true);
500

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

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

519
    m_playTimer = new QTimer(this);
520
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
521

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

Scott Wheeler's avatar
Scott Wheeler committed
534 535
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)),
		this, SLOT(slotSetVolume(int)));
536
    }
537

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

544
    m_player = Player::createPlayer(playerType);
545 546
}

547

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

561 562 563 564 565
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

566 567 568 569
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
570

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

574
    m_splitter->open(files);
575 576
}

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

584 585 586 587 588 589 590 591 592
/**
 * 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
593
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
594
        m_startDocked = config->readBoolEntry("StartDocked", false);
595 596 597
    }
}

598 599
void JuK::readConfig()
{
600
    // Automagically save and m_restore many window settings.
601 602
    setAutoSaveSettings();

603
    KConfig *config = KGlobal::config();
604
    { // player settings
605
        KConfigGroupSaver saver(config, "Player");
606
        if(m_sliderAction->getVolumeSlider()) {
Scott Wheeler's avatar
Scott Wheeler committed
607 608
	    int maxVolume = m_sliderAction->getVolumeSlider()->maxValue();
	    int volume = config->readNumEntry("Volume", maxVolume);
609
            m_sliderAction->getVolumeSlider()->setValue(volume);
610
        }
Frerich Raabe's avatar
Frerich Raabe committed
611 612
        bool randomPlay = config->readBoolEntry("RandomPlay", false);
        m_randomPlayAction->setChecked(randomPlay);
613

Frerich Raabe's avatar
Frerich Raabe committed
614 615
        bool loopPlaylist = config->readBoolEntry("LoopPlaylist", false);
        m_loopPlaylistAction->setChecked(loopPlaylist);
616
    }
617
    { // view settings
618
        KConfigGroupSaver saver(config, "View");
619 620 621 622 623

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

Frerich Raabe's avatar
Frerich Raabe committed
624 625 626
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
627

628 629 630 631 632 633
	// 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.
	
	bool showHistory = config->readBoolEntry("ShowHistory", false);
	m_showHistoryAction->setChecked(showHistory);
634
    }
635 636
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
637

Frerich Raabe's avatar
Frerich Raabe committed
638 639
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
640

Frerich Raabe's avatar
Frerich Raabe committed
641 642
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
643

Frerich Raabe's avatar
Frerich Raabe committed
644 645
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
646

Frerich Raabe's avatar
Frerich Raabe committed
647 648
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
649
    }
650

651
    m_toggleSplashAction->setChecked(m_showSplash);
652 653 654 655
}

void JuK::saveConfig()
{
656
    KConfig *config = KGlobal::config();
657
    { // m_player settings
658
        KConfigGroupSaver saver(config, "Player");
659 660
        if(m_sliderAction && m_sliderAction->getVolumeSlider())
            config->writeEntry("Volume", m_sliderAction->getVolumeSlider()->value());
Frerich Raabe's avatar
Frerich Raabe committed
661 662 663 664
        if(m_randomPlayAction)
            config->writeEntry("RandomPlay", m_randomPlayAction->isChecked());
        if(m_loopPlaylistAction)
            config->writeEntry("LoopPlaylist", m_loopPlaylistAction->isChecked());
665
    }
666 667
    { // view settings
        KConfigGroupSaver saver(config, "View");
668

Frerich Raabe's avatar
Frerich Raabe committed
669
        config->writeEntry("ShowEditor", m_showEditorAction->isChecked());
670
        config->writeEntry("ShowSearch", m_showSearchAction->isChecked());
671
	config->writeEntry("ShowHistory", m_showHistoryAction->isChecked());
672
    }
673 674
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
Frerich Raabe's avatar
Frerich Raabe committed
675
        config->writeEntry("ShowSplashScreen", m_toggleSplashAction->isChecked());
676
        config->writeEntry("StartDocked", m_startDocked);
Frerich Raabe's avatar
Frerich Raabe committed
677 678 679 680 681
        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());
682
    }
683
    config->sync();
684 685
}

686
bool JuK::queryExit()
687
{
688 689
    m_startDocked = !isVisible();

690 691 692 693
    hide();
    delete m_systemTray;
    m_systemTray = 0;

694
    stop();
695
    delete m_player;
696

697
    Cache::instance()->save();
698
    saveConfig();
699
    delete m_splitter;
700
    return true;
701 702
}

703 704
bool JuK::queryClose()
{
705
    if(!m_shuttingDown && m_systemTray && m_toggleDockOnCloseAction->isChecked()) {
706
	KMessageBox::information(this,
Scott Wheeler's avatar
Scott Wheeler committed
707 708 709
	    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");
710 711 712 713 714 715 716
	hide();
	return false;
    }
    else
	return true;
}

717 718 719
QString JuK::playingString() const
{
    QString s;
720 721
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
722

723 724 725 726
    if(!m_splitter->playingArtist().isEmpty())
	s = m_splitter->playingArtist().simplifyWhiteSpace() + " - ";

    s += m_splitter->playingTrack().simplifyWhiteSpace();
727 728 729 730

    return s;
}

731 732
void JuK::updatePlaylistInfo()
{
Scott Wheeler's avatar
Scott Wheeler committed
733 734
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(),
				   m_splitter->selectedPlaylistCount());
735 736 737 738
}

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

Scott Wheeler's avatar
Scott Wheeler committed
742 743
    float volume = float(m_sliderAction->getVolumeSlider()->value()) /
	float(m_sliderAction->getVolumeSlider()->maxValue());
744 745

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
746
        m_player->stop();
747

748 749 750 751 752
    m_player->play(file, volume);

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

    if(m_player->playing()) {
753 754 755
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
756

Frerich Raabe's avatar
Frerich Raabe committed
757
        m_backAction->setEnabled(true);
758

Frerich Raabe's avatar
Frerich Raabe committed
759 760 761
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
762

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

Frerich Raabe's avatar
Frerich Raabe committed
765
        emit signalNewSong(playingString());
766

Frerich Raabe's avatar
Frerich Raabe committed
767 768
        if(m_systemTray)
            m_systemTray->slotPlay();
769 770
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
771
        stop();
772 773
}

Scott Wheeler's avatar
Scott Wheeler committed
774 775 776
KAction *JuK::createSplitterAction(const QString &text, const char *slot,
				   const char *name, const QString &pix,
				   const KShortcut &shortcut)
777 778 779 780 781 782 783 784 785 786 787 788 789
{
    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;
}

790 791 792 793
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

794
void JuK::slotPlaylistChanged()
795
{
796 797 798 799
    if(m_splitter->collectionListSelected() ||
       !m_splitter->hasListSelected() ||
       m_splitter->readOnlyListSelected())
    {
800 801 802
        actionCollection()->action("file_save")->setEnabled(false);
        actionCollection()->action("file_save_as")->setEnabled(false);
        actionCollection()->action("renamePlaylist")->setEnabled(false);
803
        actionCollection()->action("reloadPlaylist")->setEnabled(false);
804
        actionCollection()->action("deleteItemPlaylist")->setEnabled(false);
805 806
    }
    else {
807 808 809 810
        actionCollection()->action("file_save")->setEnabled(true);
        actionCollection()->action("file_save_as")->setEnabled(true);
        actionCollection()->action("renamePlaylist")->setEnabled(true);
        actionCollection()->action("deleteItemPlaylist")->setEnabled(true);
811 812 813 814 815

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
816
    }
817 818 819 820 821
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
    
822 823 824
    updatePlaylistInfo();
}

825 826 827 828
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

829
void JuK::slotShowGenreListEditor()
830 831 832 833
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
834

835
void JuK::slotToggleSystemTray(bool enabled)
836
{
837
    if(enabled && !m_systemTray)
838
	setupSystemTray();
839 840 841
    else if(!enabled && m_systemTray) {
	delete m_systemTray;
	m_systemTray = 0;
842
	m_toggleDockOnCloseAction->setEnabled(false);
843 844 845
    }
}

846
void JuK::slotSetOutput(int output)
847
{
848
    stop();
849 850
    delete m_player;
    m_player = Player::createPlayer(output);
851 852
}

853 854 855 856 857
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

858
////////////////////////////////////////////////////////////////////////////////
859
// additional player slots
860 861
////////////////////////////////////////////////////////////////////////////////

862
void JuK::slotTrackPositionSliderClicked()
863
{
864
    m_trackPositionDragging = true;
865 866
}

867
void JuK::slotTrackPositionSliderReleased()
868
{
869
    if(!m_player)
870 871
	return;

872 873
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
874 875
}

876
void JuK::slotTrackPositionSliderUpdate(int position)
877
{
878
    if(!m_player)
879 880
	return;

881 882
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
883 884 885 886

    // 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
887 888
	float positionFraction = float(position) /
	    m_sliderAction->getTrackPositionSlider()->maxValue();
889 890 891 892 893
	float totalTime = float(m_player->totalTime());
	long seekTime = long(positionFraction * totalTime + 0.5); // "+0.5" for rounding

	m_statusLabel->setItemCurrentTime(seekTime);
    }
894 895
}

896 897
// This method is called when the play timer has expired.

898
void JuK::slotPollPlay()
899
{
900
    if(!m_player)
901 902
	return;

903
    // Our locking mechanism.  Since this method adjusts the play slider, we
904
    // want to make sure that our adjustments
905
    m_noSeek = true;
906

907
    if(!m_player->playing()) {
908

909
        m_playTimer->stop();
910

Scott Wheeler's avatar
Scott Wheeler committed
911 912
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(),
				      m_loopPlaylistAction->isChecked()));
913
    }
914 915 916 917
    else if(!m_trackPositionDragging) {
        m_sliderAction->getTrackPositionSlider()->setValue(m_player->position());
	m_statusLabel->setItemTotalTime(m_player->totalTime());
	m_statusLabel->setItemCurrentTime(m_player->currentTime());
918 919 920
    }

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

Scott Wheeler's avatar
Scott Wheeler committed
925 926 927 928
    if(m_player->playing() &&
       m_player->totalTime() > 0 &&
       float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
    {
929
        m_playTimer->changeInterval(50);
Scott Wheeler's avatar
Scott Wheeler committed
930
    }
931

932
    m_noSeek = false;
933 934
}

935
void JuK::slotSetVolume(int volume)
936
{
937 938 939
    if(m_player && m_sliderAction && m_sliderAction->getVolumeSlider() &&
       m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
940
    {
Scott Wheeler's avatar
Scott Wheeler committed
941 942
        m_player->setVolume(float(volume) /
			    float(m_sliderAction->getVolumeSlider()->maxValue()));
943 944
    }
}
945

946 947 948 949 950 951
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

952 953 954 955 956 957
void JuK::slotConfigureFileRenamer()
{
    FileRenamerConfigDlg dlg(this);
    dlg.exec();
}

958
#include "juk.moc"