1
 /****************************************************************************************
2
 * Copyright (c) 2002-2009 Mark Kretschmann <kretschmann@kde.org>                       *
3
 * Copyright (c) 2002 Max Howell <max.howell@methylblue.com>                            *
4
 * Copyright (c) 2002 Gabor Lehel <illissius@gmail.com>                                 *
5
 * Copyright (c) 2002 Nikolaj Hald Nielsen <nhn@kde.org>                                *
6
 * Copyright (c) 2009 Artur Szymiec <artur.szymiec@gmail.com>                           *
7
 * Copyright (c) 2010 Téo Mrnjavac <teo@kde.org>                                        *
8
 *                                                                                      *
9
 * This program is free software; you can redistribute it and/or modify it under        *
10
 * the terms of the GNU General Public License as published by the Free Software        *
11
 * Foundation; either version 2 of the License, or (at your option) any later           *
12
 * version.                                                                             *
13
 *                                                                                      *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
15
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
16
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
17
 *                                                                                      *
18
 * You should have received a copy of the GNU General Public License along with         *
19
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
20
 ****************************************************************************************/
21
22
#define DEBUG_PREFIX "MainWindow"
23
24
#include "MainWindow.h"
25
26
#include "aboutdialog/ExtendedAboutDialog.h"
27
#include "ActionClasses.h"
28
#include "core/support/Amarok.h"
29
#include "core/support/Debug.h"
30
#include "EngineController.h" //for actions in ctor
31
#include "KNotificationBackend.h"
32
#include "Osd.h"
33
#include "PaletteHandler.h"
34
#include "ScriptManager.h"
35
#include "amarokconfig.h"
36
#include "aboutdialog/OcsData.h"
37
#include "amarokurls/AmarokUrlHandler.h"
38
#include "amarokurls/BookmarkManager.h"
39
#include "browsers/collectionbrowser/CollectionWidget.h"
40
#include "browsers/filebrowser/FileBrowser.h"
41
#include "browsers/playlistbrowser/PlaylistBrowser.h"
42
#include "browsers/servicebrowser/ServiceBrowser.h"
43
#include "context/ContextDock.h"
44
#include "core-impl/collections/support/CollectionManager.h"
45
#include "covermanager/CoverManager.h" // for actions
46
#include "dialogs/EqualizerDialog.h"
47
#include "likeback/LikeBack.h"
48
#include "moodbar/MoodbarManager.h"
49
#include "network/NetworkAccessManagerProxy.h"
50
#ifdef DEBUG_BUILD_TYPE
51
#include "network/NetworkAccessViewer.h"
52
#endif // DEBUG_BUILD_TYPE
53
#include "playlist/layouts/LayoutConfigAction.h"
54
#include "playlist/PlaylistActions.h"
55
#include "playlist/PlaylistController.h"
56
#include "playlist/PlaylistModelStack.h"
57
#include "playlist/PlaylistDock.h"
58
#include "playlist/ProgressiveSearchWidget.h"
59
#include "playlistmanager/file/PlaylistFileProvider.h"
60
#include "playlistmanager/PlaylistManager.h"
61
#include "PodcastCategory.h"
62
#include "services/ServicePluginManager.h"
63
#include "services/scriptable/ScriptableService.h"
64
#include "statusbar/StatusBar.h"
65
#include "toolbar/SlimToolbar.h"
66
#include "toolbar/MainToolbar.h"
67
#include "SvgHandler.h"
68
#include "widgets/Splitter.h"
69
//#include "mediabrowser.h"
70
71
#include <KAction>          //m_actionCollection
72
#include <KActionCollection>
73
#include <KApplication>     //kapp
74
#include <KFileDialog>      //openPlaylist()
75
#include <KInputDialog>     //slotAddStream()
76
#include <KMessageBox>
77
#include <KLocale>
78
#include <KMenu>
79
#include <KMenuBar>
80
#include <KPixmapCache>
81
#include <KBugReport>
82
#include <KStandardAction>
83
#include <KStandardDirs>
84
#include <KWindowSystem>
85
#include <kabstractfilewidget.h>
86
87
#include <plasma/plasma.h>
88
89
#include <QCheckBox>
90
#include <QDesktopServices>
91
#include <QDesktopWidget>
92
#include <QDockWidget>
93
#include <QList>
94
#include <QSizeGrip>
95
#include <QStyle>
96
#include <QVBoxLayout>
97
98
#ifdef Q_WS_X11
99
#include <fixx11h.h>
100
#endif
101
102
#ifdef Q_WS_MAC
103
#include "mac/GrowlInterface.h"
104
#endif
105
106
#define AMAROK_CAPTION "Amarok"
107
108
109
extern KAboutData aboutData;
110
extern OcsData ocsData;
111
112
class ContextWidget : public KVBox
113
{
114
    // Set a useful size default of the center tab.
115
    public:
116
        ContextWidget( QWidget *parent ) : KVBox( parent ) {}
117
118
        QSize sizeHint() const
119
        {
120
            return QSize( static_cast<QWidget*>( parent() )->size().width() / 3, 300 );
121
        }
122
};
123
124
QPointer<MainWindow> MainWindow::s_instance = 0;
125
126
namespace The {
127
    MainWindow* mainWindow() { return MainWindow::s_instance; }
128
}
129
130
MainWindow::MainWindow()
131
    : KMainWindow( 0 )
132
    , Engine::EngineObserver( The::engineController() )
133
    , m_lastBrowser( 0 )
134
    , m_layoutEverRestored( false )
135
    , m_waitingForCd( false )
136
    , m_mouseDown( false )
137
    , m_LH_initialized( false )
138
{
139
    DEBUG_BLOCK
140
141
    m_saveLayoutChangesTimer = new QTimer( this );
142
    m_saveLayoutChangesTimer->setSingleShot( true );
143
    connect( m_saveLayoutChangesTimer, SIGNAL( timeout() ), this, SLOT( saveLayout() ) );
144
145
    setObjectName( "MainWindow" );
146
    s_instance = this;
147
148
#ifdef Q_WS_MAC
149
    QSizeGrip* grip = new QSizeGrip( this );
150
    GrowlInterface* growl = new GrowlInterface( qApp->applicationName() );
151
#endif
152
153
154
    /* The ServicePluginManager needs to be loaded before the playlist model
155
    * (which gets started by "statusBar" below up so that it can handle any
156
    * tracks in the saved playlist that are associated with services. Eg, if
157
    * the playlist has a Magnatune track in it when Amarok is closed, then the
158
    * Magnatune service needs to be initialized before the playlist is loaded
159
    * here. */
160
161
    ServicePluginManager::instance();
162
163
    StatusBar * statusBar = new StatusBar( this );
164
165
    setStatusBar( statusBar );
166
167
    // Sets caption and icon correctly (needed e.g. for GNOME)
168
//     kapp->setTopWidget( this );
169
    PERF_LOG( "Set Top Widget" )
170
    createActions();
171
    PERF_LOG( "Created actions" )
172
173
    //new K3bExporter();
174
175
    The::paletteHandler()->setPalette( palette() );
176
    setPlainCaption( i18n( AMAROK_CAPTION ) );
177
178
    init();  // We could as well move the code from init() here, but meh.. getting a tad long
179
180
    //restore active category ( as well as filters and levels and whatnot.. )
181
    const QString path = Amarok::config().readEntry( "Browser Path", QString() );
182
    if( !path.isEmpty() )
183
        m_browserDock->list()->navigate( path );
184
185
    setAutoSaveSettings();
186
}
187
188
MainWindow::~MainWindow()
189
{
190
    DEBUG_BLOCK
191
192
    //save currently active category
193
    Amarok::config().writeEntry( "Browser Path", m_browserDock->list()->path() );
194
195
    QList<int> sPanels;
196
197
    //foreach( int a, m_splitter->saveState() )
198
    //    sPanels.append( a );
199
200
    //AmarokConfig::setPanelsSavedState( sPanels );
201
202
    //delete m_splitter;
203
#ifdef DEBUG_BUILD_TYPE
204
    delete m_networkViewer;
205
#endif // DEBUG_BUILD_TYPE
206
    delete The::statusBar();
207
    delete The::svgHandler();
208
    delete The::paletteHandler();
209
}
210
211
212
///////// public interface
213
214
/**
215
 * This function will initialize the main window.
216
 */
217
void
218
MainWindow::init()
219
{
220
    DEBUG_BLOCK
221
222
    layout()->setContentsMargins( 0, 0, 0, 0 );
223
    layout()->setSpacing( 0 );
224
225
    //create main toolbar
226
    m_mainToolbar = new MainToolbar( 0 );
227
    m_mainToolbar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea );
228
    m_mainToolbar->setMovable ( true );
229
    addToolBar( Qt::TopToolBarArea, m_mainToolbar );
230
    m_mainToolbar->hide();
231
232
    //create slim toolbar
233
    m_slimToolbar = new SlimToolbar( 0 );
234
    m_slimToolbar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea );
235
    m_slimToolbar->setMovable ( true );
236
    addToolBar( Qt::TopToolBarArea, m_slimToolbar );
237
    m_slimToolbar->hide();
238
239
    //BEGIN Creating Widgets
240
    PERF_LOG( "Create sidebar" )
