juk.cpp 22 KB
Newer Older
1
2
/**
 * Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org>
3
 * Copyright (C) 2008, 2009, 2017 Michael Pyne <mpyne@kde.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
17

18
19
#include "juk.h"

Michael Pyne's avatar
Michael Pyne committed
20
#include <KSharedConfig>
21
#include <kaboutdata.h>
Laurent Montel's avatar
Poirt    
Laurent Montel committed
22
#include <kactioncollection.h>
Laurent Montel's avatar
Laurent Montel committed
23
#include <kactionmenu.h>
24
#include <kconfiggroup.h>
25
#include <kglobalaccel.h>
Michael Pyne's avatar
Michael Pyne committed
26
#include <kmessagebox.h>
27
#include <knotification.h>
Michael Pyne's avatar
Michael Pyne committed
28
29
30
#include <kstandardaction.h>
#include <ktoggleaction.h>
#include <ktoolbarpopupaction.h>
31

32
#include <QAction>
33
#include <QCoreApplication>
Michael Pyne's avatar
Michael Pyne committed
34
35
36
37
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusReply>
#include <QDesktopWidget>
38
#include <QDir>
39
#include <QDirIterator>
Michael Pyne's avatar
Michael Pyne committed
40
41
#include <QIcon>
#include <QKeyEvent>
42
#include <QMenuBar>
43
#include <QMetaType>
44
#include <QScreen>
Michael Pyne's avatar
Michael Pyne committed
45
#include <QStatusBar>
Michael Pyne's avatar
Michael Pyne committed
46
47
#include <QTime>
#include <QTimer>
48

49
#include "actioncollection.h"
50
#include "cache.h"
51
#include "collectionlist.h"
52
#include "covermanager.h"
Michael Pyne's avatar
Michael Pyne committed
53
54
55
56
57
58
59
60
61
#include "filerenamerconfigdlg.h"
#include "keydialog.h"
#include "playlistsplitter.h"
#include "scrobbleconfigdlg.h"
#include "scrobbler.h"
#include "slideraction.h"
#include "statuslabel.h"
#include "systemtray.h"
#include "tagguesserconfigdlg.h"
62
#include "tagtransactionmanager.h"
Michael Pyne's avatar
Michael Pyne committed
63

Michael Pyne's avatar
Michael Pyne committed
64
#include "juk_debug.h"
65

66
67
using namespace ActionCollection;

68
69
JuK* JuK::m_instance;

70
71
72
73
74
75
76
template<class T>
void deleteAndClear(T *&ptr)
{
    delete ptr;
    ptr = 0;
}

77
78
79
80
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

Michael Pyne's avatar
Michael Pyne committed
81
82
83
84
JuK::JuK(const QStringList &filesToOpen, QWidget *parent)
  : KXmlGuiWindow(parent, Qt::WindowFlags(Qt::WA_DeleteOnClose))
  , m_player(new PlayerManager)
  , m_filesToOpen(filesToOpen)
85
{
86
87
    // Expect segfaults if you change this order.

88
89
90
91
    // Allow to be passed across threads
    qRegisterMetaType<FileHandle>();
    qRegisterMetaType<FileHandleList>();

92
93
    m_instance = this;

94
    readSettings();
95

96
97
    Cache::ensureAppDataStorageExists();

98
    setupActions();
Michael Pyne's avatar
Michael Pyne committed
99
    setupLayout(); // Creates PlaylistSplitter and therefore CollectionList
100

101
    bool firstRun = !KSharedConfig::openConfig()->hasGroup("MainWindow");
102
103

    if(firstRun) {
104
        KConfigGroup mainWindowConfig(KSharedConfig::openConfig(), "MainWindow");
105
106
107
108
        KConfigGroup playToolBarConfig(&mainWindowConfig, "Toolbar playToolBar");
        playToolBarConfig.writeEntry("ToolButtonStyle", "IconOnly");
    }

109
110
    QSize defaultSize(800, 480);

111
    if(QApplication::isRightToLeft())
112
        setupGUI(defaultSize, ToolBar | Save | Create, "jukui-rtl.rc");
113
    else
114
115
116
117
        setupGUI(defaultSize, ToolBar | Save | Create);

    // Center the GUI if this is our first run ever.

118
    if(firstRun) {
119
        QRect r = rect();
120
121
        const QRect screenCenter = QApplication::primaryScreen()->availableGeometry();
        r.moveCenter(screenCenter.center());
122
123
        move(r.topLeft());
    }
124

Michael Pyne's avatar
Michael Pyne committed
125
    connect(m_splitter, SIGNAL(guiReady()), SLOT(slotSetupSystemTray()));
126
    readConfig();
127
    setupGlobalAccels();
128
    activateScrobblerIfEnabled();
129

130
131
132
133
134
    QDBusInterface *pmInterface = new QDBusInterface(QStringLiteral("org.kde.Solid.PowerManagement"),
                                       QStringLiteral("/org/freedesktop/PowerManagement/Inhibit"),
                                       QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
                                       QDBusConnection::sessionBus());

135
    connect(m_player, &PlayerManager::signalPlay, pmInterface, [=] () {
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
        QDBusReply<uint> reply;
        if (pmInterface->isValid() && (m_pmToken == 0)) {
            reply = pmInterface->call(QStringLiteral("Inhibit"),
                               KAboutData::applicationData().componentName(),
                               QStringLiteral("playing audio"));
            if (reply.isValid()) {
                m_pmToken = reply.value();
            }
        }
    });

    auto uninhibitPowerManagement = [=] () {
        QDBusMessage reply;
        if (pmInterface->isValid() && (m_pmToken != 0)) {
            reply = pmInterface->call(QStringLiteral("UnInhibit"), m_pmToken);
            if (reply.errorName().isEmpty()) {
                m_pmToken = 0;
            }
        }
    };

    connect(m_player, &PlayerManager::signalPause, uninhibitPowerManagement);
    connect(m_player, &PlayerManager::signalStop, uninhibitPowerManagement);

160
161
162
163
164
    // The system tray quit command will go straight to qApp->quit without calling
    // our quit action, so make sure we save config changes no matter how quit is
    // called.
    connect(qApp, &QCoreApplication::aboutToQuit, this, [this]() { saveConfig(); });

Michael Pyne's avatar
Michael Pyne committed
165
166
    // slotCheckCache loads the cached entries first to populate the collection list

Michael Pyne's avatar
Michael Pyne committed
167
    QTimer::singleShot(0, this, SLOT(slotClearOldCovers()));
168
    QTimer::singleShot(0, CollectionList::instance(), SLOT(startLoadingCachedItems()));
169
    QTimer::singleShot(0, this, SLOT(slotProcessArgs()));
170
171
172
173
}

JuK::~JuK()
{
174
175
176
    if(!m_shuttingDown)
        slotQuit();

Michael Pyne's avatar
Michael Pyne committed
177
178
179
180
181
182
    // Some items need to be deleted before others, though I haven't looked
    // at this in some time so refinement is probably possible.
    delete m_systemTray;
    delete m_splitter;
    delete m_player;
    delete m_statusLabel;
183
184
185
186
187

    // Sometimes KMainWindow doesn't actually call QCoreApplication::quit
    // after queryClose, even if not in a session shutdown, so make sure to
    // do so ourselves when closing the main window.
    QTimer::singleShot(0, qApp, &QCoreApplication::quit);
188
189
}

190
191
192
193
194
JuK* JuK::JuKInstance()
{
    return m_instance;
}

195
196
197
198
199
PlayerManager *JuK::playerManager() const
{
    return m_player;
}

200
201
202
void JuK::coverDownloaded(const QPixmap &cover)
{
    QString event(cover.isNull() ? "coverFailed" : "coverDownloaded");
203
204
    KNotification *notification = new KNotification(event);
    notification->setWidget(this);
205
206
207
208
209
210
211
212
213
214
215
    notification->setPixmap(cover);
    notification->setFlags(KNotification::CloseOnTimeout);

    if(cover.isNull())
        notification->setText(i18n("Your album art failed to download."));
    else
        notification->setText(i18n("Your album art has finished downloading."));

    notification->sendEvent();
}

216
217
218
219
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

220
221
void JuK::setupLayout()
{
222
    m_splitter = new PlaylistSplitter(m_player, this);
223
    setCentralWidget(m_splitter);
224

225
    m_statusLabel = new StatusLabel(*m_splitter->playlist(), statusBar());
226
    statusBar()->addWidget(m_statusLabel, 1);
227
228
229
230
231
232
    connect(m_player, &PlayerManager::tick, m_statusLabel,
            &StatusLabel::setItemCurrentTime);
    connect(m_player, &PlayerManager::totalTimeChanged,
            m_statusLabel, &StatusLabel::setItemTotalTime);
    connect(m_splitter, &PlaylistSplitter::currentPlaylistChanged,
            m_statusLabel, &StatusLabel::slotCurrentPlaylistHasChanged);
233

234
    m_splitter->setFocus();
235
236
}

237
238
void JuK::setupActions()
{
239
    KActionCollection *collection = ActionCollection::actions();
Michael Pyne's avatar
Michael Pyne committed
240

241
    // Setup KDE standard actions that JuK uses.
242

Aaron J. Seigo's avatar
Aaron J. Seigo committed
243
244
245
246
247
    KStandardAction::quit(this, SLOT(slotQuit()), collection);
    KStandardAction::undo(this, SLOT(slotUndo()), collection);
    KStandardAction::cut(collection);
    KStandardAction::copy(collection);
    KStandardAction::paste(collection);
248
    QAction *clear = KStandardAction::clear(collection);
Aaron J. Seigo's avatar
Aaron J. Seigo committed
249
250
    KStandardAction::selectAll(collection);
    KStandardAction::keyBindings(this, SLOT(slotEditKeys()), collection);
251
    KStandardAction::showMenubar(menuBar(), SLOT(setVisible(bool)), collection);
Scott Wheeler's avatar
Scott Wheeler committed
252

253
    // Setup the menu which handles the random play options.
254
255
    KActionMenu *actionMenu = collection->add<KActionMenu>("actionMenu");
    actionMenu->setText(i18n("&Random Play"));
256
    actionMenu->setIcon(QIcon::fromTheme(QLatin1String("media-playlist-shuffle")));
257
    actionMenu->setPopupMode(QToolButton::InstantPopup);
258

259
    QActionGroup* randomPlayGroup = new QActionGroup(this);
David Faure's avatar
David Faure committed
260

261
262
    QAction *act = collection->add<KToggleAction>("disableRandomPlay");
    act->setText(i18n("&Disable Random Play"));
263
    act->setIcon(QIcon::fromTheme(QLatin1String("go-down")));
264
    act->setActionGroup(randomPlayGroup);
Stephan Kulow's avatar
Stephan Kulow committed
265
    actionMenu->addAction(act);
266

Laurent Montel's avatar
Port++    
Laurent Montel committed
267
268
    m_randomPlayAction = collection->add<KToggleAction>("randomPlay");
    m_randomPlayAction->setText(i18n("Use &Random Play"));
269
    m_randomPlayAction->setIcon(QIcon::fromTheme(QLatin1String("media-playlist-shuffle")));
David Faure's avatar
David Faure committed
270
    m_randomPlayAction->setActionGroup(randomPlayGroup);
Stephan Kulow's avatar
Stephan Kulow committed
271
    actionMenu->addAction(m_randomPlayAction);
272

273
274
    act = collection->add<KToggleAction>("albumRandomPlay");
    act->setText(i18n("Use &Album Random Play"));
275
    act->setIcon(QIcon::fromTheme(QLatin1String("media-playlist-shuffle")));
276
    act->setActionGroup(randomPlayGroup);
277
278
    connect(act, &QAction::toggled,
            this, &JuK::slotCheckAlbumNextAction);
Stephan Kulow's avatar
Stephan Kulow committed
279
    actionMenu->addAction(act);
280

281
    act = collection->addAction("removeFromPlaylist", clear, SLOT(clear()));
Laurent Montel's avatar
Port++    
Laurent Montel committed
282
    act->setText(i18n("Remove From Playlist"));
Michael Pyne's avatar
Michael Pyne committed
283
    act->setIcon(QIcon::fromTheme( QLatin1String( "list-remove" )));
Laurent Montel's avatar
Port++    
Laurent Montel committed
284

285
286
    act = collection->addAction("play", m_player, SLOT(play()));
    act->setText(i18n("&Play"));
Michael Pyne's avatar
Michael Pyne committed
287
    act->setIcon(QIcon::fromTheme( QLatin1String( "media-playback-start" )));
288

289
    act = collection->addAction("pause", m_player, SLOT(pause()));
290
    act->setEnabled(false);
291
    act->setText(i18n("P&ause"));
Michael Pyne's avatar
Michael Pyne committed
292
    act->setIcon(QIcon::fromTheme( QLatin1String( "media-playback-pause" )));
293

294
    act = collection->addAction("stop", m_player, SLOT(stop()));
295
    act->setEnabled(false);
296
    act->setText(i18n("&Stop"));
Michael Pyne's avatar
Michael Pyne committed
297
    act->setIcon(QIcon::fromTheme( QLatin1String( "media-playback-stop" )));
Scott Wheeler's avatar
Scott Wheeler committed
298

Michael Pyne's avatar
Michael Pyne committed
299
    act = new KToolBarPopupAction(QIcon::fromTheme( QLatin1String( "media-skip-backward") ), i18nc("previous track", "Previous" ), collection);
300
    act->setEnabled(false);
301
    collection->addAction("back", act);
302
303
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(back()));

304
    act = collection->addAction("forward", m_player, SLOT(forward()));
305
    act->setEnabled(false);
306
    act->setText(i18nc("next track", "&Next"));
Michael Pyne's avatar
Michael Pyne committed
307
    act->setIcon(QIcon::fromTheme( QLatin1String( "media-skip-forward" )));
308

309
310
    act = collection->addAction("loopPlaylist");
    act->setText(i18n("&Loop Playlist"));
311
312
    act->setCheckable(true);

313
314
    act = collection->add<KToggleAction>("resizeColumnsManually");
    act->setText(i18n("&Resize Playlist Columns Manually"));
315
316

    // the following are not visible by default
317

318
319
    act = collection->addAction("mute", m_player, SLOT(mute()));
    act->setText(i18nc("silence playback", "Mute"));
Michael Pyne's avatar
Michael Pyne committed
320
    act->setIcon(QIcon::fromTheme( QLatin1String( "audio-volume-muted" )));
321

322
323
    act = collection->addAction("volumeUp", m_player, SLOT(volumeUp()));
    act->setText(i18n("Volume Up"));
Michael Pyne's avatar
Michael Pyne committed
324
    act->setIcon(QIcon::fromTheme( QLatin1String( "audio-volume-high" )));
325

326
327
    act = collection->addAction("volumeDown", m_player, SLOT(volumeDown()));
    act->setText(i18n("Volume Down"));
Michael Pyne's avatar
Michael Pyne committed
328
    act->setIcon(QIcon::fromTheme( QLatin1String( "audio-volume-low" )));
329

330
331
    act = collection->addAction("playPause", m_player, SLOT(playPause()));
    act->setText(i18n("Play / Pause"));
Michael Pyne's avatar
Michael Pyne committed
332
    act->setIcon(QIcon::fromTheme( QLatin1String( "media-playback-start" )));
333

334
335
    act = collection->addAction("seekForward", m_player, SLOT(seekForward()));
    act->setText(i18n("Seek Forward"));
Michael Pyne's avatar
Michael Pyne committed
336
    act->setIcon(QIcon::fromTheme( QLatin1String( "media-seek-forward" )));
337

338
339
    act = collection->addAction("seekBack", m_player, SLOT(seekBack()));
    act->setText(i18n("Seek Back"));
Michael Pyne's avatar
Michael Pyne committed
340
    act->setIcon(QIcon::fromTheme( QLatin1String( "media-seek-backward" )));
341

342
343
    act = collection->addAction("showHide", this, SLOT(slotShowHide()));
    act->setText(i18n("Show / Hide"));
Stephan Binner's avatar
Stephan Binner committed
344

345
    //////////////////////////////////////////////////
346
    // settings menu
347
348
    //////////////////////////////////////////////////

349
350
351
352
353
354
    m_toggleSystemTrayAction = collection->add<KToggleAction>("toggleSystemTray");
    m_toggleSystemTrayAction->setText(i18n("&Dock in System Tray"));
    connect(m_toggleSystemTrayAction, SIGNAL(triggered(bool)), SLOT(slotToggleSystemTray(bool)));

    m_toggleDockOnCloseAction = collection->add<KToggleAction>("dockOnClose");
    m_toggleDockOnCloseAction->setText(i18n("&Stay in System Tray on Close"));
355

356
357
    m_togglePopupsAction = collection->add<KToggleAction>("togglePopups");
    m_togglePopupsAction->setText(i18n("Popup &Track Announcement"));
358

359
360
    act = collection->add<KToggleAction>("saveUpcomingTracks");
    act->setText(i18n("Save &Play Queue on Exit"));
Scott Wheeler's avatar
Scott Wheeler committed
361

362
363
    act = collection->addAction("tagGuesserConfig", this, SLOT(slotConfigureTagGuesser()));
    act->setText(i18n("&Tag Guesser..."));
364

365
366
    act = collection->addAction("fileRenamerConfig", this, SLOT(slotConfigureFileRenamer()));
    act->setText(i18n("&File Renamer..."));
367

368
    act = collection->addAction("scrobblerConfig", this, SLOT(slotConfigureScrobbling()));
369
    act->setText(i18n("&Configure scrobbling..."));
370

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

375
376
377
    collection->addAction("trackPositionAction",
                          new TrackPositionAction(i18n("Track Position"), this));
    collection->addAction("volumeAction",
378
                          new VolumeAction(i18n("Volume"), this));
379

380
381
382
    ActionCollection::actions()->addAssociatedWidget(this);
    foreach (QAction* action, ActionCollection::actions()->actions())
        action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
383
384
}

Michael Pyne's avatar
Michael Pyne committed
385
void JuK::slotSetupSystemTray()
386
{
387
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
388
        m_systemTray = new SystemTray(m_player, this);
Michael Pyne's avatar
Michael Pyne committed
389
        m_systemTray->setObjectName(QStringLiteral("systemTray"));
390

391
392
        m_toggleDockOnCloseAction->setEnabled(true);
        m_togglePopupsAction->setEnabled(true);
Michael Pyne's avatar
Michael Pyne committed
393
394
395
396
397
398

        // If this flag gets set then JuK will quit if you click the cover on
        // the track announcement popup when JuK is only in the system tray
        // (the systray has no widget).

        qGuiApp->setQuitOnLastWindowClosed(false);
399
    }
400
    else {
Michael Pyne's avatar
Michael Pyne committed
401
        m_systemTray = nullptr;
402
403
        m_toggleDockOnCloseAction->setEnabled(false);
        m_togglePopupsAction->setEnabled(false);
404
    }
405
406
}

407
408
void JuK::setupGlobalAccels()
{
409
410
411
412
413
414
415
416
417
418
419
420
    KeyDialog::setupActionShortcut("play");
    KeyDialog::setupActionShortcut("playPause");
    KeyDialog::setupActionShortcut("stop");
    KeyDialog::setupActionShortcut("back");
    KeyDialog::setupActionShortcut("forward");
    KeyDialog::setupActionShortcut("seekBack");
    KeyDialog::setupActionShortcut("seekForward");
    KeyDialog::setupActionShortcut("volumeUp");
    KeyDialog::setupActionShortcut("volumeDown");
    KeyDialog::setupActionShortcut("mute");
    KeyDialog::setupActionShortcut("showHide");
    KeyDialog::setupActionShortcut("forwardAlbum");
421
422
}

423
void JuK::slotProcessArgs()
424
{
Luigi Toscano's avatar
Luigi Toscano committed
425
    CollectionList::instance()->addFiles(m_filesToOpen);
426
427
}

Michael Pyne's avatar
Michael Pyne committed
428
429
430
431
432
433
void JuK::slotClearOldCovers()
{
    // Find all saved covers from the previous run of JuK and clear them out, in case
    // we find our tracks in a different order this run, which would cause old saved
    // covers to be wrong.
    // See mpris2/mediaplayer2player.cpp
434
435
    QString tmpDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
    QStringList nameFilters;
Michael Pyne's avatar
Michael Pyne committed
436

437
438
439
440
    nameFilters << QStringLiteral("juk-cover-*.png");
    QDirIterator jukCoverIter(tmpDir, nameFilters);
    while (jukCoverIter.hasNext()) {
        QFile::remove(jukCoverIter.next());
Michael Pyne's avatar
Michael Pyne committed
441
442
443
    }
}

444
445
446
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
Frerich Raabe's avatar
Frerich Raabe committed
447
        e->accept();
Kevin Ottens's avatar
Kevin Ottens committed
448
    KXmlGuiWindow::keyPressEvent(e);
449
450
}

451
452
453
454
455
456
/**
 * These are settings that need to be know before setting up the GUI.
 */

