juk.cpp 29.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
#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
////////////////////////////////////////////////////////////////////////////////

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

46
    readSettings();
47 48 49 50 51 52

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

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

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

JuK::~JuK()
{
69

70 71
}

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

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

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

    if(m_player->paused()) {
        m_player->play();
100
        m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
	// Here, before doing anything, we want to make sure that the m_player did
	// in fact start.

        if(m_player->playing()) {
            actionCollection()->action("pause")->setEnabled(true);
            actionCollection()->action("stop")->setEnabled(true);
            m_playTimer->start(m_pollInterval);
	    if(m_systemTray)
		m_systemTray->slotPlay();
        }
    }
    else if(m_player->playing())
	m_player->seekPosition(0);
    else
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(), m_loopPlaylistAction->isChecked()));
}

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

    m_playTimer->stop();
    m_player->pause();
125
    m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
126 127 128 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 173 174 175 176 177 178 179 180 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 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
    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()
{
    m_splitter->populatePlayHistoryMenu(m_backAction->popupMenu(), m_randomPlayAction->isChecked());
}

void JuK::forward()
{
    play(m_splitter->playNextFile(m_randomPlayAction->isChecked(), m_loopPlaylistAction->isChecked()));
}

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() +
	  m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% up
	slotSetVolume(volume);
	m_sliderAction->getVolumeSlider()->setValue(volume);
    }
}

void JuK::volumeDown()
{
    if(m_sliderAction && m_sliderAction->getVolumeSlider()) {
	int volume = m_sliderAction->getVolumeSlider()->value() -
	  m_sliderAction->getVolumeSlider()->maxValue() / 25; // 4% down
	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);
}

239 240 241 242 243 244
void JuK::openFile(const QStringList &files)
{
    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
	openFile(*it);
}

245 246 247 248
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

249 250
void JuK::setupLayout()
{
251
    m_splitter = new PlaylistSplitter(this, "playlistSplitter");
252
    setCentralWidget(m_splitter);
253 254

    // playlist item activation connection
255
    connect(m_splitter, SIGNAL(signalActivated()), this, SLOT(slotPlaySelectedFile()));
256
    connect(m_splitter, SIGNAL(signalListBoxDoubleClicked()), this, SLOT(startPlayingPlaylist()));
257

258
    // create status bar
259 260
    m_statusLabel = new StatusLabel(statusBar());
    statusBar()->addWidget(m_statusLabel, 1);
261

262 263
    connect(m_splitter, SIGNAL(signalSelectedPlaylistCountChanged(int)), m_statusLabel, SLOT(setPlaylistCount(int)));
    connect(m_statusLabel, SIGNAL(jumpButtonClicked()), m_splitter, SLOT(slotSelectPlaying()));
264

265
    m_splitter->setFocus();
266

267
    resize(750, 500);
268 269
}

270 271
void JuK::setupActions()
{
272
    //////////////////////////////////////////////////
273
    // file menu
274
    //////////////////////////////////////////////////
275

Scott Wheeler's avatar
Scott Wheeler committed
276 277 278
    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));
279

Scott Wheeler's avatar
Scott Wheeler committed
280
    createSplitterAction(i18n("Open..."),            SLOT(slotOpen()),              "file_open",          "fileopen", "CTRL+o");
281 282 283
    createSplitterAction(i18n("Open &Directory..."), SLOT(slotOpenDirectory()),     "openDirectory",      "fileopen");
    createSplitterAction(i18n("&Rename..."),         SLOT(slotRenamePlaylist()),    "renamePlaylist");
    createSplitterAction(i18n("D&uplicate..."),      SLOT(slotDuplicatePlaylist()), "duplicatePlaylist");
Scott Wheeler's avatar
Scott Wheeler committed
284
    createSplitterAction(i18n("Save"),               SLOT(slotSavePlaylist()),      "file_save",          "filesave", "CTRL+s");
285 286
    createSplitterAction(i18n("Save As..."),         SLOT(slotSaveAsPlaylist()),    "file_save_as",       "filesaveas");
    createSplitterAction(i18n("R&emove"),            SLOT(slotDeletePlaylist()),    "deleteItemPlaylist", "edittrash");