241
    m_browserDock = new BrowserDock( this );
242
    m_browserDock->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored );
243
244
    m_browserDock->installEventFilter( this );
245
    PERF_LOG( "Sidebar created" )
246
247
    PERF_LOG( "Create Playlist" )
248
    m_playlistDock = new Playlist::Dock( this );
249
    m_playlistDock->installEventFilter( this );
250
    PERF_LOG( "Playlist created" )
251
252
    PERF_LOG( "Creating ContextWidget" )
253
    m_contextDock = new ContextDock( this );
254
    m_contextDock->installEventFilter( this );
255
256
    PERF_LOG( "ContextScene created" )
257
    //END Creating Widgets
258
259
    createMenus();
260
261
    PERF_LOG( "Loading default contextScene" )
262
263
    PERF_LOG( "Loaded default contextScene" )
264
265
    setDockOptions ( QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks | QMainWindow::AnimatedDocks );
266
267
    addDockWidget( Qt::LeftDockWidgetArea, m_browserDock );
268
    addDockWidget( Qt::LeftDockWidgetArea, m_contextDock, Qt::Horizontal );
269
    addDockWidget( Qt::LeftDockWidgetArea, m_playlistDock, Qt::Horizontal );
270
271
    setLayoutLocked( AmarokConfig::lockLayout() );
272
273
    //<Browsers>
274
    {
275
        Debug::Block block( "Creating browsers. Please report long start times!" );
276
277
        PERF_LOG( "Creating CollectionWidget" )
278
        m_collectionBrowser = new CollectionWidget( "collections", 0 );
279
        m_collectionBrowser->setPrettyName( i18n( "Local Music" ) );
280
        m_collectionBrowser->setIcon( KIcon( "collection-amarok" ) );
281
        m_collectionBrowser->setShortDescription( i18n( "Local sources of content" ) );
282
        m_browserDock->list()->addCategory( m_collectionBrowser );
283
        PERF_LOG( "Created CollectionWidget" )
284
285
286
        PERF_LOG( "Creating ServiceBrowser" )
287
        ServiceBrowser *internetContentServiceBrowser = ServiceBrowser::instance();
288
        internetContentServiceBrowser->setParent( 0 );
289
        internetContentServiceBrowser->setPrettyName( i18n( "Internet" ) );
290
        internetContentServiceBrowser->setIcon( KIcon( "applications-internet" ) );
291
        internetContentServiceBrowser->setShortDescription( i18n( "Online sources of content" ) );
292
        m_browserDock->list()->addCategory( internetContentServiceBrowser );
293
        PERF_LOG( "Created ServiceBrowser" )
294
295
        PERF_LOG( "Creating PlaylistBrowser" )
296
        m_playlistBrowser = new PlaylistBrowserNS::PlaylistBrowser( "playlists", 0 );
297
        m_playlistBrowser->setPrettyName( i18n("Playlists") );
298
        m_playlistBrowser->setIcon( KIcon( "view-media-playlist-amarok" ) );
299
        m_playlistBrowser->setShortDescription( i18n( "Various types of playlists" ) );
300
        m_browserDock->list()->addCategory( m_playlistBrowser );
301
        PERF_LOG( "CreatedPlaylsitBrowser" )
302
303
304
        PERF_LOG( "Creating FileBrowser" )
305
        FileBrowser * fileBrowserMkII = new FileBrowser( "files", 0 );
306
        fileBrowserMkII->setPrettyName( i18n("Files") );
307
        fileBrowserMkII->setIcon( KIcon( "folder-amarok" ) );
308
        fileBrowserMkII->setShortDescription( i18n( "Browse local hard drive for content" ) );
309
        m_browserDock->list()->addCategory( fileBrowserMkII );
310
311
312
        PERF_LOG( "Created FileBrowser" )
313
314
        PERF_LOG( "Initialising ServicePluginManager" )
315
        ServicePluginManager::instance()->init();
316
        PERF_LOG( "Initialised ServicePluginManager" )
317
318
        internetContentServiceBrowser->setScriptableServiceManager( The::scriptableServiceManager() );
319
        PERF_LOG( "ScriptableServiceManager done" )
320
321
        PERF_LOG( "Creating Podcast Category" )
322
        m_browserDock->list()->addCategory( The::podcastCategory() );
323
        PERF_LOG( "Created Podcast Category" )
324
325
        PERF_LOG( "finished MainWindow::init" )
326
    }
327
328
    The::amarokUrlHandler(); //Instantiate
329
330
    // Runtime check for Qt 4.6 here.
331
    // We delete the layout file once, because of binary incompatibility with older Qt version.
332
    // @see: https://bugs.kde.org/show_bug.cgi?id=213990
333
    const QChar major = qVersion()[0];
334
    const QChar minor = qVersion()[2];
335
    if( major.digitValue() >= 4 && minor.digitValue() > 5 )
336
    {
337
        KConfigGroup config = Amarok::config();
338
        if( !config.readEntry( "LayoutFileDeleted", false ) )
339
        {
340
            QFile::remove( Amarok::saveLocation() + "layout" );
341
            config.writeEntry( "LayoutFileDeleted", true );
342
            config.sync();
343
        }
344
    }
345
346
    // we must filter ourself to get mouseevents on the "splitter" - what is us, but filtered by the layouter
347
    installEventFilter( this );
348
349
    // restore the layout on app start
350
    restoreLayout();
351
352
    // wait for the eventloop
353
    QTimer::singleShot( 0, this, SLOT( initLayoutHack() ) );