void JuK::readSettings()
{
457
    KConfigGroup config(KSharedConfig::openConfig(), "Settings");
458
    m_startDocked = config.readEntry("StartDocked", false);
459
460
}

461
462
void JuK::readConfig()
{
463
    // player settings
464

465
    KConfigGroup playerConfig(KSharedConfig::openConfig(), "Player");
466

467
468
469
470
471
    if(m_player)
    {
        const int maxVolume = 100;
        const int volume = playerConfig.readEntry("Volume", maxVolume);
        m_player->setVolume(volume * 0.01);
472

473
474
        //bool enableCrossfade = playerConfig.readEntry("CrossfadeTracks", true);
        //m_player->setCrossfadeEnabled(enableCrossfade);
Michael Pyne's avatar
Michael Pyne committed
475
        //ActionCollection::action<QAction>("crossfadeTracks")->setChecked(enableCrossfade);
476
    }
477

478
479
    // Default to no random play

480
    ActionCollection::action<KToggleAction>("disableRandomPlay")->setChecked(true);
481

482
483
    QString randomPlayMode = playerConfig.readEntry("RandomPlay", "Disabled");
    if(randomPlayMode == "true" || randomPlayMode == "Normal")
484
        m_randomPlayAction->setChecked(true);
485
    else if(randomPlayMode == "AlbumRandomPlay")
Michael Pyne's avatar
Michael Pyne committed
486
        ActionCollection::action<QAction>("albumRandomPlay")->setChecked(true);
487

488
    bool loopPlaylist = playerConfig.readEntry("LoopPlaylist", false);
489
    ActionCollection::action<QAction>("loopPlaylist")->setChecked(loopPlaylist);
490

491
    // general settings
492

493
    KConfigGroup settingsConfig(KSharedConfig::openConfig(), "Settings");
494

495
    bool dockInSystemTray = settingsConfig.readEntry("DockInSystemTray", true);
496
497
    m_toggleSystemTrayAction->setChecked(dockInSystemTray);

498
    bool dockOnClose = settingsConfig.readEntry("DockOnClose", true);
499
500
    m_toggleDockOnCloseAction->setChecked(dockOnClose);

501
    bool showPopups = settingsConfig.readEntry("TrackPopup", false);
502
    m_togglePopupsAction->setChecked(showPopups);
503
504
505
506
}

