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

40
JuK::JuK(QWidget *parent, const char *name) : KMainWindow(parent, name, WDestructiveClose),
41
					      DCOPObject("Player"),
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

void JuK::setVolume(float volume)
{
    if(m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
    {
        slotSetVolume(volume / 100 * m_sliderAction->getVolumeSlider()->maxValue());
    }
}

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 252
    m_splitter = new PlaylistSplitter(this, m_restore, "playlistSplitter");
    setCentralWidget(m_splitter);
253 254

    // playlist item activation connection
255
    connect(m_splitter, SIGNAL(signalDoubleClicked()), 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
    createSplitterAction(i18n("New Playlist..."),    SLOT(slotCreatePlaylist()),    "file_new",           "filenew",  "CTRL+n");
    createSplitterAction(i18n("Open..."),            SLOT(slotOpen()),              "file_open",          "fileopen", "CTRL+o");
278 279 280
    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
281
    createSplitterAction(i18n("Save"),               SLOT(slotSavePlaylist()),      "file_save",          "filesave", "CTRL+s");
282 283
    createSplitterAction(i18n("Save As..."),         SLOT(slotSaveAsPlaylist()),    "file_save_as",       "filesaveas");
    createSplitterAction(i18n("R&emove"),            SLOT(slotDeletePlaylist()),    "deleteItemPlaylist", "edittrash");
284

285
    KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
286

287
    //////////////////////////////////////////////////
288
    // edit menu
289 290 291 292
    //////////////////////////////////////////////////

    KStdAction::cut(this,   SLOT(cut()),   actionCollection());
    KStdAction::copy(this,  SLOT(copy()),  actionCollection());
293
    KStdAction::paste(this, SLOT(paste()), actionCollection());
294

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

297
    KStdAction::selectAll(this, SLOT(selectAll()), actionCollection());
298

299
    //////////////////////////////////////////////////
300
    // view menu
301
    //////////////////////////////////////////////////
302

303 304
    m_showSearchAction = new KToggleAction(i18n("Show &Search Bar"), "filefind", 0, actionCollection(), "showSearch");
    m_showEditorAction = new KToggleAction(i18n("Show &Tag Editor"), "edit",     0, actionCollection(), "showEditor");
305

306
    createSplitterAction(i18n("Refresh Items"), SLOT(slotRefresh()), "refresh", "reload");
307

308
    //////////////////////////////////////////////////
309
    // play menu
310 311
    //////////////////////////////////////////////////

312
    m_randomPlayAction = new KToggleAction(i18n("&Random Play"), 0, actionCollection(), "randomPlay");
313 314 315 316 317

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

318
    m_backAction = new KToolBarPopupAction(i18n("Skip &Back"), "player_start", 0, this, SLOT(back()), actionCollection(), "back");
319
    connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),  this, SLOT(slotPopulateBackMenu()));
320
    connect(m_backAction->popupMenu(), SIGNAL(activated(int)), this, SLOT(back(int)));
321 322 323

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

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

326
    //////////////////////////////////////////////////
327
    // tagger menu
328 329 330 331
    //////////////////////////////////////////////////

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

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

335 336 337 338 339 340 341 342
    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

    //////////////////////////////////////////////////
343
    // settings menu
344 345
    //////////////////////////////////////////////////

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

348
    setStandardToolBarMenuEnabled(true);
349

350 351 352 353
    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");
354

355
    connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)), this, SLOT(slotToggleSystemTray(bool)));
356

357

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

360
    m_outputSelectAction = Player::playerSelectAction(actionCollection());
361

362
    if(m_outputSelectAction) {
Frerich Raabe's avatar
Frerich Raabe committed
363 364
        m_outputSelectAction->setCurrentItem(0);
        connect(m_outputSelectAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
365
    }
366

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

369 370
    KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());

371
    //////////////////////////////////////////////////
372
    // just in the toolbar
373 374
    //////////////////////////////////////////////////

