1  /****************************************************************************************
2  * Copyright (c) 2002-2013 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 "App.h"
27 #include "ActionClasses.h"
28 #include "EngineController.h" //for actions in ctor
29 #include "KNotificationBackend.h"
30 #include "PaletteHandler.h"
31 #include "PluginManager.h"
32 #include "SvgHandler.h"
33 #include "amarokconfig.h"
34 #include "aboutdialog/ExtendedAboutDialog.h"
35 #include "aboutdialog/OcsData.h"
36 #include "amarokurls/AmarokUrlHandler.h"
37 #include "amarokurls/BookmarkManager.h"
38 #include "browsers/BrowserDock.h"
39 #include "browsers/collectionbrowser/CollectionWidget.h"
40 #include "browsers/filebrowser/FileBrowser.h"
41 #include "browsers/playlistbrowser/PlaylistBrowser.h"
42 #include "browsers/playlistbrowser/PodcastCategory.h"
43 #include "browsers/servicebrowser/ServiceBrowser.h"
44 #include "context/ContextDock.h"
45 #include "core/meta/Statistics.h"
46 #include "core/support/Amarok.h"
47 #include "core/support/Components.h"
48 #include "core/support/Debug.h"
49 #include "core-impl/collections/support/CollectionManager.h"
50 #include "covermanager/CoverManager.h" // for actions
51 #include "dialogs/DiagnosticDialog.h"
52 #include "dialogs/EqualizerDialog.h"
53 #include "moodbar/MoodbarManager.h"
54 #include "network/NetworkAccessManagerProxy.h"
55 #ifdef DEBUG_BUILD_TYPE
56 #include "network/NetworkAccessViewer.h"
57 #endif // DEBUG_BUILD_TYPE
58 #include "playlist/PlaylistActions.h"
59 #include "playlist/PlaylistController.h"
60 #include "playlist/PlaylistDock.h"
61 #include "playlist/PlaylistModelStack.h"
62 #include "playlist/ProgressiveSearchWidget.h"
63 #include "playlist/layouts/LayoutConfigAction.h"
64 #include "playlistmanager/PlaylistManager.h"
65 #include "playlistmanager/file/PlaylistFileProvider.h"
66 #include "services/scriptable/ScriptableService.h"
67 #include "statsyncing/Controller.h"
68 #include "toolbar/MainToolbar.h"
69 #include "toolbar/SlimToolbar.h"
70 #include "widgets/Osd.h"
71 
72 #include <QAction>          //m_actionCollection
73 #include <QApplication>     //qApp
74 #include <QCheckBox>
75 #include <QClipboard>
76 #include <QDesktopServices>
77 #include <QDesktopWidget>
78 #include <QDockWidget>
79 #include <QFileDialog>      //openPlaylist()
80 #include <QInputDialog>     //slotAddStream()
81 #include <QList>
82 #include <QMenu>
83 #include <QMenuBar>
84 #include <QStandardPaths>
85 #include <QStyle>
86 #include <QVBoxLayout>
87 
88 #include <KActionCollection>
89 #include <KBugReport>
90 #include <KFileWidget>
91 #include <KFormat>
92 #include <KGlobalAccel>
93 #include <KLocalizedString>
94 #include <KMessageBox>
95 #include <KStandardAction>
96 #include <KWindowSystem>
97 
98 #include <iostream>
99 
100 #ifdef Q_WS_X11
101 #include <fixx11h.h>
102 #include <KConfigGroup>
103 #endif
104 
105 #ifdef Q_WS_MAC
106 #include "mac/GrowlInterface.h"
107 #ifdef HAVE_NOTIFICATION_CENTER
108 #include "mac/MacSystemNotify.h"
109 #endif
110 #endif
111 
112 #define AMAROK_CAPTION I18N_NOOP( "Amarok" )
113 
114 extern OcsData ocsData;
115 
116 namespace The {
mainWindow()117     MainWindow* mainWindow() { return pApp->mainWindow(); }
118 }
119 
MainWindow()120 MainWindow::MainWindow()
121     : KMainWindow( nullptr )
122     , m_showMenuBar( nullptr )
123     , m_lastBrowser( 0 )
124     , m_waitingForCd( false )
125 {
126     DEBUG_BLOCK
127 
128     setObjectName( "MainWindow" );
129 
130 #ifdef Q_WS_MAC
131     (void)new GrowlInterface( qApp->applicationName() );
132 #ifdef HAVE_NOTIFICATION_CENTER
133     (void)new OSXNotify( qApp->applicationName() );
134 #endif
135 #endif
136 
137     PERF_LOG( "Instantiate Collection Manager" )
138     CollectionManager::instance();
139     PERF_LOG( "Started Collection Manager instance" )
140 
141     /* The PluginManager needs to be loaded before the playlist model
142     * (which gets started by "statusBar::connectPlaylist" below so that it can handle any
143     * tracks in the saved playlist that are associated with services. Eg, if
144     * the playlist has a Magnatune track in it when Amarok is closed, then the
145     * Magnatune service needs to be initialized before the playlist is loaded
146     * here. */
147     PERF_LOG( "Instantiate Plugin Manager" )
148     The::pluginManager();
149     PERF_LOG( "Started Plugin Manager instance" )
150 
151     createActions();
152     PERF_LOG( "Created actions" )
153 
154     The::paletteHandler()->setPalette( palette() );
155     setPlainCaption( i18n( AMAROK_CAPTION ) );
156 
157     init();  // We could as well move the code from init() here, but meh.. getting a tad long
158 
159     //restore active category ( as well as filters and levels and whatnot.. )
160     const QString path = Amarok::config().readEntry( "Browser Path", QString() );
161     if( !path.isEmpty() )
162         m_browserDock->list()->navigate( path );
163 
164     setAutoSaveSettings();
165 
166     m_showMenuBar->setChecked(!menuBar()->isHidden());  // workaround for bug #171080
167 
168     EngineController *engine = The::engineController();
169     connect( engine, &EngineController::stopped, this, &MainWindow::slotStopped );
170     connect( engine, &EngineController::paused, this, &MainWindow::slotPaused );
171     connect( engine, &EngineController::trackPlaying, this, &MainWindow::slotNewTrackPlaying );
172     connect( engine, &EngineController::trackMetadataChanged, this, &MainWindow::slotMetadataChanged );
173 }
174 
~MainWindow()175 MainWindow::~MainWindow()
176 {
177     DEBUG_BLOCK
178 
179     //save currently active category
180     Amarok::config().writeEntry( "Browser Path", m_browserDock->list()->path() );
181 
182 #ifdef DEBUG_BUILD_TYPE
183     delete m_networkViewer.data();
184 #endif // DEBUG_BUILD_TYPE
185     delete The::svgHandler();
186     delete The::paletteHandler();
187 }
188 
189 
190 ///////// public interface
191 
192 /**
193  * This function will initialize the main window.
194  */
195 void
init()196 MainWindow::init()
197 {
198     layout()->setContentsMargins( 0, 0, 0, 0 );
199     layout()->setSpacing( 0 );
200 
201     //create main toolbar
202     m_mainToolbar = new MainToolbar( this );
203     m_mainToolbar.data()->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea );
204     m_mainToolbar.data()->setMovable ( true );
205     addToolBar( Qt::TopToolBarArea, m_mainToolbar.data() );
206 
207     //create slim toolbar
208     m_slimToolbar = new SlimToolbar( this );
209     m_slimToolbar.data()->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea );
210     m_slimToolbar.data()->setMovable ( true );
211     addToolBar( Qt::TopToolBarArea, m_slimToolbar.data() );
212     m_slimToolbar->hide();
213 
214     //BEGIN Creating Widgets
215     PERF_LOG( "Create sidebar" )
216     m_browserDock = new BrowserDock( this );
217     m_browserDock->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored );
218 
219     m_browserDock->installEventFilter( this );
220     PERF_LOG( "Sidebar created" )
221 
222     PERF_LOG( "Create Playlist" )
223     m_playlistDock = new Playlist::Dock( this );
224     m_playlistDock->installEventFilter( this );
225     //HACK, need to connect after because of order in MainWindow()
226     connect( Amarok::actionCollection()->action( "playlist_edit_queue" ),
227              &QAction::triggered, m_playlistDock.data(), &Playlist::Dock::slotEditQueue );
228     PERF_LOG( "Playlist created" )
229 
230     PERF_LOG( "Creating ContextWidget" )
231     m_contextDock = new ContextDock( this );
232     m_contextDock->installEventFilter( this );
233     PERF_LOG( "ContextScene created" )
234     //END Creating Widgets
235 
236     createMenus();
237 
238     setDockOptions( QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks
239                     | QMainWindow::AnimatedDocks | QMainWindow::VerticalTabs );
240 
241     addDockWidget( Qt::LeftDockWidgetArea, m_browserDock.data() );
242     addDockWidget( Qt::LeftDockWidgetArea, m_contextDock.data(), Qt::Horizontal );
243     addDockWidget( Qt::LeftDockWidgetArea, m_playlistDock.data(), Qt::Horizontal );
244 
245     setLayoutLocked( AmarokConfig::lockLayout() );
246 
247     //<Browsers>
248     {
249         Debug::Block block( "Creating browsers. Please report long start times!" );
250 
251         //TODO: parent these browsers?
252         PERF_LOG( "Creating CollectionWidget" )
253         m_collectionBrowser = new CollectionWidget( "collections", 0 );
254         //TODO: rename "Music Collections
255         m_collectionBrowser->setPrettyName( i18n( "Local Music" ) );
256         m_collectionBrowser->setIcon( QIcon::fromTheme( "drive-harddisk" ) );
257         m_collectionBrowser->setShortDescription( i18n( "Local sources of content" ) );
258         m_browserDock->list()->addCategory( m_collectionBrowser );
259         PERF_LOG( "Created CollectionWidget" )
260 
261         PERF_LOG( "Creating ServiceBrowser" )
262         ServiceBrowser *serviceBrowser = ServiceBrowser::instance();
263         serviceBrowser->setParent( 0 );
264         serviceBrowser->setPrettyName( i18n( "Internet" ) );
265         serviceBrowser->setIcon( QIcon::fromTheme( "internet-services" ) );
266         serviceBrowser->setShortDescription( i18n( "Online sources of content" ) );
267         m_browserDock->list()->addCategory( serviceBrowser );
268         PERF_LOG( "Created ServiceBrowser" )
269 
270         PERF_LOG( "Creating PlaylistBrowser" )
271         m_playlistBrowser = new PlaylistBrowserNS::PlaylistBrowser( "playlists", 0 );
272         m_playlistBrowser->setPrettyName( i18n("Playlists") );
273         m_playlistBrowser->setIcon( QIcon::fromTheme( "view-media-playlist-amarok" ) );
274         m_playlistBrowser->setShortDescription( i18n( "Various types of playlists" ) );
275         m_browserDock->list()->addCategory( m_playlistBrowser );
276         PERF_LOG( "CreatedPlaylsitBrowser" )
277 
278         PERF_LOG( "Creating FileBrowser" )
279         FileBrowser *fileBrowser = new FileBrowser( "files", 0 );
280         fileBrowser->setPrettyName( i18n("Files") );
281         fileBrowser->setIcon( QIcon::fromTheme( "folder-amarok" ) );
282 
283         fileBrowser->setShortDescription( i18n( "Browse local hard drive for content" ) );
284         m_browserDock->list()->addCategory( fileBrowser );
285         PERF_LOG( "Created FileBrowser" )
286 
287         serviceBrowser->setScriptableServiceManager( The::scriptableServiceManager() );
288         PERF_LOG( "ScriptableServiceManager done" )
289 
290         PERF_LOG( "Creating Podcast Category" )
291         m_browserDock->list()->addCategory( The::podcastCategory() );
292         PERF_LOG( "Created Podcast Category" )
293 
294     // If Amarok is started for the first time, set initial dock widget sizes
295         if( !Amarok::config( "MainWindow" ).hasKey( "State" ) )
296             QTimer::singleShot( 0, this, &MainWindow::setDefaultDockSizes );
297 
298         PERF_LOG( "finished MainWindow::init" )
299     }
300     The::amarokUrlHandler(); //Instantiate
301     The::coverFetcher(); //Instantiate
302 
303     // we must filter ourself to get mouseevents on the "splitter" - what is us, but filtered by the layouter
304     installEventFilter( this );
305 }
306 
307 QMenu*
createPopupMenu()308 MainWindow::createPopupMenu()
309 {
310     QMenu* menu = new QMenu( this );
311 
312     // Show/hide menu bar
313     if (!menuBar()->isVisible())
314         menu->addAction( m_showMenuBar );
315 
316     menu->addSeparator();
317 
318     addViewMenuItems(menu);
319 
320     return menu;
321 }
322 
323 void
addViewMenuItems(QMenu * menu)324 MainWindow::addViewMenuItems(QMenu* menu)
325 {
326     menu->setTitle( i18nc("@item:inmenu", "&View" ) );
327 
328     // Layout locking:
329     QAction* lockAction = new QAction( i18n( "Lock Layout" ), this );
330     lockAction->setCheckable( true );
331     lockAction->setChecked( AmarokConfig::lockLayout() );
332     connect( lockAction, &QAction::toggled, this, &MainWindow::setLayoutLocked );
333     menu->addAction( lockAction );
334 
335     menu->addSeparator();
336 
337     // Dock widgets:
338     QList<QDockWidget *> dockwidgets = findChildren<QDockWidget *>();
339 
340     foreach( QDockWidget* dockWidget, dockwidgets )
341     {
342         if( dockWidget->parentWidget() == this )
343             menu->addAction( dockWidget->toggleViewAction() );
344     }
345 
346     menu->addSeparator();
347 
348     // Toolbars:
349     QList<QToolBar *> toolbars = findChildren<QToolBar *>();
350     QActionGroup* toolBarGroup = new QActionGroup( this );
351     toolBarGroup->setExclusive( true );
352 
353     foreach( QToolBar* toolBar, toolbars )
354     {
355         if( toolBar->parentWidget() == this )
356         {
357             QAction* action = toolBar->toggleViewAction();
358             connect( action, &QAction::toggled, toolBar, &QToolBar::setVisible );
359             toolBarGroup->addAction( action );
360             menu->addAction( action );
361         }
362     }
363 
364     menu->addSeparator();
365 
366     QAction *resetAction = new QAction( i18n( "Reset Layout" ), this );
367     connect( resetAction, &QAction::triggered, this, &MainWindow::resetLayout );
368     menu->addAction( resetAction );
369 }
370 
371 void
showBrowser(const QString & name)372 MainWindow::showBrowser( const QString &name )
373 {
374     Q_UNUSED( name );
375     // showBrowser( index ); // FIXME
376 }
377 
378 void
showDock(AmarokDockId dockId)379 MainWindow::showDock( AmarokDockId dockId )
380 {
381     QString name;
382     switch( dockId )
383     {
384         case AmarokDockNavigation:
385             name = m_browserDock->windowTitle();
386             break;
387         case AmarokDockContext:
388             name = m_contextDock->windowTitle();
389             break;
390         case AmarokDockPlaylist:
391             name = m_playlistDock->windowTitle();
392             break;
393     }
394 
395     QList < QTabBar * > tabList = findChildren < QTabBar * > ();
396 
397     foreach( QTabBar *bar, tabList )
398     {
399         for( int i = 0; i < bar->count(); i++ )
400         {
401             if( bar->tabText( i ) == name )
402             {
403                 bar->setCurrentIndex( i );
404                 break;
405             }
406         }
407     }
408 }
409 
410 void
closeEvent(QCloseEvent * e)411 MainWindow::closeEvent( QCloseEvent *e )
412 {
413 #ifdef Q_WS_MAC
414     Q_UNUSED( e );
415     hide();
416 #else
417 
418     //KDE policy states we should hide to tray and not quit() when the
419     //close window button is pushed for the main widget
420     if( AmarokConfig::showTrayIcon() && e->spontaneous() && !qApp->isSavingSession() )
421     {
422         KMessageBox::information( this,
423                 i18n( "<qt>Closing the main window will keep Amarok running in the System Tray. "
424                       "Use <B>Quit</B> from the menu, or the Amarok tray icon to exit the application.</qt>" ),
425                 i18n( "Docking in System Tray" ), "hideOnCloseInfo" );
426 
427         hide();
428         e->ignore();
429         return;
430     }
431 
432     e->accept();
433     pApp->quit();
434 #endif
435 }
436 
437 void
exportPlaylist()438 MainWindow::exportPlaylist() //SLOT
439 {
440     DEBUG_BLOCK
441 
442     QFileDialog fileDialog;
443     fileDialog.restoreState( Amarok::config( "playlist-export-dialog" ).readEntry( "state", QByteArray() ) );
444 
445     // FIXME: Make checkbox visible in dialog
446     QCheckBox *saveRelativeCheck = new QCheckBox( i18n("Use relative path for &saving"), &fileDialog );
447     saveRelativeCheck->setChecked( AmarokConfig::relativePlaylist() );
448 
449     QStringList supportedMimeTypes;
450 
451     supportedMimeTypes << "video/x-ms-asf"; //ASX
452     supportedMimeTypes << "audio/x-mpegurl"; //M3U
453     supportedMimeTypes << "audio/x-scpls"; //PLS
454     supportedMimeTypes << "application/xspf+xml"; //XSPF
455 
456     fileDialog.setMimeTypeFilters( supportedMimeTypes );
457     fileDialog.setAcceptMode( QFileDialog::AcceptSave );
458     fileDialog.setFileMode( QFileDialog::AnyFile );
459     fileDialog.setWindowTitle( i18n("Save As") );
460     fileDialog.setObjectName( "PlaylistExport" );
461 
462     int result = fileDialog.exec();
463     QString playlistPath = fileDialog.selectedFiles().value( 0 );
464     if( result == QDialog::Accepted && !playlistPath.isEmpty() )
465         The::playlist()->exportPlaylist( playlistPath, saveRelativeCheck->isChecked() );
466 
467     Amarok::config( "playlist-export-dialog" ).writeEntry( "state", fileDialog.saveState() );
468 }
469 
470 void
slotShowActiveTrack() const471 MainWindow::slotShowActiveTrack() const
472 {
473     m_playlistDock->showActiveTrack();
474 }
475 
476 void
slotEditTrackInfo() const477 MainWindow::slotEditTrackInfo() const
478 {
479     m_playlistDock->editTrackInfo();
480 }
481 
482 void
slotShowCoverManager()483 MainWindow::slotShowCoverManager() //SLOT
484 {
485     CoverManager::showOnce( QString(), this );
486 }
487 
488 void
slotShowDiagnosticsDialog()489 MainWindow::slotShowDiagnosticsDialog()
490 {
491     DiagnosticDialog *dialog = new DiagnosticDialog( KAboutData::applicationData(), this );
492     dialog->show();
493 }
494 
slotShowBookmarkManager()495 void MainWindow::slotShowBookmarkManager()
496 {
497     BookmarkManager::showOnce( this );
498 }
499 
slotShowEqualizer()500 void MainWindow::slotShowEqualizer()
501 {
502     EqualizerDialog::showOnce( this );
503 }
504 
505 void
slotPlayMedia()506 MainWindow::slotPlayMedia() //SLOT
507 {
508     // Request location and immediately start playback
509     slotAddLocation( true );
510 }
511 
512 void
slotAddLocation(bool directPlay)513 MainWindow::slotAddLocation( bool directPlay ) //SLOT
514 {
515     static QUrl lastDirectory;
516 
517     // open a file selector to add media to the playlist
518     QList<QUrl> files;
519     QFileDialog dlg;
520     dlg.setDirectory( QStandardPaths::writableLocation(QStandardPaths::MusicLocation) );
521 
522     if( !lastDirectory.isEmpty() )
523         dlg.setDirectoryUrl( lastDirectory );
524 
525     dlg.setWindowTitle( directPlay ? i18n("Play Media (Files or URLs)") : i18n("Add Media (Files or URLs)") );
526     dlg.setFileMode( QFileDialog::ExistingFiles );
527     dlg.setObjectName( "PlayMedia" );
528     int accepted = dlg.exec();
529     files = dlg.selectedUrls();
530 
531     lastDirectory = dlg.directoryUrl();
532 
533     if( accepted != QDialog::Accepted || files.isEmpty() )
534         return;
535 
536     Playlist::AddOptions options = directPlay ? Playlist::OnPlayMediaAction
537                                               : Playlist::OnAppendToPlaylistAction;
538     The::playlistController()->insertOptioned( files, options );
539 }
540 
541 void
slotAddStream()542 MainWindow::slotAddStream() //SLOT
543 {
544     bool ok;
545     QString url = QInputDialog::getText( this, i18n( "Add Stream" ), i18n( "Enter Stream URL:" ),
546                                          QLineEdit::Normal, QString(), &ok );
547     if( !ok )
548         return;
549 
550     The::playlistController()->insertOptioned( QUrl( url ),
551             Playlist::OnAppendToPlaylistAction | Playlist::RemotePlaylistsAreStreams );
552 }
553 
554 void
slotFocusPlaylistSearch()555 MainWindow::slotFocusPlaylistSearch()
556 {
557     showDock( AmarokDockPlaylist );  // ensure that the dock is visible if tabbed
558     m_playlistDock->searchWidget()->focusInputLine();
559 }
560 
561 void
slotFocusCollectionSearch()562 MainWindow::slotFocusCollectionSearch()
563 {
564     // ensure collection browser is activated within navigation dock:
565     browserDock()->list()->navigate( QStringLiteral("collections") );
566     showDock( AmarokDockNavigation );  // ensure that the dock is visible if tabbed
567     m_collectionBrowser->focusInputLine();
568 }
569 
570 #ifdef DEBUG_BUILD_TYPE
571 void
showNetworkRequestViewer()572 MainWindow::showNetworkRequestViewer() //SLOT
573 {
574     if( !m_networkViewer )
575     {
576         m_networkViewer = new NetworkAccessViewer( this );
577         The::networkAccessManager()->setNetworkAccessViewer( m_networkViewer.data() );
578 
579     }
580     The::networkAccessManager()->networkAccessViewer()->show();
581 }
582 #endif // DEBUG_BUILD_TYPE
583 
584 /**
585  * "Toggle Main Window" global shortcut connects to this slot
586  */
587 void
showHide()588 MainWindow::showHide() //SLOT
589 {
590     const KWindowInfo info( winId(), 0, 0 );
591     const int currentDesktop = KWindowSystem::currentDesktop();
592 
593     if( !isVisible() )
594     {
595         setVisible( true );
596     }
597     else
598     {
599         if( !isMinimized() )
600         {
601             if( !isActiveWindow() ) // not minimised and without focus
602             {
603                 KWindowSystem::setOnDesktop( winId(), currentDesktop );
604                 KWindowSystem::activateWindow( winId() );
605             }
606             else // Amarok has focus
607             {
608                 setVisible( false );
609             }
610         }
611         else // Amarok is minimised
612         {
613             setWindowState( windowState() & ~Qt::WindowMinimized );
614             KWindowSystem::setOnDesktop( winId(), currentDesktop );
615             KWindowSystem::activateWindow( winId() );
616         }
617     }
618 }
619 
620 void
showNotificationPopup()621 MainWindow::showNotificationPopup() // slot
622 {
623     if( Amarok::KNotificationBackend::instance()->isEnabled()
624             && !Amarok::OSD::instance()->isEnabled() )
625         Amarok::KNotificationBackend::instance()->showCurrentTrack();
626     else
627         Amarok::OSD::instance()->forceToggleOSD();
628 }
629 
630 void
slotFullScreen()631 MainWindow::slotFullScreen() // slot
632 {
633     setWindowState( windowState() ^ Qt::WindowFullScreen );
634 }
635 
636 void
slotLoveTrack()637 MainWindow::slotLoveTrack()
638 {
639     Q_EMIT loveTrack( The::engineController()->currentTrack() );
640 }
641 
642 void
slotBanTrack()643 MainWindow::slotBanTrack()
644 {
645     Q_EMIT banTrack( The::engineController()->currentTrack() );
646 }
647 
648 void
slotShufflePlaylist()649 MainWindow::slotShufflePlaylist()
650 {
651     m_playlistDock->sortWidget()->trimToLevel();
652     The::playlistActions()->shuffle();
653 }
654 
655 void
slotSeekForwardShort()656 MainWindow::slotSeekForwardShort()
657 {
658     EngineController* ec = The::engineController();
659     ec->seekBy( AmarokConfig::seekShort() * 1000 );
660 }
661 
662 void
slotSeekForwardMedium()663 MainWindow::slotSeekForwardMedium()
664 {
665     EngineController* ec = The::engineController();
666     ec->seekBy( AmarokConfig::seekMedium() * 1000 );
667 }
668 
669 void
slotSeekForwardLong()670 MainWindow::slotSeekForwardLong()
671 {
672     EngineController* ec = The::engineController();
673     ec->seekBy( AmarokConfig::seekLong() * 1000 );
674 }
675 
676 void
slotSeekBackwardShort()677 MainWindow::slotSeekBackwardShort()
678 {
679     EngineController* ec = The::engineController();
680     ec->seekBy( AmarokConfig::seekShort() * -1000 );
681 }
682 
683 void
slotSeekBackwardMedium()684 MainWindow::slotSeekBackwardMedium()
685 {
686     EngineController* ec = The::engineController();
687     ec->seekBy( AmarokConfig::seekMedium() * -1000 );
688 }
689 
690 void
slotSeekBackwardLong()691 MainWindow::slotSeekBackwardLong()
692 {
693     EngineController* ec = The::engineController();
694     ec->seekBy( AmarokConfig::seekLong() * -1000 );
695 }
696 
slotPutCurrentTrackToClipboard()697 void MainWindow::slotPutCurrentTrackToClipboard()
698 {
699     Meta::TrackPtr currentTrack = The::engineController()->currentTrack();
700     if ( currentTrack )
701     {
702         QString text;
703         Meta::ArtistPtr artist = currentTrack->artist();
704         if( artist )
705             text = artist->prettyName() + " - ";
706         text += currentTrack->prettyName();
707 
708         QClipboard *clipboard = QApplication::clipboard();
709         clipboard->setText( text );
710     }
711 }
712 
713 void
activate()714 MainWindow::activate()
715 {
716 #ifdef Q_WS_X11
717     const KWindowInfo info = KWindowSystem::windowInfo( winId(), 0, 0 );
718 
719     if( KWindowSystem::activeWindow() != winId() )
720         setVisible( true );
721     else if( !info.isMinimized() )
722         setVisible( true );
723     if( !isHidden() )
724         KWindowSystem::activateWindow( winId() );
725 #else
726     setVisible( true );
727 #endif
728 }
729 
730 void
createActions()731 MainWindow::createActions()
732 {
733     KActionCollection* const ac = Amarok::actionCollection();
734     const EngineController* const ec = The::engineController();
735     const Playlist::Actions* const pa = The::playlistActions();
736     const Playlist::Controller* const pc = The::playlistController();
737 
738     KStandardAction::keyBindings( pApp, &App::slotConfigShortcuts, ac );
739     KStandardAction::preferences( pApp, &App::slotConfigAmarokWithEmptyPage, ac );
740 
741     m_showMenuBar = KStandardAction::showMenubar(this, &MainWindow::slotShowMenuBar, ac);
742     ac->action( KStandardAction::name( KStandardAction::KeyBindings ) )->setIcon( QIcon::fromTheme( QStringLiteral("configure-shortcuts-amarok") ) );
743     ac->action( KStandardAction::name( KStandardAction::Preferences ) )->setIcon( QIcon::fromTheme( QStringLiteral("configure-amarok") ) );
744     ac->action( KStandardAction::name( KStandardAction::Preferences ) )->setMenuRole(QAction::PreferencesRole); // Define OS X Prefs menu here, removes need for ifdef later
745 
746     KStandardAction::quit( pApp, &App::quit, ac );
747 
748     QAction *action = new QAction( QIcon::fromTheme( QStringLiteral("document-open") ), i18n("&Add Media..."), this );
749     ac->addAction( "playlist_add", action );
750     connect( action, &QAction::triggered, this, &MainWindow::slotAddLocation );
751     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_A ) );
752 
753     action = new QAction( QIcon::fromTheme( QStringLiteral("edit-clear-list") ), i18nc( "clear playlist", "&Clear Playlist" ), this );
754     connect( action, &QAction::triggered, pc, &Playlist::Controller::clear );
755     ac->addAction( "playlist_clear", action );
756 
757     action = new QAction( QIcon::fromTheme( QStringLiteral("format-list-ordered") ),
758                           i18nc( "edit play queue of playlist", "Edit &Queue" ), this );
759     //Qt::META+Qt::Key_Q is taken by Plasma as a global
760     action->setShortcut( QKeySequence( Qt::META + Qt::Key_U ) );
761     ac->addAction( "playlist_edit_queue", action );
762 
763     action = new QAction( i18nc( "Remove duplicate and dead (unplayable) tracks from the playlist", "Re&move Duplicates" ), this );
764     connect( action, &QAction::triggered, pc, &Playlist::Controller::removeDeadAndDuplicates );
765     ac->addAction( "playlist_remove_dead_and_duplicates", action );
766 
767     action = new Playlist::LayoutConfigAction( this );
768     ac->addAction( "playlist_layout", action );
769 
770     action = new QAction( QIcon::fromTheme( QStringLiteral("document-open-remote") ), i18n("&Add Stream..."), this );
771     connect( action, &QAction::triggered, this, &MainWindow::slotAddStream );
772     ac->addAction( "stream_add", action );
773 
774     action = new QAction( QIcon::fromTheme( QStringLiteral("document-export-amarok") ), i18n("&Export Playlist As..."), this );
775     connect( action, &QAction::triggered, this, &MainWindow::exportPlaylist );
776     ac->addAction( "playlist_export", action );
777 
778     action = new QAction( QIcon::fromTheme( QStringLiteral("bookmark-new") ), i18n( "Bookmark Media Sources View" ), this );
779     ac->addAction( "bookmark_browser", action );
780     connect( action, &QAction::triggered, The::amarokUrlHandler(), &AmarokUrlHandler::bookmarkCurrentBrowserView );
781 
782     action = new QAction( QIcon::fromTheme( QStringLiteral("bookmarks-organize") ), i18n( "Bookmark Manager" ), this );
783     ac->addAction( "bookmark_manager", action );
784     connect( action, &QAction::triggered, this, &MainWindow::slotShowBookmarkManager );
785 
786     action = new QAction( QIcon::fromTheme( QStringLiteral("view-media-equalizer") ), i18n( "Equalizer" ), this );
787     ac->addAction( "equalizer_dialog", action );
788     connect( action, &QAction::triggered, this, &MainWindow::slotShowEqualizer );
789 
790     action = new QAction( QIcon::fromTheme( QStringLiteral("bookmark-new") ), i18n( "Bookmark Playlist Setup" ), this );
791     ac->addAction( "bookmark_playlistview", action );
792     connect( action, &QAction::triggered, The::amarokUrlHandler(), &AmarokUrlHandler::bookmarkCurrentPlaylistView );
793 
794     action = new QAction( QIcon::fromTheme( QStringLiteral("bookmark-new") ), i18n( "Bookmark Context Applets" ), this );
795     ac->addAction( "bookmark_contextview", action );
796     connect( action, &QAction::triggered, The::amarokUrlHandler(), &AmarokUrlHandler::bookmarkCurrentContextView );
797 
798     action = new QAction( QIcon::fromTheme( QStringLiteral("media-album-cover-manager-amarok") ), i18n( "Cover Manager" ), this );
799     connect( action, &QAction::triggered, this, &MainWindow::slotShowCoverManager );
800     ac->addAction( "cover_manager", action );
801 
802     action = new QAction( QIcon::fromTheme(QStringLiteral("document-open")), i18n("Play Media..."), this );
803     ac->addAction( "playlist_playmedia", action );
804     action->setShortcut( Qt::CTRL + Qt::Key_O );
805     connect( action, &QAction::triggered, this, &MainWindow::slotPlayMedia );
806 
807     action = new QAction( QIcon::fromTheme(QStringLiteral("media-track-edit-amarok")), i18n("Edit Details of Currently Selected Track"), this );
808     ac->addAction( "trackdetails_edit", action );
809     action->setShortcut( Qt::CTRL + Qt::Key_E );
810     connect( action, &QAction::triggered, this, &MainWindow::slotEditTrackInfo );
811 
812     action = new QAction( QIcon::fromTheme( QStringLiteral("media-seek-forward-amarok") ),
813                           i18n( "Seek Forward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekShort() * 1000 ) ), this );
814     ac->addAction( "seek_forward_short", action );
815     action->setShortcut( Qt::CTRL + Qt::Key_Right );
816     connect( action, &QAction::triggered, this, &MainWindow::slotSeekForwardShort );
817 
818     action = new QAction( QIcon::fromTheme( QStringLiteral("media-seek-forward-amarok") ),
819                           i18n( "Seek Forward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekMedium() * 1000 ) ), this );
820     ac->addAction( "seek_forward_medium", action );
821     action->setShortcut( Qt::Key_Right );
822     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::SHIFT + Qt::Key_Plus ) );
823     connect( action, &QAction::triggered, this, &MainWindow::slotSeekForwardMedium );
824 
825     action = new QAction( QIcon::fromTheme( QStringLiteral("media-seek-forward-amarok") ),
826                           i18n( "Seek Forward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekLong() * 1000 ) ), this );
827     ac->addAction( "seek_forward_long", action );
828     action->setShortcut( Qt::SHIFT + Qt::Key_Right );
829     connect( action, &QAction::triggered, this, &MainWindow::slotSeekForwardLong );
830 
831 
832     action = new QAction( QIcon::fromTheme( QStringLiteral("media-seek-backward-amarok") ),
833                           i18n( "Seek Backward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekShort() * 1000 ) ), this );
834     ac->addAction( "seek_backward_short", action );
835     action->setShortcut( Qt::CTRL + Qt::Key_Left );
836     connect( action, &QAction::triggered, this, &MainWindow::slotSeekBackwardShort );
837 
838     action = new QAction( QIcon::fromTheme( QStringLiteral("media-seek-backward-amarok") ),
839                           i18n( "Seek Backward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekMedium() * 1000 ) ), this );
840     ac->addAction( "seek_backward_medium", action );
841     action->setShortcut( Qt::Key_Left );
842     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::SHIFT + Qt::Key_Minus ) );
843     connect( action, &QAction::triggered, this, &MainWindow::slotSeekBackwardMedium );
844 
845     action = new QAction( QIcon::fromTheme( QStringLiteral("media-seek-backward-amarok") ),
846                           i18n( "Seek Backward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekLong() * 1000 ) ), this );
847     ac->addAction( "seek_backward_long", action );
848     action->setShortcut( Qt::SHIFT + Qt::Key_Left );
849     connect( action, &QAction::triggered, this, &MainWindow::slotSeekBackwardLong );
850 
851     PERF_LOG( "MainWindow::createActions 6" )
852     action = new QAction( QIcon::fromTheme(QStringLiteral("view-refresh")), i18n( "Update Collection" ), this );
853     connect ( action, &QAction::triggered, CollectionManager::instance(), &CollectionManager::checkCollectionChanges );
854     ac->addAction( "update_collection", action );
855 
856     action =  new QAction( QIcon::fromTheme( QStringLiteral("amarok_playcount") ), i18n( "Synchronize Statistics..." ), this );
857     ac->addAction( "synchronize_statistics", action );
858     connect( action, &QAction::triggered, Amarok::Components::statSyncingController(),
859              &StatSyncing::Controller::synchronize );
860     Amarok::Components::statSyncingController();
861     action = new QAction( this );
862     ac->addAction( "prev", action );
863     action->setIcon( QIcon::fromTheme(QStringLiteral("media-skip-backward-amarok")) );
864     action->setText( i18n( "Previous Track" ) );
865     KGlobalAccel::setGlobalShortcut(action, QKeySequence() );
866     connect( action, &QAction::triggered, pa, &Playlist::Actions::back );
867 
868     action = new QAction( this );
869     ac->addAction( "replay", action );
870     action->setIcon( QIcon::fromTheme(QStringLiteral("media-playback-start")) );
871     action->setText( i18n( "Restart current track" ) );
872     KGlobalAccel::setGlobalShortcut(action, QKeySequence() );
873     connect( action, &QAction::triggered, ec, &EngineController::replay );
874 
875     action = new QAction( this );
876     ac->addAction( "shuffle_playlist", action );
877     action->setIcon( QIcon::fromTheme(QStringLiteral("media-playlist-shuffle")) );
878     action->setText( i18n( "Shuffle Playlist" ) );
879     action->setShortcut( Qt::CTRL + Qt::Key_H );
880     connect( action, &QAction::triggered, this, &MainWindow::slotShufflePlaylist );
881 
882     action = new QAction( this );
883     ac->addAction( QStringLiteral("repopulate"), action );
884     action->setText( i18n( "Repopulate Playlist" ) );
885     action->setIcon( QIcon::fromTheme("view-refresh-amarok") );
886     connect( action, &QAction::triggered, pa, &Playlist::Actions::repopulateDynamicPlaylist );
887 
888     action = new QAction( this );
889     ac->addAction( QStringLiteral("disable_dynamic"), action );
890     action->setText( i18n( "Disable Dynamic Playlist" ) );
891     action->setIcon( QIcon::fromTheme("edit-delete-amarok") );
892     //this is connected inside the dynamic playlist category
893 
894     action = new QAction( QIcon::fromTheme(QStringLiteral("media-skip-forward-amarok")), i18n( "Next Track" ), this );
895     ac->addAction( "next", action );
896     KGlobalAccel::setGlobalShortcut(action, QKeySequence() );
897     connect( action, &QAction::triggered, pa, &Playlist::Actions::next );
898 
899     action = new QAction( i18n( "Increase Volume" ), this );
900     ac->addAction( "increaseVolume", action );
901     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_Plus ) );
902     action->setShortcut( Qt::Key_Plus );
903     connect( action, &QAction::triggered, ec, &EngineController::regularIncreaseVolume );
904 
905     action = new QAction( i18n( "Decrease Volume" ), this );
906     ac->addAction( "decreaseVolume", action );
907     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_Minus ) );
908     action->setShortcut( Qt::Key_Minus );
909     connect( action, &QAction::triggered, ec, &EngineController::regularDecreaseVolume );
910 
911     action = new QAction( i18n( "Toggle Main Window" ), this );
912     ac->addAction( "toggleMainWindow", action );
913     KGlobalAccel::setGlobalShortcut(action, QKeySequence() );
914     connect( action, &QAction::triggered, this, &MainWindow::showHide );
915 
916     action = new QAction( i18n( "Toggle Full Screen" ), this );
917     ac->addAction( "toggleFullScreen", action );
918     action->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_F ) );
919     connect( action, &QAction::triggered, this, &MainWindow::slotFullScreen );
920 
921     action = new QAction( i18n( "Search playlist" ), this );
922     ac->addAction( "searchPlaylist", action );
923     action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_J ) );
924     connect( action, &QAction::triggered, this, &MainWindow::slotFocusPlaylistSearch );
925 
926     action = new QAction( i18n( "Search collection" ), this );
927     ac->addAction( "searchCollection", action );
928     action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_F ) );
929     connect( action, &QAction::triggered, this, &MainWindow::slotFocusCollectionSearch );
930 
931     action = new QAction( QIcon::fromTheme( "music-amarok" ), i18n("Show active track"), this );
932     ac->addAction( "show_active_track", action );
933     connect( action, &QAction::triggered, this, &MainWindow::slotShowActiveTrack );
934 
935     action = new QAction( i18n( "Show Notification Popup" ), this );
936     ac->addAction( "showNotificationPopup", action );
937     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_O ) );
938     connect( action, &QAction::triggered, this, &MainWindow::showNotificationPopup );
939 
940     action = new QAction( i18n( "Mute Volume" ), this );
941     ac->addAction( "mute", action );
942     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_M ) );
943     connect( action, &QAction::triggered, ec, &EngineController::toggleMute );
944 
945     action = new QAction( i18n( "Last.fm: Love Current Track" ), this );
946     ac->addAction( "loveTrack", action );
947     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_L ) );
948     connect( action, &QAction::triggered, this, &MainWindow::slotLoveTrack );
949 
950     action = new QAction( i18n( "Last.fm: Ban Current Track" ), this );
951     ac->addAction( "banTrack", action );
952     //KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_B ) );
953     connect( action, &QAction::triggered, this, &MainWindow::slotBanTrack );
954 
955     action = new QAction( i18n( "Last.fm: Skip Current Track" ), this );
956     ac->addAction( "skipTrack", action );
957     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_S ) );
958     connect( action, &QAction::triggered, this, &MainWindow::skipTrack );
959 
960     action = new QAction( QIcon::fromTheme( "media-track-queue-amarok" ), i18n( "Queue Track" ), this );
961     ac->addAction( "queueTrack", action );
962     action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_D ) );
963     connect( action, &QAction::triggered, this, &MainWindow::switchQueueStateShortcut );
964 
965     action = new QAction( i18n( "Put Artist - Title of the current track to the clipboard" ), this  );
966     ac->addAction("artistTitleClipboard", action);
967     action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_C ) );
968     connect( action, &QAction::triggered, this, &MainWindow::slotPutCurrentTrackToClipboard );
969 
970     action = new QAction( i18n( "Rate Current Track: 1" ), this );
971     ac->addAction( "rate1", action );
972     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_1 ) );
973     connect( action, &QAction::triggered, this, &MainWindow::setRating1 );
974 
975     action = new QAction( i18n( "Rate Current Track: 2" ), this );
976     ac->addAction( "rate2", action );
977     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_2 ) );
978     connect( action, &QAction::triggered, this, &MainWindow::setRating2 );
979 
980     action = new QAction( i18n( "Rate Current Track: 3" ), this );
981     ac->addAction( "rate3", action );
982     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_3 ) );
983     connect( action, &QAction::triggered, this, &MainWindow::setRating3 );
984 
985     action = new QAction( i18n( "Rate Current Track: 4" ), this );
986     ac->addAction( "rate4", action );
987     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_4 ) );
988     connect( action, &QAction::triggered, this, &MainWindow::setRating4 );
989 
990     action = new QAction( i18n( "Rate Current Track: 5" ), this );
991     ac->addAction( "rate5", action );
992     KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_5 ) );
993     connect( action, &QAction::triggered, this, &MainWindow::setRating5 );
994 
995 #ifdef DEBUG_BUILD_TYPE
996     action = new QAction( i18n( "Network Request Viewer" ), this );
997     ac->addAction( "network_request_viewer", action );
998     action->setIcon( QIcon::fromTheme( "utilities-system-monitor" ) );
999     connect( action, &QAction::triggered, this, &MainWindow::showNetworkRequestViewer );
1000 #endif // DEBUG_BUILD_TYPE
1001 
1002     action = KStandardAction::redo( pc, &Playlist::Controller::redo, this);
1003     ac->addAction( "playlist_redo", action );
1004     action->setEnabled( false );
1005     action->setIcon( QIcon::fromTheme( "edit-redo" ) );
1006     connect( pc, &Playlist::Controller::canRedoChanged, action, &QAction::setEnabled );
1007 
1008     action = KStandardAction::undo( pc, &Playlist::Controller::undo, this);
1009     ac->addAction( "playlist_undo", action );
1010     action->setEnabled( false );
1011     action->setIcon( QIcon::fromTheme( "edit-undo" ) );
1012     connect( pc, &Playlist::Controller::canUndoChanged, action, &QAction::setEnabled );
1013 
1014     action = new QAction( QIcon::fromTheme( "amarok" ), i18n( "&About Amarok" ), this );
1015     ac->addAction( "extendedAbout", action );
1016     connect( action, &QAction::triggered, this, &MainWindow::showAbout );
1017 
1018     action = new QAction ( QIcon::fromTheme( "info-amarok" ), i18n( "&Diagnostics" ), this );
1019     ac->addAction( "diagnosticDialog", action );
1020     connect( action, &QAction::triggered, this, &MainWindow::slotShowDiagnosticsDialog );
1021 
1022     action = new QAction( QIcon::fromTheme( "tools-report-bug" ), i18n("&Report Bug..."), this );
1023     ac->addAction( "reportBug", action );
1024     connect( action, &QAction::triggered, this, &MainWindow::showReportBug );
1025 
1026     PERF_LOG( "MainWindow::createActions 8" )
1027     new Amarok::MenuAction( ac, this );
1028     new Amarok::StopAction( ac, this );
1029     new Amarok::StopPlayingAfterCurrentTrackAction( ac, this );
1030     new Amarok::PlayPauseAction( ac, this );
1031     new Amarok::ReplayGainModeAction( ac, this );
1032 
1033     ac->addAssociatedWidget( this );
1034     foreach( QAction* action, ac->actions() )
1035         action->setShortcutContext( Qt::WindowShortcut );
1036 }
1037 
1038 void
setRating(int n)1039 MainWindow::setRating( int n )
1040 {
1041     n *= 2;
1042 
1043     Meta::TrackPtr track = The::engineController()->currentTrack();
1044     if( track )
1045     {
1046         Meta::StatisticsPtr statistics = track->statistics();
1047         // if we're setting an identical rating then we really must
1048         // want to set the half-star below rating
1049         if( statistics->rating() == n )
1050             n -= 1;
1051 
1052         statistics->setRating( n );
1053         Amarok::OSD::instance()->OSDWidget::ratingChanged( statistics->rating() );
1054     }
1055 }
1056 
1057 void
createMenus()1058 MainWindow::createMenus()
1059 {
1060     m_menubar = menuBar();
1061 
1062     //BEGIN Actions menu
1063     QMenu *actionsMenu = new QMenu( m_menubar );
1064 #ifdef Q_WS_MAC
1065     // Add these functions to the dock icon menu in OS X
1066     //extern void qt_mac_set_dock_menu(QMenu *);
1067     //qt_mac_set_dock_menu(actionsMenu);
1068     // Change to avoid duplicate menu titles in OS X
1069     actionsMenu->setTitle( i18n("&Music") );
1070 #else
1071     actionsMenu->setTitle( i18n("&Amarok") );
1072 #endif
1073     actionsMenu->addAction( Amarok::actionCollection()->action("playlist_playmedia") );
1074     actionsMenu->addSeparator();
1075     actionsMenu->addAction( Amarok::actionCollection()->action("prev") );
1076     actionsMenu->addAction( Amarok::actionCollection()->action("play_pause") );
1077     actionsMenu->addAction( Amarok::actionCollection()->action("stop") );
1078     actionsMenu->addAction( Amarok::actionCollection()->action("stop_after_current") );
1079     actionsMenu->addAction( Amarok::actionCollection()->action("next") );
1080 
1081 
1082 #ifndef Q_WS_MAC    // Avoid duplicate "Quit" in OS X dock menu
1083     actionsMenu->addSeparator();
1084     actionsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::Quit ) ) );
1085 #endif
1086     //END Actions menu
1087 
1088     //BEGIN View menu
1089     QMenu* viewMenu = new QMenu(this);
1090     addViewMenuItems(viewMenu);
1091     //END View menu
1092 
1093     //BEGIN Playlist menu
1094     QMenu *playlistMenu = new QMenu( m_menubar.data() );
1095     playlistMenu->setTitle( i18n("&Playlist") );
1096     playlistMenu->addAction( Amarok::actionCollection()->action("playlist_add") );
1097     playlistMenu->addAction( Amarok::actionCollection()->action("stream_add") );
1098     //playlistMenu->addAction( Amarok::actionCollection()->action("playlist_save") ); //FIXME: See FIXME in PlaylistDock.cpp
1099     playlistMenu->addAction( Amarok::actionCollection()->action( "playlist_export" ) );
1100     playlistMenu->addSeparator();
1101     playlistMenu->addAction( Amarok::actionCollection()->action("playlist_undo") );
1102     playlistMenu->addAction( Amarok::actionCollection()->action("playlist_redo") );
1103     playlistMenu->addSeparator();
1104     playlistMenu->addAction( Amarok::actionCollection()->action("playlist_clear") );
1105     playlistMenu->addAction( Amarok::actionCollection()->action("playlist_remove_dead_and_duplicates") );
1106     playlistMenu->addAction( Amarok::actionCollection()->action("playlist_layout") );
1107     playlistMenu->addAction( Amarok::actionCollection()->action("playlist_edit_queue") );
1108     //END Playlist menu
1109 
1110     //BEGIN Tools menu
1111     m_toolsMenu = new QMenu( m_menubar.data() );
1112     m_toolsMenu->setTitle( i18n("&Tools") );
1113 
1114     m_toolsMenu->addAction( Amarok::actionCollection()->action("bookmark_manager") );
1115     m_toolsMenu->addAction( Amarok::actionCollection()->action("cover_manager") );
1116     m_toolsMenu->addAction( Amarok::actionCollection()->action("equalizer_dialog") );
1117 #ifdef DEBUG_BUILD_TYPE
1118     m_toolsMenu->addAction( Amarok::actionCollection()->action("network_request_viewer") );
1119 #endif // DEBUG_BUILD_TYPE
1120     m_toolsMenu->addSeparator();
1121     m_toolsMenu->addAction( Amarok::actionCollection()->action("update_collection") );
1122     m_toolsMenu->addAction( Amarok::actionCollection()->action("synchronize_statistics") );
1123     //END Tools menu
1124 
1125     //BEGIN Settings menu
1126     m_settingsMenu = new QMenu( m_menubar.data() );
1127     m_settingsMenu->setTitle( i18n("&Settings") );
1128 
1129     m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::ShowMenubar ) ) );
1130 
1131     //TODO use KStandardAction or KXmlGuiWindow
1132 
1133     // the phonon-coreaudio  backend has major issues with either the VolumeFaderEffect itself
1134     // or with it in the pipeline. track playback stops every ~3-4 tracks, and on tracks >5min it
1135     // stops at about 5:40. while we get this resolved upstream, don't make playing amarok such on osx.
1136     // so we disable replaygain on osx
1137 
1138 #ifndef Q_WS_MAC
1139     m_settingsMenu->addAction( Amarok::actionCollection()->action("replay_gain_mode") );
1140     m_settingsMenu->addSeparator();
1141 #endif
1142 
1143     m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::KeyBindings ) ) );
1144     m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::Preferences ) ) );
1145     //END Settings menu
1146 
1147     m_menubar->addMenu( actionsMenu );
1148     m_menubar->addMenu( viewMenu );
1149     m_menubar->addMenu( playlistMenu );
1150     m_menubar->addMenu( m_toolsMenu.data() );
1151     m_menubar->addMenu( m_settingsMenu.data() );
1152 
1153     QMenu *helpMenu = Amarok::Menu::helpMenu();
1154     helpMenu->insertAction( helpMenu->actions().last(),
1155                             Amarok::actionCollection()->action( "extendedAbout" ) );
1156     helpMenu->insertAction( helpMenu->actions().last(),
1157                             Amarok::actionCollection()->action( "diagnosticDialog" ) );
1158 
1159     m_menubar->addSeparator();
1160     m_menubar->addMenu( helpMenu );
1161 }
1162 
1163 void
slotShowMenuBar()1164 MainWindow::slotShowMenuBar()
1165 {
1166     if (!m_showMenuBar->isChecked())
1167     {
1168         //User have chosen to hide a menu. Lets warn him
1169         if (KMessageBox::warningContinueCancel(this,
1170             i18n("You have chosen to hide the menu bar.\n\nPlease remember that you can always use the shortcut \"%1\" to bring it back.", m_showMenuBar->shortcut().toString() ),
1171             i18n("Hide Menu"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "showMenubar") != KMessageBox::Continue)
1172         {
1173             //Cancel menu hiding. Revert menu item to checked state.
1174             m_showMenuBar->setChecked(true);
1175             return;
1176         }
1177     }
1178     menuBar()->setVisible(m_showMenuBar->isChecked());
1179 }
1180 
1181 void
showAbout()1182 MainWindow::showAbout()
1183 {
1184     ExtendedAboutDialog dialog( KAboutData::applicationData(), &ocsData );
1185     dialog.exec();
1186 }
1187 
1188 void
showReportBug()1189 MainWindow::showReportBug()
1190 {
1191     KBugReport * rbDialog = new KBugReport( KAboutData::applicationData() ,this  );
1192     rbDialog->setObjectName( "KBugReport" );
1193     rbDialog->exec();
1194     delete rbDialog;
1195 }
1196 
1197 void
changeEvent(QEvent * event)1198 MainWindow::changeEvent( QEvent *event )
1199 {
1200     if( event->type() == QEvent::PaletteChange )
1201         The::paletteHandler()->setPalette( palette() );
1202 }
1203 
1204 void
slotStopped()1205 MainWindow::slotStopped()
1206 {
1207     setPlainCaption( i18n( AMAROK_CAPTION ) );
1208 }
1209 
1210 void
slotPaused()1211 MainWindow::slotPaused()
1212 {
1213     setPlainCaption( i18n( "Paused  ::  %1", i18n( AMAROK_CAPTION ) ) );
1214 }
1215 
1216 void
slotNewTrackPlaying()1217 MainWindow::slotNewTrackPlaying()
1218 {
1219     slotMetadataChanged( The::engineController()->currentTrack() );
1220 }
1221 
1222 void
slotMetadataChanged(Meta::TrackPtr track)1223 MainWindow::slotMetadataChanged( Meta::TrackPtr track )
1224 {
1225     if( track )
1226         setPlainCaption( i18n( "%1 - %2  ::  %3", track->artist() ? track->artist()->prettyName() : i18n( "Unknown" ), track->prettyName(), i18n( AMAROK_CAPTION ) ) );
1227 }
1228 
1229 CollectionWidget *
collectionBrowser()1230 MainWindow::collectionBrowser()
1231 {
1232     return m_collectionBrowser;
1233 }
1234 
1235 QString
activeBrowserName()1236 MainWindow::activeBrowserName()
1237 {
1238     if(m_browserDock->list()->activeCategory() )
1239         return m_browserDock->list()->activeCategory()->name();
1240     else
1241         return QString();
1242 }
1243 
1244 void
setLayoutLocked(bool locked)1245 MainWindow::setLayoutLocked( bool locked )
1246 {
1247     DEBUG_BLOCK
1248 
1249     if( m_browserDock )
1250         m_browserDock.data()->setMovable( !locked );
1251 
1252     if( m_contextDock )
1253         m_contextDock.data()->setMovable( !locked );
1254 
1255     if( m_playlistDock )
1256         m_playlistDock.data()->setMovable( !locked );
1257 
1258     if( m_slimToolbar )
1259     {
1260         m_slimToolbar.data()->setFloatable( !locked );
1261         m_slimToolbar.data()->setMovable( !locked );
1262     }
1263 
1264     if( m_mainToolbar )
1265     {
1266         m_mainToolbar.data()->setFloatable( !locked );
1267         m_mainToolbar.data()->setMovable( !locked );
1268     }
1269 
1270     AmarokConfig::setLockLayout( locked );
1271     AmarokConfig::self()->save();
1272 }
1273 
1274 void
resetLayout()1275 MainWindow::resetLayout()
1276 {
1277     // Store current state, so that we can undo the operation
1278     const QByteArray state = saveState();
1279 
1280     // Remove all dock widgets, then add them again. This resets their state completely.
1281     removeDockWidget( m_browserDock.data() );
1282     removeDockWidget( m_contextDock.data() );
1283     removeDockWidget( m_playlistDock.data() );
1284 
1285     addDockWidget( Qt::LeftDockWidgetArea, m_browserDock.data() );
1286     addDockWidget( Qt::LeftDockWidgetArea, m_contextDock.data(), Qt::Horizontal );
1287     addDockWidget( Qt::LeftDockWidgetArea, m_playlistDock.data(), Qt::Horizontal );
1288 
1289     m_browserDock->setFloating( false );
1290     m_contextDock->setFloating( false );
1291     m_playlistDock->setFloating( false );
1292 
1293     m_browserDock->show();
1294     m_contextDock->show();
1295     m_playlistDock->show();
1296 
1297     // Now set Amarok's default dockwidget sizes
1298     setDefaultDockSizes();
1299 
1300     if( KMessageBox::warningContinueCancel( this, i18n( "Apply this layout change?" ), i18n( "Reset Layout" ) ) == KMessageBox::Cancel )
1301         restoreState( state );
1302 }
1303 
1304 void
setDefaultDockSizes()1305 MainWindow::setDefaultDockSizes() // SLOT
1306 {
1307     int totalWidgetWidth = contentsRect().width();
1308 
1309     //get the width of the splitter handles, we need to subtract these...
1310     const int splitterHandleWidth = style()->pixelMetric( QStyle::PM_DockWidgetSeparatorExtent, 0, 0 );
1311 
1312     totalWidgetWidth -= ( splitterHandleWidth * 2 );
1313 
1314     const int widgetWidth = totalWidgetWidth / 3;
1315     const int leftover = totalWidgetWidth - 3 * widgetWidth;
1316 
1317     //We need to set fixed widths initially, just until the main window has been properly laid out. As soon as this has
1318     //happened, we will unlock these sizes again so that the elements can be resized by the user.
1319     const int mins[3] = { m_browserDock->minimumWidth(), m_contextDock->minimumWidth(), m_playlistDock->minimumWidth() };
1320     const int maxs[3] = { m_browserDock->maximumWidth(), m_contextDock->maximumWidth(), m_playlistDock->maximumWidth() };
1321 
1322     m_browserDock->setFixedWidth( widgetWidth * 0.65 );
1323     m_contextDock->setFixedWidth( widgetWidth * 1.7 + leftover );
1324     m_playlistDock->setFixedWidth( widgetWidth * 0.65 );
1325 
1326     // Important: We need to activate the layout we have just set
1327     layout()->activate();
1328 
1329     m_browserDock->setMinimumWidth( mins[0] ); m_browserDock->setMaximumWidth( maxs[0] );
1330     m_contextDock->setMinimumWidth( mins[1] ); m_contextDock->setMaximumWidth( maxs[1] );
1331     m_playlistDock->setMinimumWidth( mins[2] ); m_playlistDock->setMaximumWidth( maxs[2] );
1332 }
1333 
1334 bool
playAudioCd()1335 MainWindow::playAudioCd()
1336 {
1337     DEBUG_BLOCK
1338     //drop whatever we are doing and play auidocd
1339 
1340     QList<Collections::Collection*> collections = CollectionManager::instance()->viewableCollections();
1341 
1342     // Search a non-empty MemoryCollection with the id: AudioCd
1343     foreach( Collections::Collection *collection, collections )
1344     {
1345         if( collection->collectionId() == "AudioCd" )
1346         {
1347 
1348             debug() << "got audiocd collection";
1349 
1350             Collections::MemoryCollection * cdColl = dynamic_cast<Collections::MemoryCollection *>( collection );
1351 
1352             if( !cdColl || cdColl->trackMap().isEmpty() )
1353             {
1354                 debug() << "cd collection not ready yet (track count = 0 )";
1355                 m_waitingForCd = true;
1356                 return false;
1357             }
1358 
1359             The::playlistController()->insertOptioned( cdColl->trackMap().values(), Playlist::OnPlayMediaAction );
1360             m_waitingForCd = false;
1361             return true;
1362         }
1363     }
1364 
1365     debug() << "waiting for cd...";
1366     m_waitingForCd = true;
1367     return false;
1368 }
1369 
1370 bool
isWaitingForCd() const1371 MainWindow::isWaitingForCd() const
1372 {
1373     DEBUG_BLOCK
1374     debug() << "waiting?: " << m_waitingForCd;
1375     return m_waitingForCd;
1376 }
1377 
1378 bool
isOnCurrentDesktop() const1379 MainWindow::isOnCurrentDesktop() const
1380 {
1381 #ifdef Q_WS_X11
1382     return KWindowSystem::windowInfo( winId(), NET::WMDesktop ).desktop() == KWindowSystem::currentDesktop();
1383 #else
1384     return true;
1385 #endif
1386 }
1387 
1388 
1389