void JuK::saveConfig()
{
507
    // player settings
508

509
    KConfigGroup playerConfig(KSharedConfig::openConfig(), "Player");
510

511
    if (m_player)
512
    {
513
        playerConfig.writeEntry("Volume", static_cast<int>(100.0 * m_player->volume()));
514
515
    }

516
517
    playerConfig.writeEntry("RandomPlay", m_randomPlayAction->isChecked());

Michael Pyne's avatar
Michael Pyne committed
518
    QAction *a = ActionCollection::action<QAction>("loopPlaylist");
519
520
    playerConfig.writeEntry("LoopPlaylist", a->isChecked());

521
    playerConfig.writeEntry("CrossfadeTracks", false); // TODO bring back
522

Michael Pyne's avatar
Michael Pyne committed
523
    a = ActionCollection::action<QAction>("albumRandomPlay");
524
    if(a->isChecked())
525
        playerConfig.writeEntry("RandomPlay", "AlbumRandomPlay");
526
    else if(m_randomPlayAction->isChecked())
527
        playerConfig.writeEntry("RandomPlay", "Normal");
528
    else
529
        playerConfig.writeEntry("RandomPlay", "Disabled");
530

531
532
    // general settings

533
    KConfigGroup settingsConfig(KSharedConfig::openConfig(), "Settings");
534
535
536
537
538
    settingsConfig.writeEntry("StartDocked", m_startDocked);
    settingsConfig.writeEntry("DockInSystemTray", m_toggleSystemTrayAction->isChecked());
    settingsConfig.writeEntry("DockOnClose", m_toggleDockOnCloseAction->isChecked());
    settingsConfig.writeEntry("TrackPopup", m_togglePopupsAction->isChecked());

539
    KSharedConfig::openConfig()->sync();
540
541
}