354
}
355
356
357
QMenu*
358
MainWindow::createPopupMenu()
359
{
360
    DEBUG_BLOCK
361
    QMenu* menu = new QMenu( this );
362
    menu->setTitle( i18nc("@item:inmenu", "&View" ) );
363
364
    // Layout locking:
365
    QAction* lockAction = new QAction( i18n( "Lock Layout" ), this );
366
    lockAction->setCheckable( true );
367
    lockAction->setChecked( AmarokConfig::lockLayout() );
368
    connect( lockAction, SIGNAL( toggled( bool ) ), SLOT( setLayoutLocked( bool ) ) );
369
    menu->addAction( lockAction );
370
371
    menu->addSeparator();
372
373
    // Dock widgets:
374
    QList<QDockWidget *> dockwidgets = qFindChildren<QDockWidget *>( this );
375
376
    foreach( QDockWidget* dockWidget, dockwidgets )
377
    {
378
        if( dockWidget->parentWidget() == this )
379
            menu->addAction( dockWidget->toggleViewAction() );
380
    }
381
382
    menu->addSeparator();
383
384
    // Toolbars:
385
    QList<QToolBar *> toolbars = qFindChildren<QToolBar *>( this );
386
    QActionGroup* toolBarGroup = new QActionGroup( this );
387
    toolBarGroup->setExclusive( true );
388
389
    foreach( QToolBar* toolBar, toolbars )
390
    {
391
        if( toolBar->parentWidget() == this )
392
        {
393
            QAction* action = toolBar->toggleViewAction();
394
            connect( action, SIGNAL( toggled( bool ) ), toolBar, SLOT( setVisible( bool ) ) );
395
            toolBarGroup->addAction( action );
396
            menu->addAction( action );
397
        }
398
    }
399
400
    return menu;
401
}
402
403
void
404
MainWindow::showBrowser( const QString &name )
405
{
406
    const int index = m_browserNames.indexOf( name );
407
    showBrowser( index );
408
}
409
410
void
411
MainWindow::showBrowser( const int index )
412
{
413
    Q_UNUSED( index )
414
    //if( index >= 0 && index != m_browsersDock->currentIndex() )
415
    //    m_browsersDock->showWidget( index );
416
}
417
418
void
419
MainWindow::saveLayout()  //SLOT
420
{
421
    DEBUG_BLOCK
422
423
    // Do not save the layout if the main window is hidden
424
    // Qt takes widgets out of the layout if they're not visible.
425
    // So this is not going to work. Also see bug 244583
426
    if (!isVisible())
427
        return;
428
429
    //save layout to file. Does not go into to rc as it is binary data.
430
    QFile file( Amarok::saveLocation() + "layout" );
431
432
    if( file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
433
    {
434
        file.write( saveState( LAYOUT_VERSION ) );
435
436
        #ifdef Q_OS_UNIX  // fsync() only exists on Posix
437
        fsync( file.handle() );
438
        #endif
439
440
        file.close();
441
    }
442
}
443
444
void
445
MainWindow::keyPressEvent( QKeyEvent *e )
446
{
447
    if( !( e->modifiers() & Qt::ControlModifier ) )
448
        return KMainWindow::keyPressEvent( e );
449
450
    /*int n = -1;
451
    switch( e->key() )
452
    {
453
        case Qt::Key_0: n = 0; break;
454
        case Qt::Key_1: n = 1; break;
455
        case Qt::Key_2: n = 2; break;
456
        case Qt::Key_3: n = 3; break;
457
        case Qt::Key_4: n = 4; break;
458
        default:
459
            return KMainWindow::keyPressEvent( e );
460
    }
461
    if( n == 0 && m_browsersDock->currentIndex() >= 0 )
462
        m_browsersDock->showWidget( m_browsersDock->currentIndex() );
463
    else if( n > 0 )
464
        showBrowser( n - 1 ); // map from human to computer counting*/
465
}
466
467
468
void
469
MainWindow::closeEvent( QCloseEvent *e )
470
{
471
    DEBUG_BLOCK
472
473
    debug() << "Saving layout before minimizing the MainWindow";
474
    saveLayout();
475
476
#ifdef Q_WS_MAC
477
    Q_UNUSED( e );
478
    hide();
479
#else
480
481
    //KDE policy states we should hide to tray and not quit() when the
482
    //close window button is pushed for the main widget
483
    if( AmarokConfig::showTrayIcon() && e->spontaneous() && !kapp->sessionSaving() )
484
    {
485
        KMessageBox::information( this,
486
                i18n( "<qt>Closing the main window will keep Amarok running in the System Tray. "
487
                      "Use <B>Quit</B> from the menu, or the Amarok tray icon to exit the application.</qt>" ),
488
                i18n( "Docking in System Tray" ), "hideOnCloseInfo" );
489
490
        hide();
491
        e->ignore();
492
        return;
493
    }
494
495
    e->accept();
496
    kapp->quit();
497
#endif
498
}
499
500
void
501
MainWindow::showEvent(QShowEvent* e)
502
{
503
    DEBUG_BLOCK
504
505
    // restore layout on first maximize request after start. See bug 244583
506
    if (!m_layoutEverRestored)
507
        restoreLayout();
508
509
    QWidget::showEvent(e);
510
}
511
512
513
bool
514
MainWindow::queryExit()
515
{
516
    DEBUG_BLOCK
517
518
    // save layout on app exit
519
    saveLayout();
520
    
521
    return true; // KMainWindow API expects us to always return true
522
}
523
524
525
void
526
MainWindow::exportPlaylist() const //SLOT
527
{
528
    DEBUG_BLOCK
529
530
    KFileDialog fileDialog( KUrl("kfiledialog:///amarok-playlist-export"), QString(), 0 );
531
    QCheckBox *saveRelativeCheck = new QCheckBox( i18n("Use relative path for &saving") );
532
533
    QStringList supportedMimeTypes;
534
    supportedMimeTypes << "audio/x-mpegurl"; //M3U
535
    supportedMimeTypes << "audio/x-scpls"; //PLS
536
    supportedMimeTypes << "application/xspf+xml"; //XSPF
537
538
    fileDialog.setMimeFilter( supportedMimeTypes, supportedMimeTypes.first() );
539
    fileDialog.fileWidget()->setCustomWidget( saveRelativeCheck );
540
    fileDialog.setOperationMode( KFileDialog::Saving );
541
    fileDialog.setMode( KFile::File );
542
    fileDialog.setCaption( i18n("Save As") );
543
    fileDialog.setObjectName( "PlaylistExport" );
544
545
    fileDialog.exec();
546
547
    QString playlistPath = fileDialog.selectedFile();
548
549
    if( !playlistPath.isEmpty() )
550
    {
551
        AmarokConfig::setRelativePlaylist( saveRelativeCheck->isChecked() );
552
        The::playlist()->exportPlaylist( playlistPath );
553
    }
554
}
555
556
void
557
MainWindow::slotShowActiveTrack() const
558
{
559
    m_playlistDock->showActiveTrack();
560
}
561
562
void
563
MainWindow::slotShowCoverManager() const //SLOT
564
{
565
    CoverManager::showOnce();
566
}
567
568
void MainWindow::slotShowBookmarkManager() const
569
{
570
    The::bookmarkManager()->showOnce();
571
}
572
573
void MainWindow::slotShowEqualizer() const
574
{
575
    The::equalizer()->showOnce();
576
}
577
578
void
579
MainWindow::slotPlayMedia() //SLOT
580
{
581
    // Request location and immediately start playback
582
    slotAddLocation( true );
583
}
584
585
void
586
MainWindow::slotAddLocation( bool directPlay ) //SLOT
587
{
588
    static KUrl lastDirectory;
589
590
    // open a file selector to add media to the playlist
591
    KUrl::List files;
592
    KFileDialog dlg( KUrl(QDesktopServices::storageLocation(QDesktopServices::MusicLocation) ), QString("*.*|"), this );
593
594
    if( !lastDirectory.isEmpty() )
595
        dlg.setUrl( lastDirectory );
596
597
    dlg.setCaption( directPlay ? i18n("Play Media (Files or URLs)") : i18n("Add Media (Files or URLs)") );
598
    dlg.setMode( KFile::Files | KFile::Directory );
599
    dlg.setObjectName( "PlayMedia" );
600
    dlg.exec();
601
    files = dlg.selectedUrls();
602
603
    lastDirectory = dlg.baseUrl();
604
605
    if( files.isEmpty() )
606
        return;
607
608
    The::playlistController()->insertOptioned( files, directPlay ? Playlist::AppendAndPlayImmediately : Playlist::AppendAndPlay );
609
}
610
611
void
612
MainWindow::slotAddStream() //SLOT
613
{
614
    bool ok;
615
    QString url = KInputDialog::getText( i18n("Add Stream"), i18n("Enter Stream URL:"), QString(), &ok, this );
616
617
    if( !ok )
618
        return;
619
620
    Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( KUrl( url ) );
621
622
    The::playlistController()->insertOptioned( track, Playlist::Append );
623
}
624
625
void
626
MainWindow::slotJumpTo() // slot
627
{
628
    DEBUG_BLOCK
629
630
    m_playlistDock->searchWidget()->focusInputLine();
631
}
632
633
void
634
MainWindow::showScriptSelector() //SLOT
635
{
636
    ScriptManager::instance()->show();
637
    ScriptManager::instance()->raise();
638
}
639
640
#ifdef DEBUG_BUILD_TYPE
641
void
642
MainWindow::showNetworkRequestViewer() //SLOT
643
{
644
    if( !m_networkViewer )
645
    {
646
        m_networkViewer = new NetworkAccessViewer( this );
647
        The::networkAccessManager()->setNetworkAccessViewer( m_networkViewer );
648
649
    }
650
    The::networkAccessManager()->networkAccessViewer()->show();
651
}
652
#endif // DEBUG_BUILD_TYPE
653
654
/**
655
 * "Toggle Main Window" global shortcut connects to this slot
656
 */
657
void
658
MainWindow::showHide() //SLOT
659
{
660
    const KWindowInfo info = KWindowSystem::windowInfo( winId(), 0, 0 );
661
    const int currentDesktop = KWindowSystem::currentDesktop();
662
663
    if( !isVisible() )
664
    {
665
        setVisible( true );
666
    }
667
    else
668
    {
669
        if( !isMinimized() )
670
        {
671
            if( !isActiveWindow() ) // not minimised and without focus
672
            {
673
                KWindowSystem::setOnDesktop( winId(), currentDesktop );
674
                KWindowSystem::activateWindow( winId() );
675
            }
676
            else // Amarok has focus
677
            {
678
                setVisible( false );
679
            }
680
        }
681
        else // Amarok is minimised
682
        {
683
            setWindowState( windowState() & ~Qt::WindowMinimized );
684
            KWindowSystem::setOnDesktop( winId(), currentDesktop );
685
            KWindowSystem::activateWindow( winId() );
686
        }
687
    }
688
}
689
690
void
691
MainWindow::showNotificationPopup() // slot
692
{
693
    if( Amarok::KNotificationBackend::instance()->isEnabled()
694
            && !Amarok::OSD::instance()->isEnabled() )
695
        Amarok::KNotificationBackend::instance()->showCurrentTrack();
696
    else
697
        Amarok::OSD::instance()->forceToggleOSD();
698
}
699
700
void
701
MainWindow::slotFullScreen() // slot
702
{
703
    setWindowState( windowState() ^ Qt::WindowFullScreen );
704
}
705
706
void
707
MainWindow::slotLoveTrack()
708
{
709
    emit loveTrack( The::engineController()->currentTrack() );
710
}
711
712
void
713
MainWindow::activate()
714
{
715
#ifdef Q_WS_X11
716
    const KWindowInfo info = KWindowSystem::windowInfo( winId(), 0, 0 );
717
718
    if( KWindowSystem::activeWindow() != winId() )
719
        setVisible( true );
720
    else if( !info.isMinimized() )
721
        setVisible( true );
722
    if( !isHidden() )
723
        KWindowSystem::activateWindow( winId() );
724
#else
725
    setVisible( true );
726
#endif
727
}
728
729
bool
730
MainWindow::isReallyShown() const
731
{
732
#ifdef Q_WS_X11
733
    const KWindowInfo info = KWindowSystem::windowInfo( winId(), NET::WMDesktop, 0 );
734
    return !isHidden() && !info.isMinimized() && info.isOnDesktop( KWindowSystem::currentDesktop() );
735
#else
736
    return !isHidden();
737
#endif
738
}
739
740
void
741
MainWindow::createActions()
742
{
743
    KActionCollection* const ac = Amarok::actionCollection();
744
    const EngineController* const ec = The::engineController();
745
    const Playlist::Actions* const pa = The::playlistActions();
746
    const Playlist::Controller* const pc = The::playlistController();
747
748
    KStandardAction::keyBindings( kapp, SLOT( slotConfigShortcuts() ), ac );
749
    KStandardAction::preferences( kapp, SLOT( slotConfigAmarok() ), ac );
750
    ac->action( KStandardAction::name( KStandardAction::KeyBindings ) )->setIcon( KIcon( "configure-shortcuts-amarok" ) );
751
    ac->action( KStandardAction::name( KStandardAction::Preferences ) )->setIcon( KIcon( "configure-amarok" ) );
752
    ac->action( KStandardAction::name( KStandardAction::Preferences ) )->setMenuRole(QAction::PreferencesRole); // Define OS X Prefs menu here, removes need for ifdef later
753
754
    KStandardAction::quit( kapp, SLOT( quit() ), ac );
755
756
    KAction *action = new KAction( KIcon( "folder-amarok" ), i18n("&Add Media..."), this );
757
    ac->addAction( "playlist_add", action );
758
    connect( action, SIGNAL( triggered( bool ) ), this, SLOT( slotAddLocation() ) );
759
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_A ) );
760
761
    action = new KAction( KIcon( "edit-clear-list-amarok" ), i18nc( "clear playlist", "&Clear Playlist" ), this );
762
    connect( action, SIGNAL( triggered( bool ) ), pc, SLOT( clear() ) );
763
    ac->addAction( "playlist_clear", action );
764
765
    action = new KAction( i18nc( "Remove duplicate and dead (unplayable) tracks from the playlist", "Re&move Duplicates" ), this );
766
    connect( action, SIGNAL( triggered( bool ) ), pc, SLOT( removeDeadAndDuplicates() ) );
767
    ac->addAction( "playlist_remove_dead_and_duplicates", action );
768
769
    action = new Playlist::LayoutConfigAction( this );
770
    ac->addAction( "playlist_layout", action );
771
772
    action = new KAction( KIcon( "folder-amarok" ), i18n("&Add Stream..."), this );
773
    connect( action, SIGNAL( triggered( bool ) ), this, SLOT( slotAddStream() ) );
774
    ac->addAction( "stream_add", action );
775
776
    action = new KAction( KIcon( "document-export-amarok" ), i18n("&Export Playlist As..."), this );
777
    connect( action, SIGNAL( triggered( bool ) ), this, SLOT( exportPlaylist() ) );
778
    ac->addAction( "playlist_export", action );
779
780
    action = new KAction( KIcon( "bookmark-new" ), i18n( "Bookmark Media Sources View" ), this );
781
    ac->addAction( "bookmark_browser", action );
782
    connect( action, SIGNAL( triggered() ), The::amarokUrlHandler(), SLOT( bookmarkCurrentBrowserView() ) );
783
784
    action = new KAction( KIcon( "bookmarks-organize" ), i18n( "Bookmark Manager" ), this );
785
    ac->addAction( "bookmark_manager", action );
786
    connect( action, SIGNAL( triggered( bool ) ), SLOT( slotShowBookmarkManager() ) );
787
788
    action = new KAction( KIcon( "view-media-equalizer" ), i18n( "Equalizer" ), this );
789
    ac->addAction( "equalizer_dialog", action );
790
    connect( action, SIGNAL( triggered( bool ) ), SLOT( slotShowEqualizer() ) );
791
792
    action = new KAction( KIcon( "bookmark-new" ), i18n( "Bookmark Playlist Setup" ), this );
793
    ac->addAction( "bookmark_playlistview", action );
794
    connect( action, SIGNAL( triggered() ), The::amarokUrlHandler(), SLOT( bookmarkCurrentPlaylistView() ) );
795
796
    action = new KAction( KIcon( "bookmark-new" ), i18n( "Bookmark Context Applets" ), this );
797
    ac->addAction( "bookmark_contextview", action );
798
    connect( action, SIGNAL( triggered() ), The::amarokUrlHandler(), SLOT( bookmarkCurrentContextView() ) );
799
800
    action = new KAction( KIcon( "media-album-cover-manager-amarok" ), i18n( "Cover Manager" ), this );
801
    connect( action, SIGNAL( triggered( bool ) ), SLOT( slotShowCoverManager() ) );
802
    ac->addAction( "cover_manager", action );
803
804
    action = new KAction( KIcon("folder-amarok"), i18n("Play Media..."), this );
805
    ac->addAction( "playlist_playmedia", action );
806
    action->setShortcut( Qt::CTRL + Qt::Key_O );
807
    connect( action, SIGNAL( triggered( bool ) ), SLOT( slotPlayMedia() ) );
808
809
    action = new KAction( KIcon("preferences-plugin-script-amarok"), i18n("Script Manager"), this );
810
    connect( action, SIGNAL( triggered( bool ) ), SLOT( showScriptSelector() ) );
811
    ac->addAction( "script_manager", action );
812
813
    action = new KAction( KIcon( "media-seek-forward-amarok" ), i18n("&Seek Forward"), this );
814
    ac->addAction( "seek_forward", action );
815
    action->setShortcut( Qt::Key_Right );
816
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Plus ) );
817
    connect( action, SIGNAL( triggered( bool ) ), ec, SLOT( seekForward() ) );
818
819
    action = new KAction( KIcon( "media-seek-backward-amarok" ), i18n("&Seek Backward"), this );
820
    ac->addAction( "seek_backward", action );
821
    action->setShortcut( Qt::Key_Left );
822
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Minus ) );
823
    connect( action, SIGNAL( triggered( bool ) ), ec, SLOT( seekBackward() ) );
824
825
    PERF_LOG( "MainWindow::createActions 6" )
826
    action = new KAction( KIcon("collection-refresh-amarok"), i18n( "Update Collection" ), this );
827
    connect ( action, SIGNAL( triggered( bool ) ), CollectionManager::instance(), SLOT( checkCollectionChanges() ) );
828
    ac->addAction( "update_collection", action );
829
830
    action = new KAction( this );
831
    ac->addAction( "prev", action );
832
    action->setIcon( KIcon("media-skip-backward-amarok") );
833
    action->setText( i18n( "Previous Track" ) );
834
    action->setGlobalShortcut( KShortcut( Qt::Key_MediaPrevious ) );
835
    connect( action, SIGNAL( triggered( bool ) ), pa, SLOT( back() ) );
836
837
    action = new KAction( this );
838
    ac->addAction( "replay", action );
839
    action->setIcon( KIcon("media-playback-start") );
840
    action->setText( i18n( "Restart current track" ) );
841
    action->setGlobalShortcut( KShortcut() );
842
    connect( action, SIGNAL( triggered( bool ) ), ec, SLOT(replay()) );
843
844
    action = new KAction( this );
845
    ac->addAction( "repopulate", action );
846
    action->setText( i18n( "Repopulate Playlist" ) );
847
    action->setIcon( KIcon("view-refresh-amarok") );
848
    connect( action, SIGNAL( triggered( bool ) ), pa, SLOT( repopulateDynamicPlaylist() ) );
849
850
    action = new KAction( this );
851
    ac->addAction( "disable_dynamic", action );
852
    action->setText( i18n( "Disable Dynamic Playlist" ) );
853
    action->setIcon( KIcon("edit-delete-amarok") );
854
    //this is connected inside the dynamic playlist category
855
856
    action = new KAction( KIcon("media-skip-forward-amarok"), i18n( "Next Track" ), this );
857
    ac->addAction( "next", action );
858
    action->setGlobalShortcut( KShortcut( Qt::Key_MediaNext ) );
859
    connect( action, SIGNAL( triggered( bool ) ), pa, SLOT( next() ) );
860
861
    action = new KAction( i18n( "Increase Volume" ), this );
862
    ac->addAction( "increaseVolume", action );
863
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Plus ) );
864
    action->setShortcut( Qt::Key_Plus );
865
    connect( action, SIGNAL( triggered() ), ec, SLOT( increaseVolume() ) );
866
867
    action = new KAction( i18n( "Decrease Volume" ), this );
868
    ac->addAction( "decreaseVolume", action );
869
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Minus ) );
870
    action->setShortcut( Qt::Key_Minus );
871
    connect( action, SIGNAL( triggered() ), ec, SLOT( decreaseVolume() ) );
872
873
    action = new KAction( i18n( "Toggle Main Window" ), this );
874
    ac->addAction( "toggleMainWindow", action );
875
    action->setGlobalShortcut( KShortcut() );
876
    connect( action, SIGNAL( triggered() ), SLOT( showHide() ) );
877
878
    action = new KAction( i18n( "Toggle Full Screen" ), this );
879
    ac->addAction( "toggleFullScreen", action );
880
    action->setShortcut( KShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_F ) );
881
    connect( action, SIGNAL( triggered() ), SLOT( slotFullScreen() ) );
882
883
    action = new KAction( i18n( "Jump to" ), this );
884
    ac->addAction( "jumpTo", action );
885
    action->setShortcut( KShortcut( Qt::CTRL + Qt::Key_J ) );
886
    connect( action, SIGNAL( triggered() ), SLOT( slotJumpTo() ) );
887
888
    action = new KAction( KIcon( "music-amarok" ), i18n("Show active track"), this );
889
    ac->addAction( "show_active_track", action );
890
    connect( action, SIGNAL( triggered( bool ) ), SLOT( slotShowActiveTrack() ) );
891
892
    action = new KAction( i18n( "Show Notification Popup" ), this );
893
    ac->addAction( "showNotificationPopup", action );
894
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_O ) );
895
    connect( action, SIGNAL( triggered() ), SLOT( showNotificationPopup() ) );
896
897
    action = new KAction( i18n( "Mute Volume" ), this );
898
    ac->addAction( "mute", action );
899
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_M ) );
900
    connect( action, SIGNAL( triggered() ), ec, SLOT( toggleMute() ) );
901
902
    action = new KAction( i18n( "Last.fm: Love Current Track" ), this );
903
    ac->addAction( "loveTrack", action );
904
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_L ) );
905
    connect( action, SIGNAL( triggered() ), SLOT( slotLoveTrack() ) );
906
907
    action = new KAction( i18n( "Last.fm: Ban Current Track" ), this );
908
    ac->addAction( "banTrack", action );
909
    //action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_B ) );
910
    connect( action, SIGNAL( triggered() ), SIGNAL( banTrack() ) );
911
912
    action = new KAction( i18n( "Last.fm: Skip Current Track" ), this );
913
    ac->addAction( "skipTrack", action );
914
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_S ) );
915
    connect( action, SIGNAL( triggered() ), SIGNAL( skipTrack() ) );
916
917
    action = new KAction( KIcon( "media-track-queue-amarok" ), i18n( "Queue Track" ), this );
918
    ac->addAction( "queueTrack", action );
919
    action->setShortcut( KShortcut( Qt::CTRL + Qt::Key_D ) );
920
    connect( action, SIGNAL( triggered() ), SIGNAL( switchQueueStateShortcut() ) );
921
922
    action = new KAction( i18n( "Rate Current Track: 1" ), this );
923
    ac->addAction( "rate1", action );
924
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_1 ) );
925
    connect( action, SIGNAL( triggered() ), SLOT( setRating1() ) );
926
927
    action = new KAction( i18n( "Rate Current Track: 2" ), this );
928
    ac->addAction( "rate2", action );
929
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_2 ) );
930
    connect( action, SIGNAL( triggered() ), SLOT( setRating2() ) );
931
932
    action = new KAction( i18n( "Rate Current Track: 3" ), this );
933
    ac->addAction( "rate3", action );
934
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_3 ) );
935
    connect( action, SIGNAL( triggered() ), SLOT( setRating3() ) );
936
937
    action = new KAction( i18n( "Rate Current Track: 4" ), this );
938
    ac->addAction( "rate4", action );
939
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_4 ) );
940
    connect( action, SIGNAL( triggered() ), SLOT( setRating4() ) );
941
942
    action = new KAction( i18n( "Rate Current Track: 5" ), this );
943
    ac->addAction( "rate5", action );
944
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_5 ) );
945
    connect( action, SIGNAL( triggered() ), SLOT( setRating5() ) );
946
947
#ifdef DEBUG_BUILD_TYPE
948
    action = new KAction( i18n( "Network Request Viewer" ), this );
949
    ac->addAction( "network_request_viewer", action );
950
    action->setIcon( KIcon( "utilities-system-monitor" ) );
951
    connect( action, SIGNAL( triggered() ), SLOT( showNetworkRequestViewer() ) );
952
#endif // DEBUG_BUILD_TYPE
953
954
    action = KStandardAction::redo( pc, SLOT( redo() ), this);
955
    ac->addAction( "playlist_redo", action );
956
    action->setEnabled( false );
957
    action->setIcon( KIcon( "edit-redo-amarok" ) );
958
    connect( pc, SIGNAL( canRedoChanged( bool ) ), action, SLOT( setEnabled( bool ) ) );
959
960
    action = KStandardAction::undo( pc, SLOT( undo() ), this);
961
    ac->addAction( "playlist_undo", action );
962
    action->setEnabled( false );
963
    action->setIcon( KIcon( "edit-undo-amarok" ) );
964
    connect( pc, SIGNAL( canUndoChanged( bool ) ), action, SLOT( setEnabled( bool ) ) );
965
966
    action = new KAction( KIcon( "amarok" ), i18n( "&About Amarok" ), this );
967
    ac->addAction( "extendedAbout", action );
968
    connect( action, SIGNAL( triggered() ), SLOT( showAbout() ) );
969
970
    action = new KAction( KIcon( "tools-report-bug" ), i18n("&Report Bug..."), this );
971
    ac->addAction( "reportBug", action );
972
    connect( action, SIGNAL( triggered() ), SLOT( showReportBug() ) );
973
974
    LikeBack *likeBack = new LikeBack( LikeBack::AllButBugs,
975
        LikeBack::isDevelopmentVersion( KGlobal::mainComponent().aboutData()->version() ) );
976
    likeBack->setServer( "likeback.kollide.net", "/send.php" );
977
    likeBack->setAcceptedLanguages( QStringList( "en" ) );
978
    likeBack->setWindowNamesListing( LikeBack::WarnUnnamedWindows );    //Notify if a window has no name
979
980
    KActionCollection *likeBackActions = new KActionCollection( this, KGlobal::mainComponent() );
981
    likeBackActions->addAssociatedWidget( this );
982
    likeBack->createActions( likeBackActions );
983
984
    ac->addAction( "likeBackSendComment", likeBackActions->action( "likeBackSendComment" ) );
985
    ac->addAction( "likeBackShowIcons", likeBackActions->action( "likeBackShowIcons" ) );
986
987
    PERF_LOG( "MainWindow::createActions 8" )
988
    new Amarok::MenuAction( ac, this );
989
    new Amarok::StopAction( ac, this );
990
    new Amarok::StopPlayingAfterCurrentTrackAction( ac, this );
991
    new Amarok::PlayPauseAction( ac, this );
992
    new Amarok::ReplayGainModeAction( ac, this );
993
    new Amarok::EqualizerAction( ac, this);
994
995
    ac->addAssociatedWidget( this );
996
    foreach( QAction* action, ac->actions() )
997
        action->setShortcutContext( Qt::WindowShortcut );
998
}
999
1000
void
1001
MainWindow::setRating( int n )
1002
{
1003
    n *= 2;
1004
1005
    Meta::TrackPtr track = The::engineController()->currentTrack();
1006
    if( track )
1007
    {
1008
        // if we're setting an identical rating then we really must
1009
        // want to set the half-star below rating
1010
        if( track->rating() == n )
1011
            n -= 1;
1012
1013
        track->setRating( n );
1014
        Amarok::OSD::instance()->OSDWidget::ratingChanged( track->rating() );
1015
    }
1016
}
1017
1018
void
1019
MainWindow::createMenus()
1020
{
1021
    //BEGIN Actions menu
1022
    KMenu *actionsMenu;
1023
#ifdef Q_WS_MAC
1024
    m_menubar = new QMenuBar( 0 );  // Fixes menubar in OS X
1025
    actionsMenu = new KMenu( m_menubar );
1026
    // Add these functions to the dock icon menu in OS X
1027
    //extern void qt_mac_set_dock_menu(QMenu *);
1028
    //qt_mac_set_dock_menu(actionsMenu);
1029
    // Change to avoid duplicate menu titles in OS X
1030
    actionsMenu->setTitle( i18n("&Music") );
1031
#else
1032
    m_menubar = menuBar();
1033
    actionsMenu = new KMenu( m_menubar );
1034
    actionsMenu->setTitle( i18n("&Amarok") );
1035
#endif
1036
    actionsMenu->addAction( Amarok::actionCollection()->action("playlist_playmedia") );
1037
    actionsMenu->addSeparator();
1038
    actionsMenu->addAction( Amarok::actionCollection()->action("prev") );
1039
    actionsMenu->addAction( Amarok::actionCollection()->action("play_pause") );
1040
    actionsMenu->addAction( Amarok::actionCollection()->action("stop") );
1041
    actionsMenu->addAction( Amarok::actionCollection()->action("stop_after_current") );
1042
    actionsMenu->addAction( Amarok::actionCollection()->action("next") );
1043
1044
1045
#ifndef Q_WS_MAC    // Avoid duplicate "Quit" in OS X dock menu
1046
    actionsMenu->addSeparator();
1047
    actionsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::Quit ) ) );
1048
#endif
1049
    //END Actions menu
1050
1051
    //BEGIN Playlist menu
1052
    KMenu *playlistMenu = new KMenu( m_menubar );
1053
    playlistMenu->setTitle( i18n("&Playlist") );
1054
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_add") );
1055
    playlistMenu->addAction( Amarok::actionCollection()->action("stream_add") );
1056
    //playlistMenu->addAction( Amarok::actionCollection()->action("playlist_save") ); //FIXME: See FIXME in PlaylistDock.cpp
1057
    playlistMenu->addAction( Amarok::actionCollection()->action( "playlist_export" ) );
1058
    playlistMenu->addSeparator();
1059
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_undo") );
1060
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_redo") );
1061
    playlistMenu->addSeparator();
1062
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_clear") );
1063
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_remove_dead_and_duplicates") );
1064
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_layout") );
1065
    //END Playlist menu
1066
1067
    //BEGIN Tools menu
1068
    m_toolsMenu = new KMenu( m_menubar );
1069
    m_toolsMenu->setTitle( i18n("&Tools") );
1070
1071
    m_toolsMenu->addAction( Amarok::actionCollection()->action("bookmark_manager") );
1072
    m_toolsMenu->addAction( Amarok::actionCollection()->action("cover_manager") );
1073
    m_toolsMenu->addAction( Amarok::actionCollection()->action("equalizer_dialog") );
1074
    m_toolsMenu->addAction( Amarok::actionCollection()->action("script_manager") );
1075
#ifdef DEBUG_BUILD_TYPE
1076
    m_toolsMenu->addAction( Amarok::actionCollection()->action("network_request_viewer") );
1077
#endif // DEBUG_BUILD_TYPE
1078
    m_toolsMenu->addSeparator();
1079
    m_toolsMenu->addAction( Amarok::actionCollection()->action("update_collection") );
1080
    //END Tools menu
1081
1082
    //BEGIN Settings menu
1083
    m_settingsMenu = new KMenu( m_menubar );
1084
    m_settingsMenu->setTitle( i18n("&Settings") );
1085
1086
    //TODO use KStandardAction or KXmlGuiWindow
1087
1088
    // the phonon-coreaudio  backend has major issues with either the VolumeFaderEffect itself
1089
    // or with it in the pipeline. track playback stops every ~3-4 tracks, and on tracks >5min it
1090
    // stops at about 5:40. while we get this resolved upstream, don't make playing amarok such on osx.
1091
    // so we disable replaygain on osx
1092
1093
#ifndef Q_WS_MAC
1094
    m_settingsMenu->addAction( Amarok::actionCollection()->action("replay_gain_mode") );
1095
    m_settingsMenu->addSeparator();
1096
#endif
1097
1098
    m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::KeyBindings ) ) );
1099
    m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::Preferences ) ) );
1100
    //END Settings menu
1101
1102
    m_menubar->addMenu( actionsMenu );
1103
    m_menubar->addMenu( createPopupMenu() );
1104
    m_menubar->addMenu( playlistMenu );
1105
    m_menubar->addMenu( m_toolsMenu );
1106
    m_menubar->addMenu( m_settingsMenu );
1107
1108
    KMenu *helpMenu = Amarok::Menu::helpMenu();
1109
    helpMenu->insertAction( helpMenu->actions().first(),
1110
                            Amarok::actionCollection()->action( "reportBug" ) );
1111
    helpMenu->insertAction( helpMenu->actions().last(),
1112
                            Amarok::actionCollection()->action( "extendedAbout" ) );
1113
    helpMenu->insertAction( helpMenu->actions().at( 4 ),
1114
                            Amarok::actionCollection()->action( "likeBackSendComment" ) );
1115
    helpMenu->insertAction( helpMenu->actions().at( 5 ),
1116
                            Amarok::actionCollection()->action( "likeBackShowIcons" ) );
1117
1118
    m_menubar->addMenu( helpMenu );
1119
}
1120
1121
void
1122
MainWindow::showAbout()
1123
{
1124
    ExtendedAboutDialog dialog( &aboutData, &ocsData );
1125
    dialog.exec();
1126
}
1127
1128
void
1129
MainWindow::showReportBug()
1130
{
1131
    KBugReport * rbDialog = new KBugReport( this, true, KGlobal::mainComponent().aboutData() );
1132
    rbDialog->setObjectName( "KBugReport" );
1133
    rbDialog->exec();
1134
}
1135
1136
void
1137
MainWindow::paletteChange( const QPalette & oldPalette )
1138
{
1139
    Q_UNUSED( oldPalette )
1140
1141
    KPixmapCache cache( "Amarok-pixmaps" );
1142
    cache.discard();
1143
    The::paletteHandler()->setPalette( palette() );
1144
}
1145
1146
QSize
1147
MainWindow::backgroundSize() const
1148
{
1149
    const QPoint topLeft = mapToGlobal( QPoint( 0, 0 ) );
1150
    const QPoint bottomRight1 = mapToGlobal( QPoint( width(), height() ) );
1151
1152
    return QSize( bottomRight1.x() - topLeft.x() + 1, bottomRight1.y() - topLeft.y() );
1153
}
1154
1155
//BEGIN DOCK LAYOUT FIXING HACK ====================================================================
1156
1157
/**
1158
    Docks that are either hidden, floating or tabbed shall not be taken into account for ratio calculations
1159
    unfortunately docks that are hiddeen in tabs are not "hidden", but just have an invalid geometry
1160
*/
1161
bool
1162
MainWindow::LH_isIrrelevant( const QDockWidget *dock )
1163
{
1164
    bool ret = !dock->isVisibleTo( this ) || dock->isFloating();
1165
    if( !ret )
1166
    {
1167
        const QRect r = dock->geometry();
1168
        ret = r.right() < 1 || r.bottom() < 1;
1169
    }
1170
    return  ret;
1171
}
1172
1173
/**
1174
    We've atm three dock, ie. 0 or 1 tabbar. It's also the only bar as direct child.
1175
    We need to dig it as it constructed and deleted on UI changes by the user
1176
*/
1177
QTabBar *
1178
MainWindow::LH_dockingTabbar()
1179
{
1180
    QObjectList kids = children();
1181
    foreach ( QObject *kid, kids )
1182
    {
1183
        if( kid->isWidgetType() )
1184
            if( QTabBar *bar = qobject_cast<QTabBar*>( kid ) )
1185
                return bar;
1186
    }
1187
    return 0;
1188
}
1189
1190
/**
1191
    this function calculates the area covered by the docks - i.e. rect() minus menubar and toolbar
1192
*/
1193
void
1194
MainWindow::LH_extend( QRect &target, const QDockWidget *dock )
1195
{
1196
    if( LH_isIrrelevant( dock ) )
1197
        return;
1198
    if( target.isNull() )
1199
    {
1200
        target = dock->geometry();
1201
        return;
1202
    }
1203
    const QRect source = dock->geometry();
1204
    if( source.left() < target.left() ) target.setLeft( source.left() );
1205
    if( source.right() > target.right() ) target.setRight( source.right() );
1206
    if( source.top() < target.top() ) target.setTop( source.top() );
1207
    if( source.bottom() > target.bottom() ) target.setBottom( source.bottom() );
1208
}
1209
1210
/**
1211
    which size the dock should have in our opinion, based upon the ratios and the docking area.
1212
    takes size constraints into account
1213
*/
1214
QSize
1215
MainWindow::LH_desiredSize( QDockWidget *dock, const QRect &area, float rx, float ry, int splitter )
1216
{
1217
    if( LH_isIrrelevant( dock ) )
1218
        return QSize( 0, 0 );
1219
    QSize ret;
1220
    int tabHeight = 0;
1221
    if( !tabifiedDockWidgets( dock ).isEmpty() )
1222
    {
1223
        if( QTabBar *bar = LH_dockingTabbar() )
1224
            tabHeight = bar->height();
1225
    }
1226
    const QSize min = dock->minimumSize();
1227
    ret.setWidth( qMax( min.width(), (int)( rx == 1.0 ? area.width() : rx*area.width() - splitter/2 ) ) );
1228
    ret.setHeight( qMax( min.height(), (int)( ry == 1.0 ? area.height() - tabHeight : ry*area.height() - ( splitter/2 + tabHeight ) ) ) );
1229
    return ret;
1230
}
1231
#define DESIRED_SIZE(_DOCK_) LH_desiredSize( _DOCK_##Dock, m_dockingRect, _DOCK_##Ratio.x, _DOCK_##Ratio.y, splitterSize )
1232
1233
1234
/**
1235
    used to check whether the current dock size "more or less" matches our opinion
1236
    if one of the sizes isNull() it's invalid and the current size is ok.
1237
*/
1238
bool
1239
MainWindow::LH_fuzzyMatch( const QSize &sz1, const QSize &sz2 )
1240
{
1241
    return sz1.isNull() || sz2.isNull() ||
1242
           ( sz1.width() < sz2.width() + 6 && sz1.width() > sz2.width() - 6 &&
1243
             sz1.height() < sz2.height() + 6 && sz1.height() > sz2.height() - 6 );
1244
}
1245
1246
/**
1247
    whether the dock has reached it's minimum width OR height
1248
*/
1249
bool
1250
MainWindow::LH_isConstrained( const QDockWidget *dock )
1251
{
1252
    if( LH_isIrrelevant( dock ) )
1253
        return false;
1254
    return dock->minimumWidth() == dock->width() || dock->minimumHeight() == dock->height();
1255
}
1256
1257
#define FREE_SIZE(_DOCK_,_INDEX_)   if( !_DOCK_->isFloating() ) { \
1258
                                        _DOCK_->setMinimumSize( mins[_INDEX_] );\
1259
                                        _DOCK_->setMaximumSize( maxs[_INDEX_] ); } //
1260
1261
void
1262
MainWindow::initLayoutHack()
1263
{
1264
    // the init _should_ be superflous, but hey: why risk it ;-)
1265
    m_playlistRatio.x = 0.0; m_playlistRatio.y = 0.0;
1266
    m_browserRatio = m_contextRatio = m_playlistRatio;
1267
1268
    m_dockingRect = QRect();
1269
    LH_extend( m_dockingRect, m_browserDock );
1270
    LH_extend( m_dockingRect, m_contextDock );
1271
    LH_extend( m_dockingRect, m_playlistDock );
1272
1273
    // initially calculate ratios
1274
    updateDockRatio( m_browserDock );
1275
    updateDockRatio( m_contextDock );
1276
    updateDockRatio( m_playlistDock );
1277
1278
    m_LH_initialized = true;
1279
1280
    // connect to dock changes (esp. tab selection)
1281
    // this _is_ required...
1282
    connect ( m_browserDock, SIGNAL( visibilityChanged( bool ) ), this, SLOT( updateDockRatio() ) );
1283
    connect ( m_contextDock, SIGNAL( visibilityChanged( bool ) ), this, SLOT( updateDockRatio() ) );
1284
    connect ( m_playlistDock, SIGNAL( visibilityChanged( bool ) ), this, SLOT( updateDockRatio() ) );
1285
1286
    // but i think these can be omitted (move along a show event for tech. reasons)
1287
//     topLevelChanged( bool )
1288
//     dockLocationChanged(Qt::DockWidgetArea)
1289
1290
    slotShowActiveTrack();    // See comment in 'playlist/view/PrettyListView.cpp constructor'.
1291
}
1292
1293
void
1294
MainWindow::resizeEvent( QResizeEvent * event )
1295
{
1296
    DEBUG_BLOCK
1297
    // NOTICE: uncomment this to test default behaviour
1298
//     QMainWindow::resizeEvent( event );
1299
//     return;
1300
1301
    // reset timer for saving changes
1302
    // 30 secs - this is no more crucial and we don't have to store every sh** ;-)
1303
    m_saveLayoutChangesTimer->start( 30000 );
1304
1305
    // prevent flicker, NOTICE to prevent all flicker, this must be done by an (bug prone) passing-on eventfiler :-(
1306
    setUpdatesEnabled( false );
1307
1308
    // stop listening to resize events, we're gonna trigger them now
1309
    m_browserDock->removeEventFilter( this );
1310
    m_contextDock->removeEventFilter( this );
1311
    m_playlistDock->removeEventFilter( this );
1312
1313
    QMainWindow::resizeEvent( event );
1314
1315
    // ok, the window was resized and our little docklings fill the entire docking area
1316
    // -> m_dockingRect is their bounding rect
1317
    m_dockingRect = QRect();
1318
    LH_extend( m_dockingRect, m_browserDock );
1319
    LH_extend( m_dockingRect, m_contextDock );
1320
    LH_extend( m_dockingRect, m_playlistDock );
1321
1322
    // if we hit a minimumSize constraint, we can no more keep aspects at all atm. and just hope
1323
    // that Qt distributed the lacking space evenly ;-)
1324
    if( !m_LH_initialized ||
1325
        LH_isConstrained( m_contextDock ) || LH_isConstrained( m_browserDock ) || LH_isConstrained( m_playlistDock ) )
1326
    {
1327
        setUpdatesEnabled( true );
1328
1329
        // continue to listen to resize events
1330
        m_browserDock->installEventFilter( this );
1331
        m_contextDock->installEventFilter( this );
1332
        m_playlistDock->installEventFilter( this );
1333
1334
        return;
1335
    }
1336
1337
    int splitterSize = style()->pixelMetric( QStyle::PM_DockWidgetSeparatorExtent, 0, 0 );
1338
    const QSize dContextSz = DESIRED_SIZE( m_context );
1339
    const QSize dBrowsersSz = DESIRED_SIZE( m_browser );
1340
    const QSize dPlaylistSz = DESIRED_SIZE( m_playlist );
1341
1342
    // good god: prevent recursive resizes ;-)
1343
    setFixedSize( size() );
1344
1345
    layout()->setEnabled( false );
1346
    QCoreApplication::sendPostedEvents( this, QEvent::LayoutRequest );
1347
    layout()->invalidate();
1348
    layout()->setEnabled( true );
1349
1350
    // don't be super picky - there's an integer imprecision anyway and we just want to fix
1351
    // major resize errors w/o being to intrusive or flickering
1352
    if( !( LH_fuzzyMatch( dContextSz, m_contextDock->size() ) &&
1353
           LH_fuzzyMatch( dBrowsersSz, m_browserDock->size() ) &&
1354
           LH_fuzzyMatch( dPlaylistSz, m_playlistDock->size() ) ) )
1355
    {
1356
//         debug() << "SIZE MISMATCH, FORCING";
1357
        const QSize mins[3] = { m_contextDock->minimumSize(), m_browserDock->minimumSize(), m_playlistDock->minimumSize() };
1358
        const QSize maxs[3] = { m_contextDock->maximumSize(), m_browserDock->maximumSize(), m_playlistDock->maximumSize() };
1359
1360
        // fix sizes to our idea, so the layout has few options left ;-)
1361
        if( !m_contextDock->isFloating() ) m_contextDock->setFixedSize( dContextSz );
1362
        if( !m_browserDock->isFloating() ) m_browserDock->setFixedSize( dBrowsersSz );
1363
        if( !m_playlistDock->isFloating() ) m_playlistDock->setFixedSize( dPlaylistSz );
1364
1365
        // trigger a no-choice layouting
1366
        layout()->activate();
1367
        QCoreApplication::sendPostedEvents( this, QEvent::LayoutRequest );
1368
1369
        // unleash sizes
1370
        layout()->setEnabled( false );
1371
        FREE_SIZE( m_contextDock, 0 );
1372
        FREE_SIZE( m_browserDock, 1 );
1373
        FREE_SIZE( m_playlistDock, 2 );
1374
        layout()->setEnabled( true );
1375
    }
1376
1377
    // unleash size
1378
    setMinimumSize( 0, 0 );
1379
    setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
1380
1381
    // continue to listen to resize events
1382
    m_browserDock->installEventFilter( this );
1383
    m_contextDock->installEventFilter( this );
1384
    m_playlistDock->installEventFilter( this );
1385
1386
    // update on screen
1387
    setUpdatesEnabled( true );
1388
}
1389
1390
#undef DESIRED_SIZE
1391
#undef FREE_SIZE
1392
1393
// i hate these slots - but we get no show/hide events on tab selection :-(
1394
void MainWindow::updateDockRatio( QDockWidget *dock )
1395
{
1396
    if( !LH_isIrrelevant( dock ) )
1397
    {
1398
        QRect area = m_dockingRect;
1399
        int tabHeight = 0;
1400
        if( !tabifiedDockWidgets( dock ).isEmpty() )
1401
        {
1402
            if( QTabBar *bar = LH_dockingTabbar() )
1403
                tabHeight = bar->height();
1404
        }
1405
        int splitterSize = style()->pixelMetric( QStyle::PM_DockWidgetSeparatorExtent, 0, dock );
1406
1407
        Ratio r;
1408
        if( dock->width() == area.width() )
1409
            r.x = 1.0;
1410
        else
1411
            r.x = ( (float)( dock->width() + splitterSize/2) ) / area.width();
1412
        if( dock->height() == area.height() - tabHeight )
1413
            r.y = 1.0;
1414
        else
1415
            r.y = ( (float)( dock->height() + splitterSize/2 + tabHeight ) ) / area.height();
1416
1417
        if( dock == m_browserDock )
1418
            m_browserRatio = r;
1419
        else if( dock == m_contextDock )
1420
            m_contextRatio = r;
1421
        else
1422
            m_playlistRatio = r;
1423
//         debug() << "==>" << r.x << r.y;
1424
    }
1425
    // UI changed -> trigger delayed storage
1426
    m_saveLayoutChangesTimer->start( 30000 );
1427
}
1428
1429
// this slot is connected to the visibilityChange event, fired on show/hide (in most cases... *sigh*) and tab changes
1430
void MainWindow::updateDockRatio()
1431
{
1432
    if( QDockWidget *dock = qobject_cast<QDockWidget*>( sender() ) )
1433
        updateDockRatio( dock );
1434
}
1435
1436
bool MainWindow::eventFilter( QObject *o, QEvent *e )
1437
{
1438
    // NOTICE this _must_ be handled by an eventfilter, as otherwise the "spliters" eventfilter
1439
    // will eat and we don't receive it
1440
    if( o == this )
1441
    {
1442
        if( e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease )
1443
        {
1444
            QMouseEvent *me = static_cast<QMouseEvent*>( e );
1445
            if( me->button() == Qt::LeftButton )
1446
                m_mouseDown = ( e->type() == QEvent::MouseButtonPress );
1447
        }
1448
1449
        return false;
1450
1451
1452
    }
1453
1454
    if( ( ( e->type() == QEvent::Resize && m_mouseDown ) || // only when resized by the splitter :)
1455
             e->type() == QEvent::Show || e->type() == QEvent::Hide ) && // show/hide is _NOT_ sufficient for tab changes
1456
        ( o == m_browserDock || o == m_contextDock || o == m_playlistDock ) )
1457
    {
1458
        QDockWidget *dock = static_cast<QDockWidget*>( o );
1459
//         if(e->type() == QEvent::Resize)
1460
//             debug() << dock << dock->size() << m_dockingRect.size();
1461
//         else
1462
//             debug() << "other!" << dock << dock->size() << m_dockingRect.size();
1463
        updateDockRatio( dock );
1464
    }
1465
    return false;
1466
}
1467
1468
//END DOCK LAYOUT FIXING HACK ======================================================================
1469
1470
QPoint
1471
MainWindow::globalBackgroundOffset()
1472
{
1473
    return menuBar()->mapToGlobal( QPoint( 0, 0 ) );
1474
}
1475
1476
QRect
1477
MainWindow::contextRectGlobal() const
1478
{
1479
    //debug() << "pos of context vidget within main window is: " << m_contextWidget->pos();
1480
    //FIXME
1481
    QPoint contextPos = mapToGlobal( m_contextDock->pos() );
1482
    return QRect( contextPos.x(), contextPos.y(), m_contextDock->width(), m_contextDock->height() );
1483
}
1484
1485
void
1486
MainWindow::engineStateChanged( Phonon::State state, Phonon::State oldState )
1487
{
1488
    Q_UNUSED( oldState )
1489
1490
    Meta::TrackPtr track = The::engineController()->currentTrack();
1491
1492
    switch( state )
1493
    {
1494
    case Phonon::StoppedState:
1495
        m_currentTrack = 0;
1496
        setPlainCaption( i18n( AMAROK_CAPTION ) );
1497
        break;
1498
1499
    case Phonon::PlayingState:
1500
        unsubscribeFrom( m_currentTrack );
1501
        m_currentTrack = track;
1502
        subscribeTo( track );
1503
        metadataChanged( track );
1504
        break;
1505
1506
    case Phonon::PausedState:
1507
        setPlainCaption( i18n( "Paused  ::  %1", QString( AMAROK_CAPTION ) ) );
1508
        break;
1509
1510
    default:
1511
        break;
1512
    }
1513
}
1514
1515
void
1516
MainWindow::engineNewTrackPlaying()
1517
{
1518
    m_currentTrack = The::engineController()->currentTrack();
1519
    metadataChanged( m_currentTrack );
1520
}
1521
1522
void
1523
MainWindow::metadataChanged( Meta::TrackPtr track )
1524
{
1525
    if( track && The::engineController()->currentTrack() == track )
1526
        setPlainCaption( i18n( "%1 - %2  ::  %3", track->artist() ? track->artist()->prettyName() : i18n( "Unknown" ), track->prettyName(), AMAROK_CAPTION ) );
1527
}
1528
1529
CollectionWidget *
1530
MainWindow::collectionBrowser()
1531
{
1532
    return m_collectionBrowser;
1533
}
1534
1535
QString
1536
MainWindow::activeBrowserName()
1537
{
1538
    if( m_browserDock->list()->activeCategory() )
1539
        return m_browserDock->list()->activeCategory()->name();
1540
    else
1541
        return QString();
1542
}
1543
1544
PlaylistBrowserNS::PlaylistBrowser *
1545
MainWindow::playlistBrowser()
1546
{
1547
    return m_playlistBrowser;
1548
}
1549
1550
void
1551
MainWindow::setLayoutLocked( bool locked )
1552
{
1553
    DEBUG_BLOCK
1554
1555
    if( locked )
1556
    {
1557
        m_browserDock->setMovable( false );
1558
        m_contextDock->setMovable( false );
1559
        m_playlistDock->setMovable( false );
1560
1561
        m_slimToolbar->setFloatable( false );
1562
        m_slimToolbar->setMovable( false );
1563
1564
        m_mainToolbar->setFloatable( false );
1565
        m_mainToolbar->setMovable( false );
1566
    }
1567
    else
1568
    {
1569
        m_browserDock->setMovable( true );
1570
        m_contextDock->setMovable( true );
1571
        m_playlistDock->setMovable( true );
1572
1573
        m_slimToolbar->setFloatable( true );
1574
        m_slimToolbar->setMovable( true );
1575
1576
        m_mainToolbar->setFloatable( true );
1577
        m_mainToolbar->setMovable( true );
1578
    }
1579
1580
    AmarokConfig::setLockLayout( locked );
1581
    AmarokConfig::self()->writeConfig();
1582
    m_layoutLocked = locked;
1583
}
1584
1585
bool
1586
MainWindow::isLayoutLocked() const
1587
{
1588
    return m_layoutLocked;
1589
}
1590
1591
void
1592
MainWindow::restoreLayout()
1593
{
1594
    DEBUG_BLOCK
1595
1596
    // Do not restore the layout if the main window is hidden
1597
    // Qt takes widgets out of the layout if they're not visible.
1598
    // So this is not going to work. Also see bug 244583
1599
    if (!isVisible())
1600
        return;
1601
1602
    QFile file( Amarok::saveLocation() + "layout" );
1603
    QByteArray layout;
1604
    if( file.open( QIODevice::ReadOnly ) )
1605
    {
1606
        layout = file.readAll();
1607
        file.close();
1608
    }
1609
1610
    if( !restoreState( layout, LAYOUT_VERSION ) )
1611
    {
1612
        //since no layout has been loaded, we know that the items are all placed next to each other in the main window
1613
        //so get the combined size of the widgets, as this is the space we have to play with. Then figure out
1614
        //how much to give to each. Give the context view any pixels leftover from the integer division.
1615
1616
        //int totalWidgetWidth = m_browsersDock->width() + m_contextView->width() + m_playlistDock->width();
1617
        int totalWidgetWidth = contentsRect().width();
1618
1619
        //get the width of the splitter handles, we need to subtract these...
1620
        const int splitterHandleWidth = style()->pixelMetric( QStyle::PM_DockWidgetSeparatorExtent, 0, 0 );
1621
        debug() << "splitter handle widths " << splitterHandleWidth;
1622
1623
        totalWidgetWidth -= ( splitterHandleWidth * 2 );
1624
1625
        debug() << "mainwindow width" <<  contentsRect().width();
1626
        debug() << "totalWidgetWidth" <<  totalWidgetWidth;
1627
1628
        const int widgetWidth = totalWidgetWidth / 3;
1629
        const int leftover = totalWidgetWidth - 3*widgetWidth;
1630
1631
        //We need to set fixed widths initially, just until the main window has been properly layed out. As soon as this has
1632
        //happened, we will unlock these sizes again so that the elements can be resized by the user.
1633
        const int mins[3] = { m_browserDock->minimumWidth(), m_contextDock->minimumWidth(), m_playlistDock->minimumWidth() };
1634
        const int maxs[3] = { m_browserDock->maximumWidth(), m_contextDock->maximumWidth(), m_playlistDock->maximumWidth() };
1635
1636
        m_browserDock->setFixedWidth( widgetWidth );
1637
        m_contextDock->setFixedWidth( widgetWidth + leftover );
1638
        m_playlistDock->setFixedWidth( widgetWidth );
1639
        this->layout()->activate();
1640
1641
        m_browserDock->setMinimumWidth( mins[0] ); m_browserDock->setMaximumWidth( maxs[0] );
1642
        m_contextDock->setMinimumWidth( mins[1] ); m_contextDock->setMaximumWidth( maxs[1] );
1643
        m_playlistDock->setMinimumWidth( mins[2] ); m_playlistDock->setMaximumWidth( maxs[2] );
1644
    }
1645
1646
    // Ensure that only one toolbar is visible
1647
    if( !m_mainToolbar->isHidden() && !m_slimToolbar->isHidden() )
1648
        m_slimToolbar->hide();
1649
1650
    // Ensure that we don't end up without any toolbar (can happen after upgrading)
1651
    if( m_mainToolbar->isHidden() && m_slimToolbar->isHidden() )
1652
        m_mainToolbar->show();
1653
1654
    m_layoutEverRestored = true;
1655
}
1656
1657
1658
bool
1659
MainWindow::playAudioCd()
1660
{
1661
    DEBUG_BLOCK
1662
    //drop whatever we are doing and play auidocd
1663
1664
    QList<Collections::Collection*> collections = CollectionManager::instance()->viewableCollections();
1665
1666
    foreach( Collections::Collection *collection, collections )
1667
    {
1668
        if( collection->collectionId() == "AudioCd" )
1669
        {
1670
1671
            debug() << "got audiocd collection";
1672
1673
            Collections::MemoryCollection * cdColl = dynamic_cast<Collections::MemoryCollection *>( collection );
1674
1675
            if( !cdColl || cdColl->trackMap().count() == 0 )
1676
            {
1677
                debug() << "cd collection not ready yet (track count = 0 )";
1678
                m_waitingForCd = true;
1679
                return false;
1680
            }
1681
1682
            The::engineController()->stop( true );
1683
            The::playlistController()->clear();
1684
1685
            Collections::QueryMaker * qm = collection->queryMaker();
1686
            qm->setQueryType( Collections::QueryMaker::Track );
1687
            The::playlistController()->insertOptioned( qm, Playlist::DirectPlay );
1688
1689
            m_waitingForCd = false;
1690
            return true;
1691
        }
1692
    }
1693
1694
    debug() << "waiting for cd...";
1695
    m_waitingForCd = true;
1696
    return false;
1697
}
1698
1699
bool
1700
MainWindow::isWaitingForCd() const
1701
{
1702
    DEBUG_BLOCK
1703
    debug() << "waiting?: " << m_waitingForCd;
1704
    return m_waitingForCd;
1705
}
1706
1707
#include "MainWindow.moc"