1
/***************************************************************************
2
  begin                : Fre Nov 15 2002
3
  copyright            : (C) Mark Kretschmann <markey@web.de>
4
                       : (C) Max Howell <max.howell@methylblue.com>
5
                       : (C) G??bor Lehel <illissius@gmail.com>
6
                       : (C) Nikolaj Hald Nielsen
7
***************************************************************************/
8
9
/***************************************************************************
10
 *                                                                         *
11
 *   This program is free software; you can redistribute it and/or modify  *
12
 *   it under the terms of the GNU General Public License as published by  *
13
 *   the Free Software Foundation; either version 2 of the License, or     *
14
 *   (at your option) any later version.                                   *
15
 *                                                                         *
16
 ***************************************************************************/
17
18
#define DEBUG_PREFIX "MainWindow"
19
20
#include "MainWindow.h"
21
22
#include <config-amarok.h>    //HAVE_LIBVISUAL definition
23
24
#include "ActionClasses.h"
25
#include "Amarok.h"
26
#include "Debug.h"
27
#include "EngineController.h" //for actions in ctor
28
#include "MainToolbar.h"
29
#include "Osd.h"
30
#include "PaletteHandler.h"
31
#include "ScriptManager.h"
32
#include "SearchWidget.h"
33
#include "amarokconfig.h"
34
#include "amarokurls/AmarokUrlHandler.h"
35
36
#include "browsers/collectionbrowser/CollectionWidget.h"
37
#include "browsers/filebrowser/FileBrowser.h"
38
#include "browsers/playlistbrowser/PlaylistBrowser.h"
39
#include "browsers/servicebrowser/ServiceBrowser.h"
40
#include "collection/CollectionManager.h"
41
#include "context/ContextScene.h"
42
#include "context/ContextView.h"
43
#include "context/ToolbarView.h"
44
#include "covermanager/CoverManager.h" // for actions
45
#include "playlist/PlaylistActions.h"
46
#include "playlist/PlaylistController.h"
47
#include "playlist/PlaylistModel.h"
48
#include "playlist/PlaylistWidget.h"
49
#include "playlistmanager/PlaylistFileProvider.h"
50
#include "playlistmanager/PlaylistManager.h"
51
#include "services/ServicePluginManager.h"
52
#include "services/scriptable/ScriptableService.h"
53
#include "statusbar/StatusBar.h"
54
#include "SvgHandler.h"
55
#include "widgets/Splitter.h"
56
//#include "mediabrowser.h"
57
58
#include <QCheckBox>
59
#include <QDesktopWidget>
60
#include <QDockWidget>
61
#include <QList>
62
#include <QSizeGrip>
63
#include <QVBoxLayout>
64
65
#include <KAction>          //m_actionCollection
66
#include <KActionCollection>
67
#include <KApplication>     //kapp
68
#include <KFileDialog>      //savePlaylist(), openPlaylist()
69
#include <KInputDialog>     //slotAddStream()
70
#include <KMessageBox>
71
#include <KLocale>
72
#include <KMenu>
73
#include <KMenuBar>
74
#include <KPixmapCache>
75
#include <KStandardAction>
76
#include <KStandardDirs>
77
#include <KWindowSystem>
78
#include <kabstractfilewidget.h> //savePlaylist()
79
80
#include <plasma/plasma.h>
81
82
#ifdef Q_WS_X11
83
#include <fixx11h.h>
84
#endif
85
86
#ifdef Q_WS_MAC
87
#include "mac/GrowlInterface.h"
88
#endif
89
90
// Let people know OS X and Windows versions are still work-in-progress
91
#if defined(Q_WS_MAC) || defined(Q_WS_WIN)
92
#define AMAROK_CAPTION "Amarok 2 beta"
93
#else
94
#define AMAROK_CAPTION "Amarok"
95
#endif
96
97
class ContextWidget : public KVBox
98
{
99
    // Set a useful size default of the center tab.
100
    public:
101
        ContextWidget( QWidget *parent ) : KVBox( parent ) {}
102
103
        QSize sizeHint() const
104
        {
105
            return QSize( static_cast<QWidget*>( parent() )->size().width() / 3, 300 );
106
        }
107
};
108
109
QPointer<MainWindow> MainWindow::s_instance = 0;
110
111
MainWindow::MainWindow()
112
    : KMainWindow( 0 )
113
    , EngineObserver( The::engineController() )
114
    , m_lastBrowser( 0 )