542
543
bool JuK::queryClose()
{
544
    if(!m_shuttingDown &&
545
       !qApp->isSavingSession() &&
546
547
548
       m_systemTray &&
       m_toggleDockOnCloseAction->isChecked())
    {
549
550
551
552
553
554
        KMessageBox::information(this,
            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");
        hide();
        return false;
555
    }
Michael Pyne's avatar
Michael Pyne committed
556
557

    return true;
558
559
}

560
561
562
563
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

564
565
void JuK::slotShowHide()
{
Stephan Kulow's avatar
Stephan Kulow committed
566
    setHidden(!isHidden());
567
568
}

569
570
void JuK::slotQuit()
{
Michael Pyne's avatar
Michael Pyne committed
571
    m_shuttingDown = true;
572

Michael Pyne's avatar
Michael Pyne committed
573
574
575
576
577
578
579
    // Some phonon backends will crash on shutdown unless we've stopped
    // playback.
    if(m_player->playing()) {
        m_player->stop();
    }

    m_startDocked = !isVisible();
580
    saveConfig();
Michael Pyne's avatar
Michael Pyne committed
581

582
583
584
585
586
587
588
589
590
591
592
593
    // this will start chain of events causing PlaylistCollection (in
    // guise of PlaylistBox) and CollectionList (as first Playlist child)
    // to save themselves and then quit the application when this widget
    // closes.
    delete m_statusLabel;
    m_statusLabel = nullptr;

    delete m_splitter;
    m_splitter = nullptr;

    setAttribute(Qt::WA_DeleteOnClose);
    this->close();
594
595
}

596
597
598
599
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

600
void JuK::slotToggleSystemTray(bool enabled)
601
{
602
    if(enabled && !m_systemTray)
Michael Pyne's avatar
Michael Pyne committed
603
        slotSetupSystemTray();
604
    else if(!enabled && m_systemTray) {
605
        delete m_systemTray;
Michael Pyne's avatar
Michael Pyne committed
606
        m_systemTray = nullptr;
607
608
        m_toggleDockOnCloseAction->setEnabled(false);
        m_togglePopupsAction->setEnabled(false);
Michael Pyne's avatar
Michael Pyne committed
609
610

        qGuiApp->setQuitOnLastWindowClosed(true);
611
612
613
    }
}

614
615
void JuK::slotEditKeys()
{
616
    KeyDialog(ActionCollection::actions(), this).configure();
617
618
}

619
620
void JuK::slotConfigureTagGuesser()
{
621
    TagGuesserConfigDlg(this).exec();
622
623
}

624
625
void JuK::slotConfigureFileRenamer()
{
626
    FileRenamerConfigDlg(this).exec();
627
628
}

629
630
631
void JuK::slotConfigureScrobbling()
{
    ScrobbleConfigDlg(this).exec();
632
    activateScrobblerIfEnabled();
633
634
}

635
636
637
638
639
640
641
642
643
644
645
646
647
648
void JuK::activateScrobblerIfEnabled()
{
    bool isScrobbling = Scrobbler::isScrobblingEnabled();

    if (!m_scrobbler && isScrobbling) {
        m_scrobbler = new Scrobbler(this);
        connect (m_player,    SIGNAL(signalItemChanged(FileHandle)),
                 m_scrobbler, SLOT(nowPlaying(FileHandle)));
    }
    else if (m_scrobbler && !isScrobbling) {
        delete m_scrobbler;
        m_scrobbler = 0;
    }
}
649

650
651
652
653
654
void JuK::slotUndo()
{
    TagTransactionManager::instance()->undo();
}

655
656
void JuK::slotCheckAlbumNextAction(bool albumRandomEnabled)
{
657
    action("forwardAlbum")->setEnabled(m_player->playing() && albumRandomEnabled);
658
659
}

660
// vim: set et sw=4 tw=0 sta: