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

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

16
17
#include "juk.h"

18
19
#include <config.h>

20
21
22
23
#include <kcmdlineargs.h>
#include <kstatusbar.h>
#include <kdebug.h>
#include <kmessagebox.h>
24
#include <kstandarddirs.h>
Laurent Montel's avatar
Poirt    
Laurent Montel committed
25
#include <kactioncollection.h>
Aaron J. Seigo's avatar
Aaron J. Seigo committed
26
#include <kstandardaction.h>
Laurent Montel's avatar
Laurent Montel committed
27
28
#include <ktoggleaction.h>
#include <kactionmenu.h>
29
#include <kicon.h>
30
31
#include <kaction.h>
#include <kconfiggroup.h>
32
33
34
#include <kapplication.h>
#include <kglobalaccel.h>
#include <ktoolbarpopupaction.h>
35
#include <kdeversion.h>
36
37

#include <QKeyEvent>
38
39
#include <QDir>
#include <QTimer>
40

41
42
43
44
45
46
#include "slideraction.h"
#include "statuslabel.h"
#include "splashscreen.h"
#include "systemtray.h"
#include "keydialog.h"
#include "tagguesserconfigdlg.h"
47
#include "filerenamerconfigdlg.h"
48
#include "actioncollection.h"
49
#include "cache.h"
50
51
#include "playlistsplitter.h"
#include "collectionlist.h"
52
#include "covermanager.h"
53
#include "tagtransactionmanager.h"
54

55
56
using namespace ActionCollection;

57
58
JuK* JuK::m_instance;

59
60
61
62
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

63
JuK::JuK(QWidget *parent) :
Kevin Ottens's avatar
Kevin Ottens committed
64
    KXmlGuiWindow(parent, Qt::WDestructiveClose),
65
    m_systemTray(0),
66
    m_player(PlayerManager::instance()),
67
    m_shuttingDown(false)
68
{
69
70
    // Expect segfaults if you change this order.

71
72
    m_instance = this;

73
    readSettings();
74

75
    if(m_showSplash && !m_startDocked && Cache::cacheFileExists()) {
76
77
        SplashScreen::instance()->show();
        kapp->processEvents();
78
79
    }

80
    setupActions();
81
    setupLayout();
82

83
    if(QApplication::isRightToLeft())
84
        setupGUI(ToolBar | Save | Create, "jukui-rtl.rc");
85
    else
86
        setupGUI(ToolBar | Save | Create);
87

88
    readConfig();
89
    setupSystemTray();
90
    setupGlobalAccels();
91
    createDirs();
92

93
    SplashScreen::finishedLoading();
Scott Wheeler's avatar
Scott Wheeler committed
94
    QTimer::singleShot(0, CollectionList::instance(), SLOT(slotCheckCache()));
95
    QTimer::singleShot(0, this, SLOT(slotProcessArgs()));
96
97

    m_sliderAction->slotUpdateOrientation();
98
99
100
101
}

JuK::~JuK()
{
Thiago Macieira's avatar
Thiago Macieira committed
102
    kDebug(65432) ;
103
104
}

105
106
107
108
109
KActionCollection *JuK::actionCollection() const
{
    return ActionCollection::actions();
}

110
111
112
113
114
JuK* JuK::JuKInstance()
{
    return m_instance;
}

115
116
117
118
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

119
120
void JuK::setupLayout()
{
121
122
    new TagTransactionManager(this);

123
124
    m_splitter = new PlaylistSplitter(this, "playlistSplitter");
    setCentralWidget(m_splitter);
125

126
    m_statusLabel = new StatusLabel(m_splitter->playlist(), statusBar());
127
128
    connect(CollectionList::instance(), SIGNAL(signalCollectionChanged()),
            m_statusLabel, SLOT(updateData()));
129
    statusBar()->addWidget(m_statusLabel, 1);
130
131
    PlayerManager::instance()->setStatusLabel(m_statusLabel);

132
    m_splitter->setFocus();
133
    resize(750, 500);
134
135
}