115
{
116
    DEBUG_BLOCK
117
118
    setObjectName( "MainWindow" );
119
    s_instance = this;
120
121
#ifdef Q_WS_MAC
122
    QSizeGrip* grip = new QSizeGrip( this );
123
    GrowlInterface* growl = new GrowlInterface( qApp->applicationName() );
124
#endif
125
126
    StatusBar * statusBar = new StatusBar( this );
127
128
    setStatusBar( statusBar );
129
130
    // Sets caption and icon correctly (needed e.g. for GNOME)
131
    kapp->setTopWidget( this );
132
    PERF_LOG( "Set Top Widget" )
133
    createActions();
134
    PERF_LOG( "Created actions" )
135
136
    //new K3bExporter();
137
138
    KConfigGroup config = Amarok::config();
139
    const QSize size = config.readEntry( "MainWindow Size", QSize() );
140
    const QPoint pos = config.readEntry( "MainWindow Position", QPoint() );
141
    if( size.isValid() )
142
    {
143
        resize( size );
144
        move( pos );
145
    }
146
147
    The::paletteHandler()->setPalette( palette() );
148
    setPlainCaption( i18n( AMAROK_CAPTION ) );
149
150
    init();  // We could as well move the code from init() here, but meh.. getting a tad long
151
}
152
153
MainWindow::~MainWindow()
154
{
155
    DEBUG_BLOCK
156
157
    KConfigGroup config = Amarok::config();
158
    config.writeEntry( "MainWindow Size", size() );
159
    config.writeEntry( "MainWindow Position", pos() );
160
161
    QList<int> sPanels;
162
163
    //foreach( int a, m_splitter->saveState() )
164
    //    sPanels.append( a );
165
166
167
    //save layout to file. Does not go into to rc as it is binary data.
168
    QFile file( Amarok::saveLocation() + "layout" );
169
    if ( file.open( QIODevice::ReadWrite | QIODevice::Unbuffered | QIODevice::Truncate ) )
170
    {
171
        file.write( saveState( LAYOUT_VERSION ) );
172
        file.close();
173
    }
174
175
    //AmarokConfig::setPanelsSavedState( sPanels );
176
177
    delete m_browserDummyTitleBarWidget;
178
    delete m_contextDummyTitleBarWidget;
179
    delete m_playlistDummyTitleBarWidget;
180
    
181
    delete m_playlistFiles;
182
    delete m_contextView;
183
    delete m_corona;
184
    //delete m_splitter;
185
    delete The::statusBar();
186
    delete m_controlBar;
187
    delete The::svgHandler();
188
    delete The::paletteHandler();
189
}
190
191
192
///////// public interface
193
194
/**
195
 * This function will initialize the main window.
196
 */
197
void
198
MainWindow::init()
199
{
200
    DEBUG_BLOCK
201
202
    layout()->setContentsMargins( 0, 0, 0, 0 );
203
    layout()->setSpacing( 0 );
204
205
    m_controlBar = new MainToolbar( 0 );
206
    m_controlBar->layout()->setContentsMargins( 0, 0, 0, 0 );
207
    m_controlBar->layout()->setSpacing( 0 );
208
    m_controlBar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea );
209
    m_controlBar->setMovable ( true );
210
211
    addToolBar( Qt::TopToolBarArea, m_controlBar );
212
213
    PERF_LOG( "Create sidebar" )
214
    m_browsers = new BrowserWidget( this );
215
    m_browsers->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored );
216
217
    m_browserDummyTitleBarWidget = new QWidget();
218
    m_contextDummyTitleBarWidget = new QWidget();
219
    m_playlistDummyTitleBarWidget = new QWidget();
220
221
    m_browsersDock = new QDockWidget( i18n( "Browsers" ), this );
222
    m_browsersDock->setObjectName( "Browsers dock" );
223
    m_browsersDock->setWidget( m_browsers );
224
    m_browsersDock->setAllowedAreas( Qt::AllDockWidgetAreas );
225
    
226
227
    PERF_LOG( "Create Playlist" )
228
    m_playlistWidget = new Playlist::Widget( 0 );
229
    m_playlistWidget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored );
230
    m_playlistWidget->setFocus( Qt::ActiveWindowFocusReason );
231
232
    m_playlistDock = new QDockWidget( i18n( "Playlist" ), this );
233
    m_playlistDock->setObjectName( "Playlist dock" );
234
    m_playlistDock->setWidget( m_playlistWidget );
235
    m_playlistDock->setAllowedAreas( Qt::AllDockWidgetAreas );
236
    
237
    PERF_LOG( "Playlist created" )
238
239
    createMenus();
240
241
    PERF_LOG( "Creating ContextWidget" )
242
    m_contextWidget = new ContextWidget( this );
243
    PERF_LOG( "ContextWidget created" )
244
    m_contextWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
245
    m_contextWidget->setSpacing( 0 );
246
    m_contextWidget->setFrameShape( QFrame::NoFrame );
247
    m_contextWidget->setFrameShadow( QFrame::Sunken );
248
    m_contextWidget->setMinimumSize( 100, 100 );
249
    PERF_LOG( "Creating ContexScene" )
250
251
    m_corona = new Context::ContextScene( this );
252
    connect( m_corona, SIGNAL( containmentAdded( Plasma::Containment* ) ),
253
            this, SLOT( createContextView( Plasma::Containment* ) ) );
254
255
    m_contextDock = new QDockWidget( i18n( "Context" ), this );
256
    m_contextDock->setObjectName( "Context dock" );
257
    m_contextDock->setWidget( m_contextWidget );
258
    m_contextDock->setAllowedAreas( Qt::AllDockWidgetAreas );
259
260
    PERF_LOG( "ContextScene created" )
261
262
    PERF_LOG( "Loading default contextScene" )
263
    m_corona->loadDefaultSetup(); // this method adds our containment to the scene
264
    PERF_LOG( "Loaded default contextScene" )
265
266
    connect( m_browsers, SIGNAL( widgetActivated( int ) ), SLOT( slotShrinkBrowsers( int ) ) );
267
268
    setDockOptions ( QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks );
269
    
270
    addDockWidget( Qt::LeftDockWidgetArea, m_browsersDock );
271
    addDockWidget( Qt::LeftDockWidgetArea, m_contextDock );
272
    addDockWidget( Qt::RightDockWidgetArea, m_playlistDock );
273
274
    KConfigGroup config = Amarok::config( "General Options" );
275
    const bool locked = config.readEntry( "Lock Layout", true );
276
277
    setLayoutLocked( locked );
278
279
    //<Browsers>
280
    {
281
        Debug::Block block( "Creating browsers. Please report long start times!" );
282
283
284
285
286
        PERF_LOG( "Creating CollectionWidget" )
287
        m_collectionBrowser = new CollectionWidget( "collections", 0 );
288
        m_collectionBrowser->setPrettyName( i18n( "Collections" ) );
289
        m_collectionBrowser->setIcon( KIcon( "collection-amarok" ) );
290
        m_collectionBrowser->setShortDescription( i18n( "The list of available collections" ) );
291
        m_browsers->list()->addCategory( m_collectionBrowser );
292
        PERF_LOG( "Created CollectionWidget" )
293
294
                
295
        PERF_LOG( "Creating ServiceBrowser" )
296
        ServiceBrowser *internetContentServiceBrowser = ServiceBrowser::instance();
297
        internetContentServiceBrowser->setParent( 0 );
298
        internetContentServiceBrowser->setPrettyName( i18n( "Internet" ) );
299
        internetContentServiceBrowser->setIcon( KIcon( "applications-internet" ) );
300
        internetContentServiceBrowser->setShortDescription( i18n( "Sources  of online content" ) );
301
        m_browsers->list()->addCategory( internetContentServiceBrowser );
302
        PERF_LOG( "Created ServiceBrowser" )
303
304
        m_playlistFiles = new PlaylistFileProvider();
305
        The::playlistManager()->addProvider( m_playlistFiles, PlaylistManager::UserPlaylist );
306
307
        PERF_LOG( "Creating PlaylistBrowser" )
308
        m_playlistBrowser = new PlaylistBrowserNS::PlaylistBrowser( "playlists", 0 );
309
        m_playlistBrowser->setPrettyName( i18n("Playlists") );
310
        m_playlistBrowser->setIcon( KIcon( "view-media-playlist-amarok" ) );
311
        m_playlistBrowser->setShortDescription( i18n( "Different kinds of playlists" ) );
312
        m_browsers->list()->addCategory( m_playlistBrowser );
313
        PERF_LOG( "CreatedPlaylsitBrowser" )
314
315
                
316
        PERF_LOG( "Creating FileBrowser" )
317
        FileBrowser::Widget * fileBrowser = new FileBrowser::Widget( "files", 0 );
318
        fileBrowser->setPrettyName( i18n("Files") );
319
        fileBrowser->setIcon( KIcon( "folder-amarok" ) );
320
        fileBrowser->setShortDescription( i18n( "Browse local files" ) );
321
        m_browsers->list()->addCategory( fileBrowser );
322
        PERF_LOG( "Created FileBrowser" )
323
324
        PERF_LOG( "Initialising ServicePluginManager" )
325
        ServicePluginManager::instance()->init();
326
        PERF_LOG( "Initialised ServicePluginManager" )
327
328
        internetContentServiceBrowser->setScriptableServiceManager( The::scriptableServiceManager() );
329
        PERF_LOG( "ScriptableServiceManager done" )
330
331
        #undef addBrowserMacro
332
        PERF_LOG( "finished MainWindow::init" )
333
    }
334
335
    The::amarokUrlHandler(); //Instantiate
336
337
    //restore the layout
338
    restoreLayout();