287 288
    createSplitterAction(i18n("R&emove"),            SLOT(slotDeletePlaylist()),    "deleteItemPlaylist", "edittrash");
    createSplitterAction(i18n("Reload"),             SLOT(slotReloadPlaylist()),    "reloadPlaylist",     "reload");
289

290
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
291

292
    //////////////////////////////////////////////////
293
    // edit menu
294 295 296 297
    //////////////////////////////////////////////////

    KStdAction::cut(this,   SLOT(cut()),   actionCollection());
    KStdAction::copy(this,  SLOT(copy()),  actionCollection());
298
    KStdAction::paste(this, SLOT(paste()), actionCollection());
299

300
    new KAction(i18n("C&lear"), "editclear", 0, this, SLOT(clear()), actionCollection(), "clear");
301

302
    KStdAction::selectAll(this, SLOT(selectAll()), actionCollection());
303

304
    //////////////////////////////////////////////////
305
    // view menu
306
    //////////////////////////////////////////////////
307

308 309
    m_showSearchAction = new KToggleAction(i18n("Show &Search Bar"), "filefind", 0, actionCollection(), "showSearch");
    m_showEditorAction = new KToggleAction(i18n("Show &Tag Editor"), "edit",     0, actionCollection(), "showEditor");
310

311
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
312

313
    //////////////////////////////////////////////////
314
    // play menu
315 316
    //////////////////////////////////////////////////

317
    m_randomPlayAction = new KToggleAction(i18n("&Random Play"), 0, actionCollection(), "randomPlay");
318 319 320 321 322

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

323
    m_backAction = new KToolBarPopupAction(i18n("Skip &Back"), "player_start", 0, this, SLOT(back()), actionCollection(), "back");
324
    connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),  this, SLOT(slotPopulateBackMenu()));
325
    connect(m_backAction->popupMenu(), SIGNAL(activated(int)), this, SLOT(back(int)));
326 327 328

    new KAction(i18n("Skip &Forward"), "player_end", 0, this, SLOT(forward()), actionCollection(), "forward");

329
    m_loopPlaylistAction = new KToggleAction(i18n("&Loop Playlist"), 0, 0, actionCollection(), "loopPlaylist");
330

331
    //////////////////////////////////////////////////
332
    // tagger menu
333 334 335 336
    //////////////////////////////////////////////////

    createSplitterAction(i18n("&Save"),   SLOT(slotSaveTag()),             "saveItem",   "filesave", "CTRL+t");
    createSplitterAction(i18n("&Delete"), SLOT(slotDeleteSelectedItems()), "removeItem", "editdelete");
337 338

    KActionMenu *guessMenu = new KActionMenu(i18n("&Guess Tag Information"), "", actionCollection(), "guessTag");
339

340 341 342 343 344 345 346 347
    guessMenu->insert(
        createSplitterAction(i18n("From &Filename"), SLOT(slotGuessTagInfoFile()),     "guessTagFile",     0, "CTRL+f"));
    guessMenu->insert(
        createSplitterAction(i18n("From &Internet"), SLOT(slotGuessTagInfoInternet()), "guessTagInternet", 0, "CTRL+i"));

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

    //////////////////////////////////////////////////
348
    // settings menu
349 350
    //////////////////////////////////////////////////

351
    new KToggleAction(i18n("Show Menu Bar"), "CTRL+m", this, SLOT(slotToggleMenuBar()), actionCollection(), "toggleMenuBar");
352

353
    setStandardToolBarMenuEnabled(true);
354

355 356 357 358
    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");
    m_togglePopupsAction      = new KToggleAction(i18n("&Popup Track Announcement"),     0, this, 0, actionCollection(), "togglePopups");
359

360
    connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)), this, SLOT(slotToggleSystemTray(bool)));
361

362

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

365
    m_outputSelectAction = Player::playerSelectAction(actionCollection());
366

367
    if(m_outputSelectAction) {
368 369
        m_outputSelectAction->setCurrentItem(0);
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
370
    }
371

372
    new KAction(i18n("&Tag Guesser..."), 0, 0, this, SLOT(slotConfigureTagGuesser()), actionCollection(), "tagGuesserConfig");