136
137
void JuK::setupActions()
{
138
    KActionCollection *collection = ActionCollection::actions();
Michael Pyne's avatar
Michael Pyne committed
139

140
    // Setup KDE standard actions that JuK uses.
141

Aaron J. Seigo's avatar
Aaron J. Seigo committed
142
143
144
145
146
147
148
149
    KStandardAction::quit(this, SLOT(slotQuit()), collection);
    KStandardAction::undo(this, SLOT(slotUndo()), collection);
    KStandardAction::cut(collection);
    KStandardAction::copy(collection);
    KStandardAction::paste(collection);
    KAction *clear = KStandardAction::clear(collection);
    KStandardAction::selectAll(collection);
    KStandardAction::keyBindings(this, SLOT(slotEditKeys()), collection);
Scott Wheeler's avatar
Scott Wheeler committed
150

151
152

    // Setup the menu which handles the random play options.
153
    KActionMenu *actionMenu = new KActionMenu(KIcon("media-playlist-shuffle"), i18n("&Random Play"), collection);
Laurent Montel's avatar
Port++    
Laurent Montel committed
154
    collection->addAction("actionMenu",actionMenu);
155
    actionMenu->setDelayed(false);
156

157
158
    // ### KDE4: Investigate how QActionGroups integrate into menus now.
    QActionGroup* randomPlayGroup = new QActionGroup(this);
David Faure's avatar
David Faure committed
159

160
    QAction *act = new KToggleAction(KIcon("go-down"), i18n("&Disable Random Play"), collection);
161
    collection->addAction("disableRandomPlay", act);
Laurent Montel's avatar
Port++    
Laurent Montel committed
162

163
    act->setActionGroup(randomPlayGroup);
Stephan Kulow's avatar
Stephan Kulow committed
164
    actionMenu->addAction(act);
165

Laurent Montel's avatar
Port++    
Laurent Montel committed
166
167
    m_randomPlayAction = collection->add<KToggleAction>("randomPlay");
    m_randomPlayAction->setText(i18n("Use &Random Play"));
168
    m_randomPlayAction->setIcon(KIcon("media-playlist-shuffle"));
David Faure's avatar
David Faure committed
169
    m_randomPlayAction->setActionGroup(randomPlayGroup);
Stephan Kulow's avatar
Stephan Kulow committed
170
    actionMenu->addAction(m_randomPlayAction);
171

172
    act = new KToggleAction(KIcon("media-playlist-shuffle"), i18n("Use &Album Random Play"), collection);
173
    collection->addAction("albumRandomPlay", act);
174
175
    act->setActionGroup(randomPlayGroup);
    connect(act, SIGNAL(triggered(bool)), SLOT(slotCheckAlbumNextAction(bool)));
Stephan Kulow's avatar
Stephan Kulow committed
176
    actionMenu->addAction(act);
177

Laurent Montel's avatar
Port++    
Laurent Montel committed
178
179
    act = collection->addAction("removeFromPlaylist");
    act->setText(i18n("Remove From Playlist"));
180
    act->setIcon(KIcon("list-remove"));
Laurent Montel's avatar
Port++    
Laurent Montel committed
181

182
    connect(act, SIGNAL(triggered(bool)), clear, SLOT(clear()));
183

184
    act = new KAction(KIcon("media-playback-start"), i18n("&Play"), collection);
185
    collection->addAction("play", act);
186
187
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(play()));

188
    act = new KAction(KIcon("media-playback-pause"), i18n("P&ause"), collection);
189
    collection->addAction("pause", act);
190
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(pause()));
191

192
    act = new KAction(KIcon("media-playback-stop"), i18n("&Stop"), collection);
193
    collection->addAction("stop", act);
194
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(stop()));
Scott Wheeler's avatar
Scott Wheeler committed
195

196
    act = new KToolBarPopupAction(KIcon("media-skip-backward"), i18nc("previous track", "Previous"), collection);
197
    collection->addAction("back", act);
198
199
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(back()));

200
    act = new KAction(KIcon("media-skip-forward"), i18nc("next track", "&Next"), collection);
201
    collection->addAction("forward", act);
202
203
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(forward()));

204
205
    act = new KAction(i18n("&Loop Playlist"), collection);
    collection->addAction("loopPlaylist", act);
206
207
    act->setCheckable(true);

208
    KToggleAction *resizeColumnAction =