339
340
}
341
342
void
343
MainWindow::createContextView( Plasma::Containment *containment )
344
{
345
    DEBUG_BLOCK
346
    disconnect( m_corona, SIGNAL( containmentAdded( Plasma::Containment* ) ),
347
            this, SLOT( createContextView( Plasma::Containment* ) ) );
348
    PERF_LOG( "Creating ContexView" )
349
    m_contextView = new Context::ContextView( containment, m_corona, m_contextWidget );
350
    m_contextView->setFrameShape( QFrame::NoFrame );
351
    m_contextToolbarView = new Context::ToolbarView( containment, m_corona, m_contextWidget );
352
    m_contextToolbarView->setFrameShape( QFrame::NoFrame );
353
    m_contextView->showHome();
354
    PERF_LOG( "ContexView created" )
355
356
    bool hide = AmarokConfig::hideContextView();
357
    hideContextView( hide );
358
}
359
360
void
361
MainWindow::slotShrinkBrowsers( int index )
362
{
363
    DEBUG_BLOCK
364
365
    // Because QSplitter sucks and will not recompute sizes if a pane is shrunk and not hidden.
366
   /* if( index == -1 )
367
    {
368
        m_splitterState = m_splitter->saveState();
369
370
        QList<int> sizes;
371
        sizes << m_splitter->sizes()[1] + m_splitter->sizes()[0]  // context view
372
              << m_splitter->sizes()[2]; // playlist
373
        m_splitter->setSizes( sizes );
374
    }
375
    else
376
    {
377
        m_splitter->restoreState( m_splitterState );
378
    }*/
379
}
380
381
void
382
MainWindow::showBrowser( const QString &name )
383
{
384
    const int index = m_browserNames.indexOf( name );
385
    showBrowser( index );
386
}
387
388
void
389
MainWindow::showBrowser( const int index )
390
{
391
    //if( index >= 0 && index != m_browsers->currentIndex() )
392
    //    m_browsers->showWidget( index );
393
}
394
395
void
396
MainWindow::keyPressEvent( QKeyEvent *e )
397
{
398
    if( !( e->modifiers() & Qt::ControlModifier ) )
399
        return KMainWindow::keyPressEvent( e );
400
401
    /*int n = -1;
402
    switch( e->key() )
403
    {
404
        case Qt::Key_0: n = 0; break;
405
        case Qt::Key_1: n = 1; break;
406
        case Qt::Key_2: n = 2; break;
407
        case Qt::Key_3: n = 3; break;
408
        case Qt::Key_4: n = 4; break;
409
        default:
410
            return KMainWindow::keyPressEvent( e );
411
    }
412
    if( n == 0 && m_browsers->currentIndex() >= 0 )
413
        m_browsers->showWidget( m_browsers->currentIndex() );
414
    else if( n > 0 )
415
        showBrowser( n - 1 ); // map from human to computer counting*/
416
}
417
418
void
419
MainWindow::closeEvent( QCloseEvent *e )
420
{
421
    DEBUG_BLOCK
422
423
#ifdef Q_WS_MAC
424
425
    Q_UNUSED( e );
426
    hide();
427
428
#else
429
430
    //KDE policy states we should hide to tray and not quit() when the
431
    //close window button is pushed for the main widget
432
433
    if( AmarokConfig::showTrayIcon() && e->spontaneous() && !kapp->sessionSaving() )
434
    {
435
        KMessageBox::information( this,
436
                i18n( "<qt>Closing the main window will keep Amarok running in the System Tray. "
437
                      "Use <B>Quit</B> from the menu, or the Amarok tray icon to exit the application.</qt>" ),
438
                i18n( "Docking in System Tray" ), "hideOnCloseInfo" );
439
440
        hide();
441
        e->ignore();
442
        return;
443
    }
444
445
    e->accept();
446
    kapp->quit();
447
448
#endif
449
}
450
451
QSize
452
MainWindow::sizeHint() const
453
{
454
    return QApplication::desktop()->screenGeometry( (QWidget*)this ).size() / 1.5;
455
}
456
457
void
458
MainWindow::exportPlaylist() const //SLOT
459
{
460
    DEBUG_BLOCK
461
462
    KFileDialog fileDialog( KUrl("kfiledialog:///amarok-playlist-export"), QString(), 0 );
463
    QCheckBox *saveRelativeCheck = new QCheckBox( i18n("Use relative path for &saving") );
464
465
    fileDialog.fileWidget()->setCustomWidget( saveRelativeCheck );
466
    fileDialog.setOperationMode( KFileDialog::Saving );
467
    fileDialog.setMode( KFile::File );
468
    fileDialog.setCaption( i18n("Save As") );
469
470
    fileDialog.exec();
471
472
    QString playlistName = fileDialog.selectedFile();
473
474
    if( !playlistName.isEmpty() )
475
    {
476
        AmarokConfig::setRelativePlaylist( saveRelativeCheck->isChecked() );
477
        The::playlistModel()->exportPlaylist( playlistName );
478
    }
479
}
480
481
void
482
MainWindow::savePlaylist() const
483
{
484
    The::playlistModel()->savePlaylist();
485
}
486
487
void
488
MainWindow::slotShowCoverManager() const //SLOT
489
{
490
    CoverManager::showOnce();
491
}
492
493
void
494
MainWindow::slotPlayMedia() //SLOT
495
{
496
    // Request location and immediately start playback
497
    slotAddLocation( true );
498
}
499
500
void
501
MainWindow::slotAddLocation( bool directPlay ) //SLOT
502
{
503
    // open a file selector to add media to the playlist
504
    KUrl::List files;
505
    KFileDialog dlg( KUrl(QString()), QString("*.*|"), this );
506
    dlg.setCaption( directPlay ? i18n("Play Media (Files or URLs)") : i18n("Add Media (Files or URLs)") );
507
    dlg.setMode( KFile::Files | KFile::Directory );
508
    dlg.exec();
509
    files = dlg.selectedUrls();
510
511
    if( files.isEmpty() )
512
        return;
513
514
    The::playlistController()->insertOptioned( files , Playlist::AppendAndPlayImmediately );
515
}
516
517
void
518
MainWindow::slotAddStream() //SLOT
519
{
520
    bool ok;
521
    QString url = KInputDialog::getText( i18n("Add Stream"), i18n("Enter Stream URL:"), QString(), &ok, this );
522
523
    if( !ok )
524
        return;
525
526
    Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( KUrl( url ) );
527
528
    The::playlistController()->insertOptioned( track, Playlist::Append );
529
}
530
531
void
532
MainWindow::playAudioCD() //SLOT
533
{
534
    KUrl::List urls;
535
    if( The::engineController()->getAudioCDContents(QString(), urls) )
536
    {
537
        Meta::TrackList tracks = CollectionManager::instance()->tracksForUrls( urls );
538
        if( !tracks.isEmpty() )
539
            The::playlistController()->insertOptioned( tracks, Playlist::Replace );
540
    }
541
    else
542
    { // Default behaviour
543
        showBrowser( "FileBrowser" );
544
    }
545
}
546
547
void
548
MainWindow::showScriptSelector() //SLOT
549
{
550
    ScriptManager::instance()->show();
551
    ScriptManager::instance()->raise();
552
}
553
554
/**
555
 * "Toggle Main Window" global shortcut connects to this slot
556
 */
557
void
558
MainWindow::showHide() //SLOT
559
{
560
    setVisible( !isVisible() );
561
}
562
563
void
564
MainWindow::loveTrack()
565
{
566
    Meta::TrackPtr cTrack = The::engineController()->currentTrack();
567
    if( cTrack )
568
        emit loveTrack( cTrack );
569
}
570
571
void
572
MainWindow::activate()
573
{
574
#ifdef Q_WS_X11
575
    const KWindowInfo info = KWindowSystem::windowInfo( winId(), 0, 0 );
576
577
    if( KWindowSystem::activeWindow() != winId())
578
        setVisible( true );
579
    else if( !info.isMinimized() )
580
        setVisible( true );
581
    if( !isHidden() )
582
        KWindowSystem::activateWindow( winId() );
583
#else
584
    setVisible( true );
585
#endif
586
}
587
588
bool
589
MainWindow::isReallyShown() const
590
{
591
#ifdef Q_WS_X11
592
    const KWindowInfo info = KWindowSystem::windowInfo( winId(), 0, 0 );
593
    return !isHidden() && !info.isMinimized() && info.isOnDesktop( KWindowSystem::currentDesktop() );
594
#else
595
    return !isHidden();
596
#endif
597
}
598
599
void
600
MainWindow::createActions()
601
{
602
    KActionCollection* const ac = Amarok::actionCollection();
603
    const EngineController* const ec = The::engineController();
604
    const Playlist::Actions* const pa = The::playlistActions();
605
    const Playlist::Controller* const pc = The::playlistController();
606
607
    KStandardAction::keyBindings( kapp, SLOT( slotConfigShortcuts() ), ac );
608
    KStandardAction::preferences( kapp, SLOT( slotConfigAmarok() ), ac );
609
    ac->action(KStandardAction::name(KStandardAction::KeyBindings))->setIcon( KIcon( "configure-shortcuts-amarok" ) );
610
    ac->action(KStandardAction::name(KStandardAction::Preferences))->setIcon( KIcon( "configure-amarok" ) );
611
    ac->action(KStandardAction::name(KStandardAction::Preferences))->setMenuRole(QAction::PreferencesRole); // Define OS X Prefs menu here, removes need for ifdef later
612
613
    KStandardAction::quit( kapp, SLOT( quit() ), ac );
614
615
    KAction *action = new KAction( KIcon( "folder-amarok" ), i18n("&Add Media..."), this );
616
    ac->addAction( "playlist_add", action );
617
    connect( action, SIGNAL( triggered(bool) ), this, SLOT( slotAddLocation() ) );
618
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_A ) );
619
620
    action = new KAction( KIcon( "edit-clear-list-amarok" ), i18nc( "clear playlist", "&Clear Playlist" ), this );
621
    connect( action, SIGNAL( triggered( bool ) ), pc, SLOT( clear() ) );
622
    ac->addAction( "playlist_clear", action );
623
624
    action = new KAction( KIcon( "folder-amarok" ), i18n("&Add Stream..."), this );
625
    connect( action, SIGNAL( triggered(bool) ), this, SLOT( slotAddStream() ) );
626
    ac->addAction( "stream_add", action );
627
628
    action = new KAction( KIcon( "document-export-amarok" ), i18n("&Export Playlist As..."), this );
629
    connect( action, SIGNAL( triggered(bool) ), this, SLOT( exportPlaylist() ) );
630
    ac->addAction( "playlist_export", action );
631
632
    action = new KAction( KIcon( "document-save-amarok" ), i18n("&Save Playlist"), this );
633
    connect( action, SIGNAL( triggered(bool) ), this, SLOT( savePlaylist() ) );
634
    ac->addAction( "playlist_save", action );
635
636
    action = new KAction( KIcon( "media-album-cover-manager-amarok" ), i18n( "Cover Manager" ), this );
637
    connect( action, SIGNAL( triggered(bool) ), SLOT( slotShowCoverManager() ) );
638
    ac->addAction( "cover_manager", action );
639
640
641
//     KAction *update_podcasts = new KAction( this );
642
//     update_podcasts->setText( i18n( "Update Podcasts" ) );
643
//     //update_podcasts->setIcon( KIcon("view-refresh-amarok") );
644
//     ac->addAction( "podcasts_update", update_podcasts );
645
//     connect(update_podcasts, SIGNAL(triggered(bool)),
646
//             The::podcastCollection(), SLOT(slotUpdateAll()));
647
648
    action = new KAction( KIcon("folder-amarok"), i18n("Play Media..."), this );
649
    connect(action, SIGNAL(triggered(bool)), SLOT(slotPlayMedia()));
650
    ac->addAction( "playlist_playmedia", action );
651
652
#if 0
653
    // Audio CD is not currently supported
654
    action = new KAction( KIcon( "media-optical-audio-amarok" ), i18n("Play Audio CD"), this );
655
    connect(action, SIGNAL(triggered(bool)), SLOT(playAudioCD()));
656
    ac->addAction( "play_audiocd", action );
657
#endif
658
659
    action = new KAction( KIcon("preferences-plugin-script-amarok"), i18n("Script Manager"), this );
660
    connect(action, SIGNAL(triggered(bool)), SLOT(showScriptSelector()));
661
    ac->addAction( "script_manager", action );
662
663
    action = new KAction( KIcon( "media-seek-forward-amarok" ), i18n("&Seek Forward"), this );
664
    ac->addAction( "seek_forward", action );
665
    action->setShortcut( Qt::Key_Right );
666
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Plus ) );
667
    connect(action, SIGNAL(triggered(bool)), ec, SLOT(seekForward()));
668
669
    action = new KAction( KIcon( "media-seek-backward-amarok" ), i18n("&Seek Backward"), this );
670
    ac->addAction( "seek_backward", action );
671
    action->setShortcut( Qt::Key_Left );
672
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Minus ) );
673
    connect(action, SIGNAL(triggered(bool)), ec, SLOT(seekBackward()));
674
675
    PERF_LOG( "MainWindow::createActions 6" )
676
    action = new KAction( KIcon("collection-refresh-amarok"), i18n( "Update Collection" ), this );
677
    connect(action, SIGNAL(triggered(bool)), CollectionManager::instance(), SLOT(checkCollectionChanges()));
678
    ac->addAction( "update_collection", action );
679
680
    action = new KAction( this );
681
    ac->addAction( "prev", action );
682
    action->setIcon( KIcon("media-skip-backward-amarok") );