373

374 375
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

376
    //////////////////////////////////////////////////
377
    // just in the toolbar
378 379
    //////////////////////////////////////////////////

380
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(), "trackPositionAction");
381 382

    createGUI();
383 384

    // set the slider to the proper orientation and make it stay that way
385
    m_sliderAction->slotUpdateOrientation();
386 387
}

388 389 390 391 392 393 394 395 396
void JuK::setupSplitterConnections()
{
    QValueListConstIterator<SplitterConnection> it = m_splitterConnections.begin();
    for(; it != m_splitterConnections.end(); ++it)
        connect((*it).first, SIGNAL(activated()), m_splitter, (*it).second);

    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 *)));
397
    connect(m_splitter, SIGNAL(signalPlaylistChanged()), this, SLOT(slotPlaylistChanged()));
398 399
}

400
void JuK::setupSystemTray()
401
{
402
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
403 404
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
405

406
        connect(this, SIGNAL(signalNewSong(const QString&)), m_systemTray, SLOT(slotNewSong(const QString&)));
407

408 409 410 411
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
412

413
        m_toggleDockOnCloseAction->setEnabled(true);
414

415
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
416
    }
417
    else {
418 419
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
420
    }
421 422
}

423 424
void JuK::setupPlayer()
{
425 426
    m_trackPositionDragging = false;
    m_noSeek = false;
Scott Wheeler's avatar
Scott Wheeler committed
427
    m_muted = false;
428 429 430 431
    actionCollection()->action("pause")->setEnabled(false);
    actionCollection()->action("stop")->setEnabled(false);
    actionCollection()->action("back")->setEnabled(false);
    actionCollection()->action("forward")->setEnabled(false);
432

433
    m_playTimer = new QTimer(this);
434
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
435

436
    if(m_sliderAction && m_sliderAction->getTrackPositionSlider() && m_sliderAction->getVolumeSlider()) {
437 438 439
        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()));
440
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
441

442
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)), this, SLOT(slotSetVolume(int)));
443
    }
444

445
    int playerType = 0;
446
    if(m_outputSelectAction) {
447 448
        playerType = m_outputSelectAction->currentItem();
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
449 450
    }

451
    m_player = Player::createPlayer(playerType);
452 453
}

454

455 456 457
void JuK::setupGlobalAccels()
{
    m_accel = new KGlobalAccel(this);
458 459 460 461 462 463 464 465 466
    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()));
467

468 469 470 471 472
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

473 474 475 476
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
477

478
    for(int i = 0; i < args->count(); i++)
479
        files.append(args->arg(i));
480

481
    m_splitter->open(files);
482 483
}

484 485 486
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
487
        e->accept();
488 489 490
    KMainWindow::keyPressEvent(e);
}