209
        new KToggleAction(i18n("&Resize Playlist Columns Manually"),
210
211
                          collection);
    collection->addAction("resizeColumnsManually", resizeColumnAction);
Laurent Montel's avatar
Laurent Montel committed
212
    resizeColumnAction->setCheckedState(KGuiItem(i18n("&Resize Column Headers Automatically")));
213
214

    // the following are not visible by default
215

216
    act = new KAction(KIcon("audio-volume-mute"), i18nc("silence playback", "Mute"), collection);
217
    collection->addAction("mute", act);
218
219
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(mute()));

220
    act = new KAction(KIcon("audio-volume-high"),    i18n("Volume Up"),    collection);
221
    collection->addAction("volumeUp", act);
222
223
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(volumeUp()));

224
    act = new KAction(KIcon("audio-volume-low"),  i18n("Volume Down"),  collection);
225
    collection->addAction("volumeDown", act);
226
227
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(volumeDown()));

228
    act = new KAction(KIcon("media-playback-play"),   i18n("Play / Pause"), collection);
229
    collection->addAction("playPause", act);
230
231
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(playPause()));

232
    act = new KAction(KIcon("media-seek-forward"), i18n("Seek Forward"), collection);
233
    collection->addAction("seekForward", act);
234
235
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(seekForward()));

236
    act = new KAction(KIcon("media-seek-backward"),    i18n("Seek Back"),    collection);
237
    collection->addAction("seekBack", act);
238
239
    connect(act, SIGNAL(triggered(bool)), m_player, SLOT(seekBack()));

240
241
    act = new KAction(i18n("Show / Hide"), collection);
    collection->addAction("showHide", act);
242
    connect(act, SIGNAL(triggered(bool)), this,     SLOT(slotShowHide()));
Stephan Binner's avatar
Stephan Binner committed
243

244
    //////////////////////////////////////////////////
245
    // settings menu
246
247
    //////////////////////////////////////////////////

248
249
    m_toggleSplashAction = new KToggleAction(i18n("Show Splash Screen on Startup"), collection);
    collection->addAction("showSplashScreen", m_toggleSplashAction);
Laurent Montel's avatar
Laurent Montel committed
250
    m_toggleSplashAction->setCheckedState(KGuiItem(i18n("Hide Splash Screen on Startup")));
251
252
253
254
255
256
257
258
    m_toggleSystemTrayAction = new KToggleAction(i18n("&Dock in System Tray"), collection);
    collection->addAction("toggleSystemTray", m_toggleSystemTrayAction);
    m_toggleDockOnCloseAction = new KToggleAction(i18n("&Stay in System Tray on Close"), collection);
    collection->addAction("dockOnClose", m_toggleDockOnCloseAction);
    m_togglePopupsAction = new KToggleAction(i18n("Popup &Track Announcement"), collection);
    collection->addAction("togglePopups", m_togglePopupsAction);
    act = new KToggleAction(i18n("Save &Play Queue on Exit"), collection);
    collection->addAction("saveUpcomingTracks", act);
259

260
    connect(m_toggleSystemTrayAction, SIGNAL(triggered(bool)),
261
            this, SLOT(slotToggleSystemTray(bool)));
262

Scott Wheeler's avatar
Scott Wheeler committed
263

264
265
    act = new KAction(i18n("&Tag Guesser..."), collection);
    collection->addAction("tagGuesserConfig", act);
266
    connect(act, SIGNAL(triggered(bool)), SLOT(slotConfigureTagGuesser()));
267

268
269
    act = new KAction(i18n("&File Renamer..."), collection);
    collection->addAction("fileRenamerConfig", act);
270
    connect(act, SIGNAL(triggered(bool)), SLOT(slotConfigureFileRenamer()));
271

272
    //////////////////////////////////////////////////
273
    // just in the toolbar
274
275
    //////////////////////////////////////////////////

276
277
    m_sliderAction = new SliderAction(i18n("Track Position"), this);
    collection->addAction("trackPositionAction", m_sliderAction);
278

279
280
281
    ActionCollection::actions()->addAssociatedWidget(this);
    foreach (QAction* action, ActionCollection::actions()->actions())
        action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
282
283
}