683
    action->setText( i18n( "Previous Track" ) );
684
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Z ) );
685
    connect( action, SIGNAL(triggered(bool)), pa, SLOT( back() ) );
686
687
    action = new KAction( this );
688
    ac->addAction( "next", action );
689
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_B ) );
690
    action->setIcon( KIcon("media-skip-forward-amarok") );
691
    action->setText( i18n( "Next Track" ) );
692
    connect( action, SIGNAL(triggered(bool)), pa, SLOT( next() ) );
693
694
    action = new KAction( i18n( "Increase Volume" ), this );
695
    ac->addAction( "increaseVolume", action );
696
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Plus ) );
697
    action->setShortcut( Qt::Key_Plus );
698
    connect( action, SIGNAL( triggered() ), ec, SLOT( increaseVolume() ) );
699
700
    action = new KAction( i18n( "Decrease Volume" ), this );
701
    ac->addAction( "decreaseVolume", action );
702
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Minus ) );
703
    action->setShortcut( Qt::Key_Minus );
704
    connect( action, SIGNAL( triggered() ), ec, SLOT( decreaseVolume() ) );
705
706
    action = new KAction( i18n( "Toggle Main Window" ), this );
707
    ac->addAction( "toggleMainWindow", action );
708
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_P ) );
709
    connect( action, SIGNAL( triggered() ), SLOT( showHide() ) );
710
711
    action = new KAction( i18n( "Show On Screen Display" ), this );
712
    ac->addAction( "showOsd", action );
713
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_O ) );
714
    connect( action, SIGNAL( triggered() ), Amarok::OSD::instance(), SLOT( forceToggleOSD() ) );
715
716
    action = new KAction( i18n( "Mute Volume" ), this );
717
    ac->addAction( "mute", action );
718
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_M ) );
719
    connect( action, SIGNAL( triggered() ), ec, SLOT( toggleMute() ) );
720
721
    action = new KAction( i18n( "Love Current Track" ), this );
722
    ac->addAction( "loveTrack", action );
723
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_L ) );
724
    connect( action, SIGNAL(triggered()), SLOT(loveTrack()) );
725
726
    action = new KAction( i18n( "Rate Current Track: 1" ), this );
727
    ac->addAction( "rate1", action );
728
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_1 ) );
729
    connect( action, SIGNAL( triggered() ), SLOT( setRating1() ) );
730
731
    action = new KAction( i18n( "Rate Current Track: 2" ), this );
732
    ac->addAction( "rate2", action );
733
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_2 ) );
734
    connect( action, SIGNAL( triggered() ), SLOT( setRating2() ) );
735
736
    action = new KAction( i18n( "Rate Current Track: 3" ), this );
737
    ac->addAction( "rate3", action );
738
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_3 ) );
739
    connect( action, SIGNAL( triggered() ), SLOT( setRating3() ) );
740
741
    action = new KAction( i18n( "Rate Current Track: 4" ), this );
742
    ac->addAction( "rate4", action );
743
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_4 ) );
744
    connect( action, SIGNAL( triggered() ), SLOT( setRating4() ) );
745
746
    action = new KAction( i18n( "Rate Current Track: 5" ), this );
747
    ac->addAction( "rate5", action );
748
    action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_5 ) );
749
    connect( action, SIGNAL( triggered() ), SLOT( setRating5() ) );
750
751
    action = KStandardAction::redo(pc, SLOT(redo()), this);
752
    ac->addAction( "playlist_redo", action );
753
    action->setEnabled(false);
754
    action->setIcon( KIcon( "edit-redo-amarok" ) );
755
    connect(pc, SIGNAL(canRedoChanged(bool)), action, SLOT(setEnabled(bool)));
756
757
    action = KStandardAction::undo(pc, SLOT(undo()), this);
758
    ac->addAction( "playlist_undo", action );
759
    action->setEnabled(false);
760
    action->setIcon( KIcon( "edit-undo-amarok" ) );
761
    connect(pc, SIGNAL(canUndoChanged(bool)), action, SLOT(setEnabled(bool)));
762
763
    PERF_LOG( "MainWindow::createActions 8" )
764
    new Amarok::MenuAction( ac, this );
765
    new Amarok::StopAction( ac, this );
766
    new Amarok::PlayPauseAction( ac, this );
767
    new Amarok::RepeatAction( ac, this );
768
    new Amarok::RandomAction( ac, this );
769
    new Amarok::FavorAction( ac, this );
770
    new Amarok::ReplayGainModeAction( ac, this );
771
772
    ac->addAssociatedWidget( this );
773
    foreach (QAction* action, ac->actions())
774
        action->setShortcutContext(Qt::WindowShortcut);