375
    m_sliderAction = new SliderAction(i18n("Track Position"), actionCollection(), "trackPositionAction");
376 377

    createGUI();
378 379

    // set the slider to the proper orientation and make it stay that way
380
    m_sliderAction->slotUpdateOrientation();
381 382
}

383 384 385 386 387 388
void JuK::setupSplitterConnections()
{
    QValueListConstIterator<SplitterConnection> it = m_splitterConnections.begin();
    for(; it != m_splitterConnections.end(); ++it)
        connect((*it).first, SIGNAL(activated()), m_splitter, (*it).second);

389 390
    actionCollection()->insert(m_splitter->columnVisibleAction());

391 392 393
    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 *)));
394
    connect(m_splitter, SIGNAL(signalPlaylistChanged()), this, SLOT(slotPlaylistChanged()));
395 396
}

397
void JuK::setupSystemTray()
398
{
399
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
Frerich Raabe's avatar
Frerich Raabe committed
400 401
        m_systemTray = new SystemTray(this, "systemTray");
        m_systemTray->show();
402

Frerich Raabe's avatar
Frerich Raabe committed
403
        connect(this, SIGNAL(signalNewSong(const QString&)), m_systemTray, SLOT(slotNewSong(const QString&)));
404

Frerich Raabe's avatar
Frerich Raabe committed
405 406 407 408
        if(m_player && m_player->paused())
            m_systemTray->slotPause();
        else if(m_player && m_player->playing())
            m_systemTray->slotPlay();
409

Frerich Raabe's avatar
Frerich Raabe committed
410
        m_toggleDockOnCloseAction->setEnabled(true);
411

Frerich Raabe's avatar
Frerich Raabe committed
412
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotQuit()));
413
    }
414
    else {
Frerich Raabe's avatar
Frerich Raabe committed
415 416
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
417
    }
418 419
}

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

430
    m_playTimer = new QTimer(this);
431
    connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
432

433
    if(m_sliderAction && m_sliderAction->getTrackPositionSlider() && m_sliderAction->getVolumeSlider()) {
434 435 436
        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()));
437
        m_sliderAction->getTrackPositionSlider()->setEnabled(false);
438

439
        connect(m_sliderAction->getVolumeSlider(), SIGNAL(valueChanged(int)), this, SLOT(slotSetVolume(int)));
440
    }
441

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

448
    m_player = Player::createPlayer(playerType);
449 450
}

451

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

465 466 467 468 469
    m_accel->setConfigGroup("Shortcuts");
    m_accel->readSettings();
    m_accel->updateConnections();
}

470 471 472 473
void JuK::processArgs()
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
474

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

478
    m_splitter->open(files);
479 480
}

481 482 483
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
Frerich Raabe's avatar
Frerich Raabe committed
484
        e->accept();
485 486 487
    KMainWindow::keyPressEvent(e);
}

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