284
void JuK::setupSystemTray()
285
{
286
    if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
287
288
        m_systemTray = new SystemTray(this);
        m_systemTray->setObjectName( "systemTray" );
289
        m_systemTray->show();
290

291
292
        m_toggleDockOnCloseAction->setEnabled(true);
        m_togglePopupsAction->setEnabled(true);
293

294
        connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotAboutToQuit()));
295
    }
296
    else {
297
298
299
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
        m_togglePopupsAction->setEnabled(false);
300
    }
301
302
}

303
304
void JuK::setupGlobalAccels()
{
305
306
307
308
309
310
311
312
313
314
315
316
    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");
317
318
}

319
void JuK::slotProcessArgs()
320
321
322
{
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    QStringList files;
323

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

327
    CollectionList::instance()->addFiles(files);
328
329
}

330
331
void JuK::createDirs()
{
332
    QDir dir(KGlobal::dirs()->saveLocation("data", KGlobal::mainComponent().componentName() + '/'));
333
334
335
336

    if(!dir.exists("covers"))
       dir.mkdir("covers");

337
    dir.cd("covers");
338
339
340

    if(!dir.exists("large"))
        dir.mkdir("large");
341
342
}

343
344
345
void JuK::keyPressEvent(QKeyEvent *e)
{
    if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
Frerich Raabe's avatar
Frerich Raabe committed
346
        e->accept();
Kevin Ottens's avatar
Kevin Ottens committed
347
    KXmlGuiWindow::keyPressEvent(e);
348
349
}

350
351
352
353
354
355
/**
 * These are settings that need to be know before setting up the GUI.
 */

void JuK::readSettings()
{
356
    KConfigGroup config(KGlobal::config(), "Settings");
357
358
    m_showSplash = config.readEntry("ShowSplashScreen", true);
    m_startDocked = config.readEntry("StartDocked", false);
359
360
}

361
362
void JuK::readConfig()
{
363
    // player settings
364

365
    KConfigGroup playerConfig(KGlobal::config(), "Player");
366

367
368
369
370
371
    if(m_player)
    {
        const int maxVolume = 100;
        const int volume = playerConfig.readEntry("Volume", maxVolume);
        m_player->setVolume(volume * 0.01);
372
    }
373

374
375
    // Default to no random play

376
    ActionCollection::action<KToggleAction>("disableRandomPlay")->setChecked(true);
377

378
379
    QString randomPlayMode = playerConfig.readEntry("RandomPlay", "Disabled");
    if(randomPlayMode == "true" || randomPlayMode == "Normal")
380
        m_randomPlayAction->setChecked(true);
381
    else if(randomPlayMode == "AlbumRandomPlay")
382
        ActionCollection::action<KAction>("albumRandomPlay")->setChecked(true);
383

384
    bool loopPlaylist = playerConfig.readEntry("LoopPlaylist", false);
385
    ActionCollection::action<KAction>("loopPlaylist")->setChecked(loopPlaylist);
386

387
    // general settings
388

389
390
    KConfigGroup settingsConfig(KGlobal::config(), "Settings");

391
    bool dockInSystemTray = settingsConfig.readEntry("DockInSystemTray", true);
392
393
    m_toggleSystemTrayAction->setChecked(dockInSystemTray);

394
    bool dockOnClose = settingsConfig.readEntry("DockOnClose", true);
395
396
    m_toggleDockOnCloseAction->setChecked(dockOnClose);

397
    bool showPopups = settingsConfig.readEntry("TrackPopup", false);
398
399
    m_togglePopupsAction->setChecked(showPopups);

400
    m_toggleSplashAction->setChecked(m_showSplash);
401
402
403
404
}