775
}
776
777
void
778
MainWindow::setRating( int n )
779
{
780
    n *= 2;
781
782
    Meta::TrackPtr track = The::engineController()->currentTrack();
783
    if( track )
784
    {
785
        // if we're setting an identical rating then we really must
786
        // want to set the half-star below rating
787
        if( track->rating() == n )
788
            n -= 1;
789
790
        track->setRating( n );
791
        Amarok::OSD::instance()->OSDWidget::ratingChanged( track->rating() );
792
    }
793
}
794
795
void
796
MainWindow::createMenus()
797
{
798
    //BEGIN Actions menu
799
    KMenu *actionsMenu;
800
#ifdef Q_WS_MAC
801
    m_menubar = new QMenuBar(0);  // Fixes menubar in OS X
802
    actionsMenu = new KMenu( m_menubar );
803
    // Add these functions to the dock icon menu in OS X
804
    //extern void qt_mac_set_dock_menu(QMenu *);
805
    //qt_mac_set_dock_menu(actionsMenu);
806
    // Change to avoid duplicate menu titles in OS X
807
    actionsMenu->setTitle( i18n("&Music") );
808
#else
809
    m_menubar = menuBar();
810
    actionsMenu = new KMenu( m_menubar );
811
    actionsMenu->setTitle( i18n("&Amarok") );
812
#endif
813
    actionsMenu->addAction( Amarok::actionCollection()->action("playlist_playmedia") );
814
    actionsMenu->addSeparator();
815
    actionsMenu->addAction( Amarok::actionCollection()->action("prev") );
816
    actionsMenu->addAction( Amarok::actionCollection()->action("play_pause") );
817
    actionsMenu->addAction( Amarok::actionCollection()->action("stop") );
818
    actionsMenu->addAction( Amarok::actionCollection()->action("next") );
819
820
821
#ifndef Q_WS_MAC    // Avoid duplicate "Quit" in OS X dock menu
822
    actionsMenu->addSeparator();
823
    actionsMenu->addAction( Amarok::actionCollection()->action(KStandardAction::name(KStandardAction::Quit)) );
824
#endif
825
    //END Actions menu
826
827
    //BEGIN Playlist menu
828
    KMenu *playlistMenu = new KMenu( m_menubar );
829
    playlistMenu->setTitle( i18n("&Playlist") );
830
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_add") );
831
    playlistMenu->addAction( Amarok::actionCollection()->action("stream_add") );
832
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_save") );
833
    playlistMenu->addSeparator();
834
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_undo") );
835
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_redo") );
836
    playlistMenu->addSeparator();
837
    playlistMenu->addAction( Amarok::actionCollection()->action("playlist_clear") );
838
839
    QAction *repeat = Amarok::actionCollection()->action("repeat");
840
    playlistMenu->addAction( repeat );
841
842
    KSelectAction *random = static_cast<KSelectAction*>( Amarok::actionCollection()->action("random_mode") );
843
    playlistMenu->addAction( random );
844
    random->menu()->addSeparator();
845
    random->menu()->addAction( Amarok::actionCollection()->action("favor_tracks") );
846
847
    playlistMenu->addSeparator();
848
    //END Playlist menu
849
850
    //BEGIN Tools menu
851
    m_toolsMenu = new KMenu( m_menubar );
852
    m_toolsMenu->setTitle( i18n("&Tools") );
853
    m_toolsMenu->addAction( Amarok::actionCollection()->action("cover_manager") );
854
//FIXME: Reenable when ported//working
855
//     m_toolsMenu->addAction( Amarok::actionCollection()->action("queue_manager") );
856
    m_toolsMenu->addAction( Amarok::actionCollection()->action("script_manager") );
857
    m_toolsMenu->addSeparator();
858
    m_toolsMenu->addAction( Amarok::actionCollection()->action("update_collection") );
859
    //END Tools menu
860
861
    //BEGIN Settings menu
862
    m_settingsMenu = new KMenu( m_menubar );
863
    m_settingsMenu->setTitle( i18n("&Settings") );
864
    //TODO use KStandardAction or KXmlGuiWindow
865
866
    // the phonon-coreaudio  backend has major issues with either the VolumeFaderEffect itself
867
    // or with it in the pipeline. track playback stops every ~3-4 tracks, and on tracks >5min it
868
    // stops at about 5:40. while we get this resolved upstream, don't make playing amarok such on osx.
869
    // so we disable replaygain on osx
870
#ifndef Q_WS_MAC
871
    m_settingsMenu->addAction( Amarok::actionCollection()->action("replay_gain_mode") );
872
    m_settingsMenu->addSeparator();
873
#endif
874
875
    m_settingsMenu->addAction( Amarok::actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings)) );
876
    m_settingsMenu->addAction( Amarok::actionCollection()->action(KStandardAction::name(KStandardAction::Preferences)) );
877
    //END Settings menu
878
879
    m_menubar->addMenu( actionsMenu );
880
    m_menubar->addMenu( playlistMenu );
881
    m_menubar->addMenu( m_toolsMenu );
882
    m_menubar->addMenu( m_settingsMenu );
883
    m_menubar->addMenu( Amarok::Menu::helpMenu() );