501 502
void JuK::readConfig()
{
503
    // Automagically save and m_restore many window settings.
504 505
    setAutoSaveSettings();

506
    KConfig *config = KGlobal::config();
507
    { // player settings
508
        KConfigGroupSaver saver(config, "Player");
509
        if(m_sliderAction->getVolumeSlider()) {
510 511
            int volume = config->readNumEntry("Volume", m_sliderAction->getVolumeSlider()->maxValue());
            m_sliderAction->getVolumeSlider()->setValue(volume);
512
        }
Frerich Raabe's avatar
Frerich Raabe committed
513 514
        bool randomPlay = config->readBoolEntry("RandomPlay", false);
        m_randomPlayAction->setChecked(randomPlay);
515

Frerich Raabe's avatar
Frerich Raabe committed
516 517
        bool loopPlaylist = config->readBoolEntry("LoopPlaylist", false);
        m_loopPlaylistAction->setChecked(loopPlaylist);
518
    }
519
    { // view settings
520
        KConfigGroupSaver saver(config, "View");
521 522 523 524 525

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

Frerich Raabe's avatar
Frerich Raabe committed
526 527 528
        bool showEditor = config->readBoolEntry("ShowEditor", false);
        m_showEditorAction->setChecked(showEditor);
        m_splitter->slotSetEditorVisible(showEditor);
529
    }
530 531
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
532

Frerich Raabe's avatar
Frerich Raabe committed
533 534
        bool dockInSystemTray = config->readBoolEntry("DockInSystemTray", true);
        m_toggleSystemTrayAction->setChecked(dockInSystemTray);
535

Frerich Raabe's avatar
Frerich Raabe committed
536 537
        bool dockOnClose = config->readBoolEntry("DockOnClose", true);
        m_toggleDockOnCloseAction->setChecked(dockOnClose);
538

Frerich Raabe's avatar
Frerich Raabe committed
539 540
        bool showPopups = config->readBoolEntry("TrackPopup", false);
        m_togglePopupsAction->setChecked(showPopups);
541

Frerich Raabe's avatar
Frerich Raabe committed
542 543
        if(m_outputSelectAction)
            m_outputSelectAction->setCurrentItem(config->readNumEntry("MediaSystem", 0));
544
    }
545

546
    m_toggleSplashAction->setChecked(m_showSplash);
547 548 549 550
}

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

Frerich Raabe's avatar
Frerich Raabe committed
564
        config->writeEntry("ShowEditor", m_showEditorAction->isChecked());
565
        config->writeEntry("ShowSearch", m_showSearchAction->isChecked());
566
    }
567 568
    { // general settings
        KConfigGroupSaver saver(config, "Settings");
Frerich Raabe's avatar
Frerich Raabe committed
569 570 571 572 573 574
        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());
575
    }
576
    config->sync();
577 578
}

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

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

603
void JuK::invokeEditSlot(const char *slotName, const char *slot)
604 605
{
    QObject *object = focusWidget();
606

607
    if(!object || !slotName || !slot)
Frerich Raabe's avatar
Frerich Raabe committed
608
        return;
609

610 611
    QMetaObject *meta = object->metaObject();
    QStrList l = meta->slotNames(true);
612

613
    if(l.find(slotName) == -1)
Frerich Raabe's avatar
Frerich Raabe committed
614
        return;
615

616 617 618
    connect(this, SIGNAL(signalEdit()), object, slot);
    emit signalEdit();
    disconnect(this, SIGNAL(signalEdit()), object, slot);
619 620
}

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

627
    if(m_splitter->playingArtist().isEmpty())
Frerich Raabe's avatar
Frerich Raabe committed
628
        s = m_splitter->playingTrack().simplifyWhiteSpace();
629
    else
Frerich Raabe's avatar
Frerich Raabe committed
630
        s = m_splitter->playingArtist().simplifyWhiteSpace() + " - " + m_splitter->playingTrack().simplifyWhiteSpace();
631 632 633 634

    return s;
}

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

void JuK::play(const QString &file)
{
642
    if(!m_player || !m_sliderAction || !m_sliderAction->getVolumeSlider())
Frerich Raabe's avatar
Frerich Raabe committed
643
        return;
644 645 646 647

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

    if(m_player->paused())
Frerich Raabe's avatar
Frerich Raabe committed
648
        m_player->stop();
649

650 651 652 653 654
    m_player->play(file, volume);

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

    if(m_player->playing()) {
655 656 657
        actionCollection()->action("pause")->setEnabled(true);
        actionCollection()->action("stop")->setEnabled(true);
        actionCollection()->action("forward")->setEnabled(true);
658

Frerich Raabe's avatar
Frerich Raabe committed
659
        m_backAction->setEnabled(true);
660

Frerich Raabe's avatar
Frerich Raabe committed
661 662 663
        m_sliderAction->getTrackPositionSlider()->setValue(0);
        m_sliderAction->getTrackPositionSlider()->setEnabled(true);
        m_playTimer->start(m_pollInterval);
664

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

Frerich Raabe's avatar
Frerich Raabe committed
667
        emit signalNewSong(playingString());
668

Frerich Raabe's avatar
Frerich Raabe committed
669 670
        if(m_systemTray)
            m_systemTray->slotPlay();
671 672
    }
    else
Frerich Raabe's avatar
Frerich Raabe committed
673
        stop();
674 675
}

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
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;
}