491 492 493 494 495 496 497 498 499
/**
 * 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");
500
        m_showSplash = config->readBoolEntry("ShowSplashScreen", true);
501 502 503
    }
}

504 505
void JuK::readConfig()
{
506
    // Automagically save and m_restore many window settings.
507 508
    setAutoSaveSettings();

509
    KConfig *config = KGlobal::config();
510
    { // player settings
511
        KConfigGroupSaver saver(config, "Player");
512
        if(m_sliderAction->getVolumeSlider()) {
513 514
            int volume = config->readNumEntry("Volume", m_sliderAction->getVolumeSlider()->maxValue());
            m_sliderAction->getVolumeSlider()->setValue(volume);
515
        }
516 517
        bool randomPlay = config->readBoolEntry("RandomPlay", false);
        m_randomPlayAction->setChecked(randomPlay);
518

519 520
        bool loopPlaylist = config->readBoolEntry("LoopPlaylist", false);
        m_loopPlaylistAction->setChecked(loopPlaylist);
521
    }
522
    { // view settings
523
        KConfigGroupSaver saver(config, "View");
524 525 526 527 528

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

529 530 531
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
532
    }
533 534
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
535

536 537
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
538

539 540
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
541

542 543
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
544

545 546
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
547
    }
548

549
    m_toggleSplashAction->setChecked(m_showSplash);
550 551 552 553
}

void JuK::saveConfig()
{
554
    KConfig *config = KGlobal::config();
555
    { // m_player settings
556
        KConfigGroupSaver saver(config, "Player");
557 558
        if(m_sliderAction && m_sliderAction->getVolumeSlider())
            config->writeEntry("Volume", m_sliderAction->getVolumeSlider()->value());
559 560 561 562
        if(m_randomPlayAction)
            config->writeEntry("RandomPlay", m_randomPlayAction->isChecked());
        if(m_loopPlaylistAction)
            config->writeEntry("LoopPlaylist", m_loopPlaylistAction->isChecked());
563
    }
564 565
    { // view settings
        KConfigGroupSaver saver(config, "View");
566

567
        config->writeEntry("ShowEditor", m_showEditorAction->isChecked());
568
        config->writeEntry("ShowSearch", m_showSearchAction->isChecked());
569
    }
570 571
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
572 573 574 575 576 577
        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());
578
    }
579
    config->sync();
580 581
}

582
bool JuK::queryExit()
583
{
584
    stop();
585
    delete m_player;
586
    Cache::instance()->save();
587
    saveConfig();
588
    delete m_splitter;
589
    return true;
590 591
}

592 593
bool JuK::queryClose()
{
594
    if(!m_shuttingDown && m_systemTray && m_toggleDockOnCloseAction->isChecked()) {
595 596
	KMessageBox::information(this,
				 i18n("<qt>Closing the main window will keep JuK running in the system tray. "
597
				      "Use Quit from the File menu to quit the application.</qt>"),
598 599 600 601 602 603 604 605
				 i18n("Docking in System Tray"), "hideOnCloseInfo");
	hide();
	return false;
    }
    else
	return true;
}

606
void JuK::invokeEditSlot(const char *slotName, const char *slot)
607 608
{
    QObject *object = focusWidget();
609

610
    if(!object || !slotName || !slot)
611
        return;
612

613 614
    QMetaObject *meta = object->metaObject();
    QStrList l = meta->slotNames(true);
615

616
    if(l.find(slotName) == -1)
617
        return;
618

619 620 621
    connect(this, SIGNAL(signalEdit()), object, slot);
    emit signalEdit();
    disconnect(this, SIGNAL(signalEdit()), object, slot);
622 623
}

624 625 626
QString JuK::playingString() const
{
    QString s;
627 628
    if(!m_player->playing() && !m_player->paused())
	return i18n("No song playing");
629

630
    if(m_splitter->playingArtist().isEmpty())
631
        s = m_splitter->playingTrack().simplifyWhiteSpace();
632
    else
633
        s = m_splitter->playingArtist().simplifyWhiteSpace() + " - " + m_splitter->playingTrack().simplifyWhiteSpace();
634 635 636 637

    return s;
}

638 639
void JuK::updatePlaylistInfo()
{
Frerich Raabe's avatar
Frerich Raabe committed
640
    m_statusLabel->setPlaylistInfo(m_splitter->visiblePlaylistName(), m_splitter->selectedPlaylistCount());
641 642 643 644
}

void JuK::play(const QString &file)
{
645
    if(!m_player || !m_sliderAction || !m_sliderAction->getVolumeSlider())
646
        return;
647 648 649 650

    float volume = float(m_sliderAction->getVolumeSlider()->value()) / float(m_sliderAction->getVolumeSlider()->maxValue());

    if(m_player->paused())
651
        m_player->stop();
652

653 654 655 656 657
    m_player->play(file, volume);

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

    if(m_player->playing()) {
658 659 660
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
661

662
        m_backAction->setEnabled(true);
663

664 665 666
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
667

668
        m_statusLabel->setPlayingItemInfo(playingString(), m_splitter->playingList());
669

670
        emit signalNewSong(playingString());
671

672 673
        if(m_systemTray)
            m_systemTray->slotPlay();
674 675
    }
    else
676
        stop();
677 678
}

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
KAction *JuK::createSplitterAction(const QString &text, const char *slot, const char *name, 
				   const QString &pix, const KShortcut &shortcut)
{
    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;
}

694 695 696 697
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

698
void JuK::slotPlaylistChanged()
699
{
700
    if(m_splitter->collectionListSelected() || !m_splitter->hasListSelected()) {
701 702 703
        actionCollection()->action("file_save")->setEnabled(false);
        actionCollection()->action("file_save_as")->setEnabled(false);
        actionCollection()->action("renamePlaylist")->setEnabled(false);
704
        actionCollection()->action("reloadPlaylist")->setEnabled(false);
705
        actionCollection()->action("deleteItemPlaylist")->setEnabled(false);
706 707
    }
    else {
708 709 710 711
        actionCollection()->action("file_save")->setEnabled(true);
        actionCollection()->action("file_save_as")->setEnabled(true);
        actionCollection()->action("renamePlaylist")->setEnabled(true);
        actionCollection()->action("deleteItemPlaylist")->setEnabled(true);
712 713 714 715 716

	if(m_splitter->fileBasedListSelected() || m_splitter->dynamicListSelected())
	    actionCollection()->action("reloadPlaylist")->setEnabled(true);
	else
	    actionCollection()->action("reloadPlaylist")->setEnabled(false);
717
    }
718 719 720 721 722
    if(m_splitter->hasListSelected())
        actionCollection()->action("duplicatePlaylist")->setEnabled(true);
    else
        actionCollection()->action("duplicatePlaylist")->setEnabled(false);
    
723 724 725 726

    updatePlaylistInfo();
}

727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
////////////////////////////////////////////////////////////////////////////////
// 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()));
}

756 757 758 759
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

760
void JuK::slotShowGenreListEditor()
761 762 763 764
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
765

766
void JuK::slotToggleSystemTray(bool enabled)
767
{
768
    if(enabled && !m_systemTray)
769
	setupSystemTray();
770 771 772
    else if(!enabled && m_systemTray) {
	delete m_systemTray;
	m_systemTray = 0;
773
	m_toggleDockOnCloseAction->setEnabled(false);
774 775 776
    }
}

777
void JuK::slotSetOutput(int output)
778
{
779
    stop();
780 781
    delete m_player;
    m_player = Player::createPlayer(output);
782 783
}

784 785 786 787 788
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

789
////////////////////////////////////////////////////////////////////////////////
790
// additional player slots
791 792
////////////////////////////////////////////////////////////////////////////////

793
void JuK::slotTrackPositionSliderClicked()
794
{
795
    m_trackPositionDragging = true;
796 797
}

798
void JuK::slotTrackPositionSliderReleased()
799
{
800
    if(!m_player)
801 802
	return;

803 804
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
805 806
}

807
void JuK::slotTrackPositionSliderUpdate(int position)
808
{
809
    if(!m_player)
810 811
	return;

812 813
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
814 815 816 817 818 819 820 821 822 823

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

	m_statusLabel->setItemCurrentTime(seekTime);
    }
824 825
}

826 827
// This method is called when the play timer has expired.

828
void JuK::slotPollPlay()
829
{
830
    if(!m_player)
831 832
	return;

833
    // Our locking mechanism.  Since this method adjusts the play slider, we
834
    // want to make sure that our adjustments
835
    m_noSeek = true;
836

837
    if(!m_player->playing()) {
838

839
        m_playTimer->stop();
840

841
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(), m_loopPlaylistAction->isChecked()));
842
    }
843 844 845 846
    else if(!m_trackPositionDragging) {
        m_sliderAction->getTrackPositionSlider()->setValue(m_player->position());
	m_statusLabel->setItemTotalTime(m_player->totalTime());
	m_statusLabel->setItemCurrentTime(m_player->currentTime());
847 848 849
    }

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

854
    if(m_player->playing() && m_player->totalTime() > 0 && float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
855
        m_playTimer->changeInterval(50);
856

857
    m_noSeek = false;
858 859
}

860
void JuK::slotSetVolume(int volume)
861
{
862 863 864
    if(m_player && m_sliderAction && m_sliderAction->getVolumeSlider() &&
       m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
865
    {
866
        m_player->setVolume(float(volume) / float(m_sliderAction->getVolumeSlider()->maxValue()));
867 868
    }
}
869

870 871 872 873 874 875
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

876
#include "juk.moc"