884
}
885
886
void
887
MainWindow::paletteChange(const QPalette & oldPalette)
888
{
889
    Q_UNUSED( oldPalette )
890
891
    KPixmapCache cache( "Amarok-pixmaps" );
892
    cache.discard();
893
    The::paletteHandler()->setPalette( palette() );
894
}
895
896
QSize
897
MainWindow::backgroundSize()
898
{
899
    QPoint topLeft = mapToGlobal( QPoint( 0, 0 ) );
900
    QPoint bottomRight1 = mapToGlobal( QPoint( width(), height() ) );
901
902
    return QSize( bottomRight1.x() - topLeft.x() + 1, bottomRight1.y() - topLeft.y() );
903
}
904
905
int
906
MainWindow::contextXOffset()
907
{
908
    QPoint topLeft1 = mapToGlobal( m_controlBar->pos() );
909
    QPoint topLeft2 = mapToGlobal( m_contextWidget->pos() );
910
911
    return topLeft2.x() - topLeft1.x();
912
}
913
914
void MainWindow::resizeEvent( QResizeEvent * event )
915
{
916
    QWidget::resizeEvent( event );
917
    m_controlBar->reRender();
918
}
919
920
QPoint MainWindow::globalBackgroundOffset()
921
{
922
    return menuBar()->mapToGlobal( QPoint( 0, 0 ) );
923
}
924
925
QRect MainWindow::contextRectGlobal()
926
{
927
    //debug() << "pos of context vidget within main window is: " << m_contextWidget->pos();
928
    QPoint contextPos = mapToGlobal( m_contextWidget->pos() );
929
    return QRect( contextPos.x(), contextPos.y(), m_contextWidget->width(), m_contextWidget->height() );
930
}
931
932
void MainWindow::engineStateChanged( Phonon::State state, Phonon::State oldState )
933
{
934
    Q_UNUSED( oldState )
935
    DEBUG_BLOCK
936
937
    debug() << "Phonon state: " << state;
938
939
    Meta::TrackPtr track = The::engineController()->currentTrack();
940
    //track is 0 if the engien state is Empty. we check that in the switch
941
    switch( state )
942
    {
943
    case Phonon::StoppedState:
944
        setPlainCaption( i18n( AMAROK_CAPTION ) );
945
        break;
946
947
    case Phonon::PlayingState:
948
        if( track ) {
949
            unsubscribeFrom( m_currentTrack );
950
            m_currentTrack = track;
951
            subscribeTo( track );
952
            metadataChanged( track );
953
        }
954
        else
955
            warning() << "currentTrack is 0. Can't subscribe to it!";
956
        break;
957
958
    case Phonon::PausedState:
959
        setPlainCaption( i18n( "Paused  ::  %1", QString( AMAROK_CAPTION ) ) );
960
        break;
961
962
    case Phonon::LoadingState:
963
    case Phonon::ErrorState:
964
    case Phonon::BufferingState:
965
        break;
966
    }
967
}
968
969
void MainWindow::metadataChanged( Meta::TrackPtr track )
970
{
971
    setPlainCaption( i18n( "%1 - %2  ::  %3", track->artist() ? track->artist()->prettyName() : i18n( "Unknown" ), track->prettyName(), AMAROK_CAPTION ) );
972
}
973
974
CollectionWidget * MainWindow::collectionBrowser()
975
{
976
    return m_collectionBrowser;
977
}
978
979
QString MainWindow::activeBrowserName()
980
{
981
    if ( m_browsers->list()->activeCategory() )
982
        return m_browsers->list()->activeCategory()->name();
983
    else
984
        return QString();
985
}
986
987
PlaylistBrowserNS::PlaylistBrowser * MainWindow::playlistBrowser()
988
{
989
    return m_playlistBrowser;
990
}
991
992
void MainWindow::hideContextView( bool hide )
993
{
994
    DEBUG_BLOCK
995
    if ( hide )
996
        m_contextWidget->hide();
997
    else
998
        m_contextWidget->show();
999
}
1000
1001
void MainWindow::setLayoutLocked( bool locked )
1002
{
1003
    DEBUG_BLOCK
1004
    if ( locked )
1005
    {
1006
        debug() << "locked!";
1007
        const QFlags<QDockWidget::DockWidgetFeature> features = QDockWidget::NoDockWidgetFeatures;
1008
1009
        m_browsersDock->setFeatures( features );
1010
        m_browsersDock->setTitleBarWidget( m_browserDummyTitleBarWidget );
1011
1012
        m_contextDock->setFeatures( features );
1013
        m_contextDock->setTitleBarWidget( m_contextDummyTitleBarWidget );
1014
1015
        m_playlistDock->setFeatures( features );
1016
        m_playlistDock->setTitleBarWidget( m_playlistDummyTitleBarWidget );
1017
1018
        m_controlBar->setFloatable( false );
1019
        m_controlBar->setMovable( false );
1020
1021
    }
1022
    else
1023
    {
1024
        debug() << "unlocked!";
1025
        const QFlags<QDockWidget::DockWidgetFeature> features = QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable;
1026
        
1027
        m_browsersDock->setFeatures( features );
1028
        m_contextDock->setFeatures( features );
1029
        m_playlistDock->setFeatures( features );
1030
1031
1032
        m_browsersDock->setTitleBarWidget( 0 );
1033
        m_contextDock->setTitleBarWidget( 0 );
1034
        m_playlistDock->setTitleBarWidget( 0 );
1035
1036
        m_controlBar->setFloatable( true );
1037
        m_controlBar->setMovable( true );
1038
1039
    }
1040
1041
    m_layoutLocked = locked;
1042
}
1043
1044
bool MainWindow::isLayoutLocked()
1045
{
1046
    return m_layoutLocked;
1047
}
1048
1049
void MainWindow::restoreLayout()
1050
{
1051
1052
    QFile file( Amarok::saveLocation() + "layout" );
1053
1054
    bool loadDefault = true;
1055
    if ( file.open( QIODevice::ReadOnly ) )
1056
    {
1057
1058
        QByteArray layout = file.readAll();
1059
        file.close();
1060
1061
        loadDefault = !restoreState( layout, LAYOUT_VERSION );
1062
    }
1063
1064
    if ( loadDefault )
1065
    {
1066
1067
        const KUrl url( KStandardDirs::locate( "data", "amarok/data/" ) );
1068
        QFile defaultFile( url.path() + "DefaultDockLayout" );
1069
1070
        if ( defaultFile.open( QIODevice::ReadOnly ) )
1071
        {
1072
            QByteArray defaultLayout = defaultFile.readAll();
1073
            defaultFile.close();
1074
1075
            restoreState( defaultLayout, LAYOUT_VERSION );
1076
        }
1077
    }
1078
}
1079
1080
1081
1082
namespace The {
1083
    MainWindow* mainWindow() { return MainWindow::s_instance; }
1084
}
1085
1086
1087
1088
1089
#include "MainWindow.moc"
1090