691 692 693 694
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

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

    updatePlaylistInfo();
}

718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
////////////////////////////////////////////////////////////////////////////////
// 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()));
}

747 748 749 750
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

751
void JuK::slotShowGenreListEditor()
752 753 754 755
{
    GenreListEditor * editor = new GenreListEditor();
    editor->exec();
}
756

757
void JuK::slotToggleSystemTray(bool enabled)
758
{
759
    if(enabled && !m_systemTray)
760
	setupSystemTray();
761 762 763
    else if(!enabled && m_systemTray) {
	delete m_systemTray;
	m_systemTray = 0;
764
	m_toggleDockOnCloseAction->setEnabled(false);
765 766 767
    }
}

768
void JuK::slotSetOutput(int output)
769
{
770
    stop();
771 772
    delete m_player;
    m_player = Player::createPlayer(output);
773 774
}

775 776 777 778 779
void JuK::slotEditKeys()
{
    KeyDialog::configure(m_accel, actionCollection(), this);
}

780
////////////////////////////////////////////////////////////////////////////////
781
// additional player slots
782 783
////////////////////////////////////////////////////////////////////////////////

784
void JuK::slotTrackPositionSliderClicked()
785
{
786
    m_trackPositionDragging = true;
787 788
}

789
void JuK::slotTrackPositionSliderReleased()
790
{
791
    if(!m_player)
792 793
	return;

794 795
    m_trackPositionDragging = false;
    m_player->seekPosition(m_sliderAction->getTrackPositionSlider()->value());
796 797
}

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

803 804
    if(m_player->playing() && !m_trackPositionDragging && !m_noSeek)
        m_player->seekPosition(position);
805 806 807 808 809 810 811 812 813 814

    // 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);
    }
815 816
}

817 818
// This method is called when the play timer has expired.

819
void JuK::slotPollPlay()
820
{
821
    if(!m_player)
822 823
	return;

824
    // Our locking mechanism.  Since this method adjusts the play slider, we
825
    // want to make sure that our adjustments
826
    m_noSeek = true;
827

828
    if(!m_player->playing()) {
829

830
        m_playTimer->stop();
831

832
	play(m_splitter->playNextFile(m_randomPlayAction->isChecked(), m_loopPlaylistAction->isChecked()));
833
    }
834 835 836 837
    else if(!m_trackPositionDragging) {
        m_sliderAction->getTrackPositionSlider()->setValue(m_player->position());
	m_statusLabel->setItemTotalTime(m_player->totalTime());
	m_statusLabel->setItemCurrentTime(m_player->currentTime());
838 839 840
    }

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

845
    if(m_player->playing() && m_player->totalTime() > 0 && float(m_player->totalTime() - m_player->currentTime()) < m_pollInterval * 2)
846
        m_playTimer->changeInterval(50);
847

848
    m_noSeek = false;
849 850
}

851
void JuK::slotSetVolume(int volume)
852
{
853 854 855
    if(m_player && m_sliderAction && m_sliderAction->getVolumeSlider() &&
       m_sliderAction->getVolumeSlider()->maxValue() > 0 &&
       volume >= 0 && m_sliderAction->getVolumeSlider()->maxValue() >= volume)
856
    {
857
        m_player->setVolume(float(volume) / float(m_sliderAction->getVolumeSlider()->maxValue()));
858 859
    }
}
860

861 862 863 864 865 866
void JuK::slotConfigureTagGuesser()
{
    TagGuesserConfigDlg dlg(this);
    dlg.exec();
}

867
#include "juk.moc"