void JuK::saveConfig()
{
405
    // player settings
406

407
    KConfigGroup playerConfig(KGlobal::config(), "Player");
408

409
    if (m_player)
410
    {
411
        playerConfig.writeEntry("Volume", static_cast<int>(100.0 * m_player->volume()));
412
413
    }

414
415
    playerConfig.writeEntry("RandomPlay", m_randomPlayAction->isChecked());

416
    KAction *a = ActionCollection::action<KAction>("loopPlaylist");
417
418
    playerConfig.writeEntry("LoopPlaylist", a->isChecked());

419
    a = ActionCollection::action<KAction>("albumRandomPlay");
420
    if(a->isChecked())
421
        playerConfig.writeEntry("RandomPlay", "AlbumRandomPlay");
422
    else if(m_randomPlayAction->isChecked())
423
        playerConfig.writeEntry("RandomPlay", "Normal");
424
    else
425
        playerConfig.writeEntry("RandomPlay", "Disabled");
426

427
428
429
430
431
432
433
434
435
436
    // general settings

    KConfigGroup settingsConfig(KGlobal::config(), "Settings");
    settingsConfig.writeEntry("ShowSplashScreen", m_toggleSplashAction->isChecked());
    settingsConfig.writeEntry("StartDocked", m_startDocked);
    settingsConfig.writeEntry("DockInSystemTray", m_toggleSystemTrayAction->isChecked());
    settingsConfig.writeEntry("DockOnClose", m_toggleDockOnCloseAction->isChecked());
    settingsConfig.writeEntry("TrackPopup", m_togglePopupsAction->isChecked());

    KGlobal::config()->sync();
437
438
}

439
bool JuK::queryExit()
440
{
Scott Wheeler's avatar
Scott Wheeler committed
441
442
    m_startDocked = !isVisible();

Thiago Macieira's avatar
Thiago Macieira committed
443
    kDebug(65432) ;
444

445
446
    hide();

David Faure's avatar
David Faure committed
447
    action("stop")->trigger();
448
449
450
    delete m_systemTray;
    m_systemTray = 0;

451
    CoverManager::shutdown();
452
    Cache::instance()->save();
453
    saveConfig();
454
455

    delete m_splitter;
456
    return true;
457
458
}

459
460
bool JuK::queryClose()
{
Thiago Macieira's avatar
Thiago Macieira committed
461
    kDebug(65432) ;
462

463
464
465
466
467
    if(!m_shuttingDown &&
       !kapp->sessionSaving() &&
       m_systemTray &&
       m_toggleDockOnCloseAction->isChecked())
    {
468
469
470
471
472
473
        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;
474
475
    }
    else
476
        return true;
477
478
}

479
480
481
482
////////////////////////////////////////////////////////////////////////////////
// private slot definitions
////////////////////////////////////////////////////////////////////////////////

483
484
void JuK::slotShowHide()
{
Stephan Kulow's avatar
Stephan Kulow committed
485
    setHidden(!isHidden());
486
487
}

488
489
490
491
492
void JuK::slotAboutToQuit()
{
    m_shuttingDown = true;
}

493
494
void JuK::slotQuit()
{
Thiago Macieira's avatar
Thiago Macieira committed
495
    kDebug(65432) ;
Michael Pyne's avatar
Michael Pyne committed
496
    m_shuttingDown = true;
497

498
499
500
    kapp->quit();
}

501
502
503
504
////////////////////////////////////////////////////////////////////////////////
// settings menu
////////////////////////////////////////////////////////////////////////////////

505
void JuK::slotToggleSystemTray(bool enabled)
506
{
507
    if(enabled && !m_systemTray)
508
        setupSystemTray();
509
    else if(!enabled && m_systemTray) {
510
511
512
513
        delete m_systemTray;
        m_systemTray = 0;
        m_toggleDockOnCloseAction->setEnabled(false);
        m_togglePopupsAction->setEnabled(false);
514
515
516
    }
}

517
518
void JuK::slotEditKeys()
{
519
    KeyDialog::configure(ActionCollection::actions(), this);
520
521
}

522
523
void JuK::slotConfigureTagGuesser()
{
524
    TagGuesserConfigDlg(this).exec();
525
526
}

527
528
void JuK::slotConfigureFileRenamer()
{
529
    FileRenamerConfigDlg(this).exec();
530
531
}

532
533
534
535
536
void JuK::slotUndo()
{
    TagTransactionManager::instance()->undo();
}

537
538
539
540
541
542
void JuK::slotCheckAlbumNextAction(bool albumRandomEnabled)
{
    // If album random play is enabled, then enable the Play Next Album action
    // unless we're not playing right now.

    if(albumRandomEnabled && !m_player->playing())
543
        albumRandomEnabled = false;
544
545
546
547

    action("forwardAlbum")->setEnabled(albumRandomEnabled);
}

548
#include "juk.moc"
549
550

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