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 ***************************************************************************/
8 /***************************************************************************
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
15 ***************************************************************************/
17 #include "config-amarok.h" //HAVE_LIBVISUAL definition
19 #include "actionclasses.h" //see toolbar construction
20 #include "amarokconfig.h"
22 #include "analyzerwidget.h"
23 #include "collection/CollectionManager.h"
24 #include "collectionbrowser/CollectionWidget.h"
25 #include "context/CoverBling.h"
26 #include "context/ContextView.h"
27 #include "context/ControlBox.h"
28 #include "context/DataEngineManager.h"
29 #include "covermanager.h" // for actions
31 #include "editfilterdialog.h"
32 #include "enginecontroller.h" //for actions in ctor
33 #include "k3bexporter.h"
34 #include "lastfm.h" //check credentials when adding lastfm streams
35 #include "MainWindow.h"
36 #include "mediabrowser.h"
37 #include "playlist/PlaylistModel.h"
38 #include "playlist/PlaylistWidget.h"
40 #include "progressslider.h"
41 #include "scriptmanager.h"
42 #include "searchwidget.h"
43 #include "selectLabel.h"
44 #include "servicebrowser/magnatunestore/MagnatuneStore.h"
45 #include "servicebrowser/scriptableservice/scriptableservice.h"
46 #include "servicebrowser/servicebrowser.h"
47 #include "servicebrowser/jamendo/jamendoservice.h"
48 #include "servicebrowser/shoutcast/ShoutcastService.h"
49 #include "servicebrowser/mp3tunes/mp3tunesservice.h"
51 #include "Sidebar.moc"
52 #include "socketserver.h"
53 #include "Statistics.h"
54 #include "statusbar.h"
55 #include "TheInstances.h"
56 #include "threadmanager.h"
58 #include "volumewidget.h"
59 #include "PodcastCollection.h"
60 #include "playlistmanager/PlaylistManager.h"
62 #include "playlistbrowser/PlaylistBrowser.h"
64 #include "queuemanager/QueueManager.h"
67 #include <QHeaderView>
68 #include <QLabel> //search filter label
70 #include <QPaintEngine>
71 #include <QPainter> //dynamic title
73 #include <QTimer> //search filter timer
74 #include <QToolTip> //QToolTip::add()
75 #include <QVBoxLayout>
77 #include <KAction> //m_actionCollection
78 #include <KActionCollection>
79 #include <KActionMenu>
80 #include <kapplication.h> //kapp
81 #include <kfiledialog.h> //savePlaylist(), openPlaylist()
83 #include <kinputdialog.h> //slotAddStream()
86 #include <kmessagebox.h> //savePlaylist()
88 #include <kpushbutton.h>
89 #include <kstandarddirs.h> //Welcome Tab, locate welcome.html
90 #include <kstatusbar.h>
92 #include <ktoolbarspaceraction.h>
94 #include <kxmlguibuilder.h> //XMLGUI
95 #include <kxmlguifactory.h> //XMLGUI
98 MainWindow
*MainWindow::s_instance
= 0;
100 MainWindow::MainWindow()
101 :KXmlGuiWindow( 0, Qt::WGroupLeader
)
104 setObjectName("MainWindow");
107 new Playlist::Model( this );
109 // Sets caption and icon correctly (needed e.g. for GNOME)
110 kapp
->setTopWidget( this );
116 if( AmarokConfig::mainWindowSize().isValid() ) {
117 // if first ever run, use sizeHint(), and let
118 // KWindowSystem place us otherwise use the stored values
119 resize( AmarokConfig::mainWindowSize() );
120 move( AmarokConfig::mainWindowPos() );
122 m_browsers
= new SideBar( this, new KVBox
);
125 MainWindow::~MainWindow()
127 AmarokConfig::setMainWindowPos( pos() ); //TODO de XT?
128 AmarokConfig::setMainWindowSize( size() ); //TODO de XT?
132 ///////// public interface
135 * This function will initialize the playlist window.
137 void MainWindow::init()
139 layout()->setContentsMargins( 0, 0, 0, 0 );
142 //this function is necessary because Amarok::actionCollection() returns our actionCollection
143 //via the App::m_pMainWindow pointer since App::m_pMainWindow is not defined until
144 //the above ctor returns it causes a crash unless we do the initialisation in 2 stages.
145 KVBox
*playlistwindow
= new KVBox
;
146 playlistwindow
->setMaximumSize( QSize( 300, 7000 ) );
147 playlistwindow
->setMinimumSize( QSize( 250, 100 ) );
148 playlistwindow
->setSizePolicy( QSizePolicy::Fixed
, QSizePolicy::Expanding
);
150 new Playlist::Widget( playlistwindow
);
152 KToolBar
*plBar
= new Amarok::ToolBar( playlistwindow
);
153 plBar
->setObjectName( "PlaylistToolBar" );
155 { //START Playlist toolbar
156 plBar
->setToolButtonStyle( Qt::ToolButtonIconOnly
);
157 plBar
->setSizePolicy( QSizePolicy::Preferred
, QSizePolicy::Preferred
);
158 plBar
->setIconDimensions( 22 );
159 plBar
->setMovable( false );
160 plBar
->addAction( new KToolBarSpacerAction( this ) );
161 plBar
->addAction( actionCollection()->action( "playlist_clear") );
162 plBar
->addAction( actionCollection()->action( "playlist_save") );
163 plBar
->addSeparator();
164 plBar
->addAction( actionCollection()->action( "playlist_undo") );
165 plBar
->addAction( actionCollection()->action( "playlist_redo") );
166 plBar
->addSeparator();
167 plBar
->addWidget( new SelectLabel( static_cast<Amarok::SelectAction
*>( actionCollection()->action("repeat") ), plBar
) );
168 plBar
->addWidget( new SelectLabel( static_cast<Amarok::SelectAction
*>( actionCollection()->action("random_mode") ), plBar
) );
169 plBar
->addAction( new KToolBarSpacerAction( this ) );
170 plBar
->addSeparator();
171 plBar
->addAction( actionCollection()->action( "playlist_switch") );
172 } //END Playlist Toolbar
175 m_controlBar
= new MainToolbar( this );
176 m_controlBar
->setMaximumSize( 20000, 62 );
177 m_controlBar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Preferred
);
179 AnalyzerWidget
*aw
= new AnalyzerWidget( m_controlBar
);
180 aw
->setMinimumSize( 200, 30 );
182 m_controlBar
->layout()->setAlignment( aw
, Qt::AlignLeft
);
184 KVBox
*aVBox
= new KVBox( m_controlBar
);
185 aVBox
->setMaximumSize( 50000, 60 );
187 KHBox
*insideBox
= new KHBox( aVBox
);
188 insideBox
->setMaximumSize( 600000, 45 );
190 KToolBar
*playerControlsToolbar
= new Amarok::ToolBar( insideBox
);
192 playerControlsToolbar
->setMinimumSize( 180, 45 );
193 insideBox
->layout()->setAlignment( playerControlsToolbar
, Qt::AlignCenter
);
196 QWidget
* spacer
= new QWidget( m_controlBar
);
197 spacer
->setMinimumSize( 50, 30 );
198 m_controlBar
->layout()->setAlignment( spacer
, Qt::AlignRight
);
200 VolumeWidget
*vw
= new VolumeWidget( m_controlBar
);
201 vw
->setMinimumSize( 150, 30 );
202 m_controlBar
->layout()->setAlignment( vw
, Qt::AlignRight
| Qt::AlignVCenter
);
205 ProgressWidget
*pWidget
= new ProgressWidget( aVBox
);
206 pWidget
->setMinimumSize( 400, 17 );
207 pWidget
->setMaximumSize( 600000, 17 );
209 playerControlsToolbar
->setToolButtonStyle( Qt::ToolButtonIconOnly
);
210 playerControlsToolbar
->setSizePolicy( QSizePolicy::Preferred
, QSizePolicy::Preferred
);
211 playerControlsToolbar
->setIconDimensions( 32 );
212 playerControlsToolbar
->setMovable( false );
213 playerControlsToolbar
->addAction( actionCollection()->action( "prev" ) );
214 playerControlsToolbar
->addAction( actionCollection()->action( "play_pause" ) );
215 playerControlsToolbar
->addAction( actionCollection()->action( "stop" ) );
216 playerControlsToolbar
->addAction( actionCollection()->action( "next" ) );
221 QColor topColor
= bottomColor
= palette().highlight().color().dark( 150 );
222 bottomColor
= bottomColor
.dark( 100 );
223 topColor
.setAlpha( 75 );
224 bottomColor
.setAlpha( 130 );
226 this->toolBars().clear();
231 QWidget
*contextWidget
= new QWidget( this );
232 contextWidget
->setMinimumSize( QSize(500,100) );
233 contextWidget
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Expanding
);
234 new Context::ContextView( contextWidget
);
236 QVBoxLayout
* layout
= new QVBoxLayout( contextWidget
);
237 layout
->setContentsMargins( 0, 0, 0, 0 );
238 layout
->addWidget( Context::ContextView::self() );
239 Amarok::StatusBar
*statusbar
= new Amarok::StatusBar( contextWidget
);
240 layout
->addWidget( statusbar
);
242 if( AmarokConfig::useCoverBling() && QGLFormat::hasOpenGL() )
243 layout
->addWidget( new CoverBling( this ) );
245 ControlBox
* controlBox
= new ControlBox( contextWidget
);
247 // TODO fix the location of the controlbox so its not a few pixels out of the
249 connect( controlBox
, SIGNAL(zoomIn()), Context::ContextView::self(), SLOT(zoomIn()) );
250 connect( controlBox
, SIGNAL(zoomOut()), Context::ContextView::self(), SLOT(zoomOut()) );
251 connect( controlBox
, SIGNAL(addApplet(const QString
&)), Context::ContextView::self(), SLOT(addApplet(const QString
&)) );
252 connect( controlBox
, SIGNAL(lockInterface(bool)), Context::ContextView::self()->contextScene(), SLOT(setImmutable(bool)) );
255 m_browsers
->setMaximumSize( QSize(300,7000) );
256 connect( m_browsers
, SIGNAL( widgetActivated( int ) ), SLOT( slotShrinkBrowsers( int ) ) );
258 QVBoxLayout
*mainLayout
= new QVBoxLayout
;
259 mainLayout
->setContentsMargins( 0, 0, 0, 0 );
261 QWidget
*centralWidget
= new QWidget( this );
262 centralWidget
->setLayout( mainLayout
);
264 m_splitter
= new QSplitter( Qt::Horizontal
, centralWidget
);
265 m_splitter
->setHandleWidth( 0 );
266 m_splitter
->addWidget( m_browsers
);
267 m_splitter
->addWidget( contextWidget
);
268 m_splitter
->addWidget( playlistwindow
);
270 mainLayout
->addWidget( m_controlBar
);
271 mainLayout
->addWidget( m_splitter
);
272 setCentralWidget( centralWidget
);
276 Debug::Block
block( "Creating browsers. Please report long start times!" );
278 #define addBrowserMacro( Type, name, text, icon ) { \
279 Debug::Block block( name ); \
280 m_browsers->addWidget( KIcon( icon ), text, new Type( name ) ); \
281 m_browserNames.append( name ); }
283 #define addInstBrowserMacro( Type, name, text, icon ) { \
284 m_browsers->addWidget( KIcon( icon ), text, Type::instance() ); \
285 m_browserNames.append( name ); }
287 addBrowserMacro( CollectionWidget
, "CollectionBrowser", i18n("Collection"), Amarok::icon( "collection" ) )
288 //FIXME: figure this out
289 //m_browsers->makeDropProxy( "CollectionBrowser", CollectionView::instance() );
291 addBrowserMacro( PlaylistBrowserNS::PlaylistBrowser
, "NeoPlaylistBrowser", i18n("Playlists"), Amarok::icon( "playlist" ) )
293 PodcastCollection
*podcastCollection
= new PodcastCollection();
294 The::playlistManager()->addProvider( podcastCollection
->channelProvider(), PlaylistManager::PodcastChannel
);
296 //DEBUG: Comment out the addBrowserMacro line and uncomment the m_browsers line (passing in a vfat device name) to see the "virtual root" functionality
301 //addBrowserMacro( FileBrowser, "FileBrowser", i18n("Files"), Amarok::icon( "files" ) )
302 //Add Magnatune browser
303 //addInstBrowserMacro( MagnatuneStore, "MagnatuneStore", i18n("Magnatune"), Amarok::icon( "magnatune" ) )
306 //cant use macros here since we need access to the browsers directly
307 // ServiceBrowser * storeServiceBrowser = new ServiceBrowser(this, "Stores" );;
308 //m_browsers->addWidget( KIcon( Amarok::icon( "magnatune" ) ), i18n("Stores"), storeServiceBrowser );
309 //m_browserNames.append( "Stores" );
312 ServiceBrowser
* internetContentServiceBrowser
= new ServiceBrowser(this, "Internet Content" );;
313 m_browsers
->addWidget( KIcon( Amarok::icon( "magnatune" ) ), i18n("Internet"), internetContentServiceBrowser
);
314 m_browserNames
.append( "Internet" );
316 debug() << "Add me dammit!!!!!";
317 internetContentServiceBrowser
->setScriptableServiceManager( new ScriptableServiceManager( 0 ) );
319 internetContentServiceBrowser
->addService( new MagnatuneStore( "Magnatune Store" ) );
321 internetContentServiceBrowser
->addService( new JamendoService( "Jamendo.com" ) );
323 internetContentServiceBrowser
->addService( new ShoutcastService( "Shoutcasst Directory" ) );
325 internetContentServiceBrowser
->addService( new Mp3tunesService( "Mp3Tunes Locker" ) );
328 //internetContentServiceBrowser->addService( new Mp3tunesService( "Mp3tunes.com" ) );
330 //addInstBrowserMacro( ServiceBrowser, "Stores", i18n("Stores"), Amarok::icon( "magnatune" ) ) //FIXME: icon
331 //addInstBrowserMacro( ServiceBrowser, "Internet Content", i18n("Internet Content"), Amarok::icon( "magnatune" ) ) //FIXME: icon
333 // new MediaBrowser( "MediaBrowser" );
334 // if( MediaBrowser::isAvailable() )
336 // addInstBrowserMacro( MediaBrowser, "MediaBrowser", i18n("Devices"), Amarok::icon( "device" ) )
338 #undef addBrowserMacro
339 #undef addInstBrowserMacro
344 kapp
->installEventFilter( this ); // keyboards shortcuts for the browsers
346 Amarok::MessageQueue::instance()->sendMessages();
349 void MainWindow::slotSetFilter( const QString
&filter
) //SLOT
352 // m_lineEdit->setText( filter );
355 void MainWindow::slotShrinkBrowsers( int index
) const
357 // Because QSplitter sucks and will not recompute sizes if a pane is shrunk and not hidden.
361 sizes
<< m_browsers
->sideBarWidget()->width()
362 << m_splitter
->sizes()[1] + m_splitter
->sizes()[0] - m_browsers
->sideBarWidget()->width()
363 << m_splitter
->sizes()[2];
364 m_splitter
->setSizes( sizes
);
368 // void MainWindow::slotEditFilter() //SLOT
370 // EditFilterDialog *fd = new EditFilterDialog( this, true, "" );
371 // connect( fd, SIGNAL(filterChanged(const QString &)), SLOT(slotSetFilter(const QString &)) );
373 // m_searchWidget->lineEdit()->setText( fd->filter() );
377 void MainWindow::addBrowser( const QString
&name
, QWidget
*browser
, const QString
&text
, const QString
&icon
)
379 if( !m_browserNames
.contains( name
) )
381 m_browsers
->addWidget( KIcon(Amarok::icon( icon
)), text
, browser
);
382 m_browserNames
.append( name
);
386 void MainWindow::showBrowser( const QString
&name
)
388 const int index
= m_browserNames
.indexOf( name
);
390 m_browsers
->showWidget( index
);
395 * @param o The object
398 * Here we filter some events for the Playlist Search LineEdit and the Playlist. @n
399 * this makes life easier since we have more useful functions available from this class
401 bool MainWindow::eventFilter( QObject
*o
, QEvent
*e
)
403 typedef Q3ListViewItemIterator It
;
407 case 6/*QEvent::KeyPress*/:
409 //there are a few keypresses that we intercept
411 #define e static_cast<QKeyEvent*>(e)
414 // if( e->key() == Qt::Key_F2 )
416 // // currentItem is ALWAYS visible.
417 // Q3ListViewItem *item = pl->currentItem();
419 // // intercept F2 for inline tag renaming
420 // // NOTE: tab will move to the next tag
421 // // NOTE: if item is still null don't select first item in playlist, user wouldn't want that. It's silly.
422 // // TODO: berkus has solved the "inability to cancel" issue with K3ListView, but it's not in kdelibs yet..
424 // // item may still be null, but this is safe
425 // // NOTE: column 0 cannot be edited currently, hence we pick column 1
426 // pl->rename( item, 1 ); //TODO what if this column is hidden?
432 // if( o == m_searchWidget->lineEdit() ) //the search lineedit
434 // Q3ListViewItem *item;
435 // switch( e->key() )
438 // case Qt::Key_Down:
439 // case Qt::Key_PageDown:
440 // case Qt::Key_PageUp:
442 // QApplication::sendEvent( pl, e );
445 // case Qt::Key_Return:
446 // case Qt::Key_Enter:
447 // item = *It( pl, It::Visible );
448 // //m_lineEdit->clear();
449 // pl->m_filtertimer->stop(); //HACK HACK HACK
451 // if( e->modifiers() & Qt::ControlModifier )
453 // QList<PlaylistItem*> in, out;
454 // if( e->modifiers() & Qt::ShiftModifier )
455 // for( It it( pl, It::Visible ); PlaylistItem *x = static_cast<PlaylistItem*>( *it ); ++it )
457 // pl->queue( x, true );
458 // ( pl->m_nextTracks.contains( x ) ? in : out ).append( x );
462 // It it( pl, It::Visible );
463 // pl->activate( *it );
465 // for( int i = 0; PlaylistItem *x = static_cast<PlaylistItem*>( *it ); ++i, ++it )
468 // pl->m_nextTracks.insert( i, x );
471 // if( !in.isEmpty() || !out.isEmpty() )
472 // emit pl->queueChanged( in, out );
473 // pl->setFilter( "" );
474 // pl->ensureItemCentered( ( e->modifiers() & Qt::ShiftModifier ) ? item : pl->currentTrack() );
478 // pl->setFilter( "" );
479 // if( ( e->modifiers() & Qt::ShiftModifier ) && item )
481 // pl->queue( item );
482 // pl->ensureItemCentered( item );
486 // pl->activate( item );
487 // pl->showCurrentTrack();
492 // case Qt::Key_Escape:
493 // m_searchWidget->lineEdit()->clear();
501 //following are for Playlist::instance() only
502 //we don't handle these in the playlist because often we manipulate the lineEdit too
511 return QWidget::eventFilter( o
, e
);
515 void MainWindow::closeEvent( QCloseEvent
*e
)
521 Amarok::genericEventHandler( this, e
);
526 void MainWindow::showEvent( QShowEvent
* )
529 // static bool firstTime = true;
531 // Playlist::instance()->setFocus();
532 // firstTime = false;
535 #include <qdesktopwidget.h>
536 QSize
MainWindow::sizeHint() const
538 return QApplication::desktop()->screenGeometry( (QWidget
*)this ).size() / 1.5;
542 void MainWindow::savePlaylist() const //SLOT
545 // Playlist *pl = Playlist::instance();
547 // PlaylistItem *item = pl->firstChild();
548 // if( item && !item->isVisible() )
549 // item = static_cast<PlaylistItem*>( item->itemBelow() );
551 // QString title = pl->playlistName();
553 // if( item && title == i18n( "Untitled" ) )
555 // QString artist = item->artist();
556 // QString album = item->album();
558 // bool useArtist = true, useAlbum = true;
560 // item = static_cast<PlaylistItem*>( item->itemBelow() );
562 // for( ; item; item = static_cast<PlaylistItem*>( item->itemBelow() ) )
564 // if( artist != item->artist() )
565 // useArtist = false;
566 // if( album != item->album() )
569 // if( !useArtist && !useAlbum )
573 // if( useArtist && useAlbum )
574 // title = i18n("%1 - %2", artist, album );
575 // else if( useArtist )
577 // else if( useAlbum )
581 // QString path = PlaylistDialog::getSaveFileName( title, pl->proposeOverwriteOnSave() );
583 // if( !path.isEmpty() && Playlist::instance()->saveM3U( path ) )
584 // MainWindow::self()->showBrowser( "PlaylistBrowser" );
588 void MainWindow::slotBurnPlaylist() const //SLOT
590 K3bExporter::instance()->exportCurrentPlaylist();
593 void MainWindow::slotShowCoverManager() const //SLOT
595 CoverManager::showOnce();
598 void MainWindow::slotPlayMedia() //SLOT
600 // Request location and immediately start playback
601 slotAddLocation( true );
605 void MainWindow::slotAddLocation( bool directPlay
) //SLOT
607 // open a file selector to add media to the playlist
609 //files = KFileDialog::getOpenUrls( QString::null, "*.*|" + i18n("All Files"), this, i18n("Add Media") );
610 KFileDialog
dlg( KUrl(QString()), QString("*.*|"), this );
611 dlg
.setCaption( directPlay
? i18n("Play Media (Files or URLs)") : i18n("Add Media (Files or URLs)") );
612 dlg
.setMode( KFile::Files
| KFile::Directory
);
614 files
= dlg
.selectedUrls();
615 if( files
.isEmpty() ) return;
616 const int options
= directPlay
? Playlist::Append
| Playlist::DirectPlay
: Playlist::Append
;
618 Meta::TrackList tracks
= CollectionManager::instance()->tracksForUrls( files
);
620 The::playlistModel()->insertOptioned( tracks
.takeFirst(), options
);
622 foreach( Meta::TrackPtr track
, tracks
)
624 The::playlistModel()->insertOptioned( track
, Playlist::Append
);
628 void MainWindow::slotAddStream() //SLOT
631 QString url
= KInputDialog::getText( i18n("Add Stream"), i18n("URL"), QString(), &ok
, this );
635 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( KUrl( url
) );
637 The::playlistModel()->insertOptioned( track
, Playlist::Append
|Playlist::DirectPlay
);
641 void MainWindow::playLastfmPersonal() //SLOT
643 if( !LastFm::Controller::checkCredentials() ) return;
645 const KUrl
url( QString( "lastfm://user/%1/personal" )
646 .arg( AmarokConfig::scrobblerUsername() ) );
648 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
650 The::playlistModel()->insertOptioned( track
, Playlist::Append
|Playlist::DirectPlay
);
654 void MainWindow::addLastfmPersonal() //SLOT
656 if( !LastFm::Controller::checkCredentials() ) return;
658 const KUrl
url( QString( "lastfm://user/%1/personal" )
659 .arg( AmarokConfig::scrobblerUsername() ) );
661 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
663 The::playlistModel()->insertOptioned( track
, Playlist::Append
);
667 void MainWindow::playLastfmNeighbor() //SLOT
669 if( !LastFm::Controller::checkCredentials() ) return;
671 const KUrl
url( QString( "lastfm://user/%1/neighbours" )
672 .arg( AmarokConfig::scrobblerUsername() ) );
674 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
676 The::playlistModel()->insertOptioned( track
, Playlist::Append
);
680 void MainWindow::addLastfmNeighbor() //SLOT
682 if( !LastFm::Controller::checkCredentials() ) return;
684 const KUrl
url( QString( "lastfm://user/%1/neighbours" )
685 .arg( AmarokConfig::scrobblerUsername() ) );
687 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
689 The::playlistModel()->insertOptioned( track
, Playlist::Append
);
693 void MainWindow::playLastfmCustom() //SLOT
695 const QString token
= LastFm::Controller::createCustomStation();
696 if( token
.isEmpty() ) return;
698 const KUrl
url( "lastfm://artist/" + token
+ "/similarartists" );
699 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
701 The::playlistModel()->insertOptioned( track
, Playlist::Append
|Playlist::DirectPlay
);
705 void MainWindow::addLastfmCustom() //SLOT
707 const QString token
= LastFm::Controller::createCustomStation();
708 if( token
.isEmpty() ) return;
710 const KUrl
url( "lastfm://artist/" + token
+ "/similarartists" );
711 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
713 The::playlistModel()->insertOptioned( track
, Playlist::Append
);
717 void MainWindow::playLastfmGlobaltag() //SLOT
719 if( !LastFm::Controller::checkCredentials() ) return;
721 KAction
*action
= dynamic_cast<KAction
*>( sender() );
722 if( !action
) return;
724 const QString tag
= action
->text();
725 const KUrl
url( "lastfm://globaltags/" + tag
);
726 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
728 The::playlistModel()->insertOptioned( track
, Playlist::Append
|Playlist::DirectPlay
);
732 void MainWindow::addLastfmGlobaltag() //SLOT
734 if( !LastFm::Controller::checkCredentials() ) return;
736 KAction
*action
= dynamic_cast<KAction
*>( sender() );
737 if( !action
) return;
739 const QString tag
= action
->text();
740 const KUrl
url( "lastfm://globaltags/" + tag
);
741 Meta::TrackPtr track
= CollectionManager::instance()->trackForUrl( url
);
743 The::playlistModel()->insertOptioned( track
, Playlist::Append
);
747 void MainWindow::playAudioCD() //SLOT
750 if( EngineController::engine()->getAudioCDContents(QString(), urls
) )
752 Meta::TrackList tracks
= CollectionManager::instance()->tracksForUrls( urls
);
753 if( !tracks
.isEmpty() )
754 The::playlistModel()->insertOptioned( tracks
, Playlist::Replace
);
757 { // Default behaviour
758 showBrowser( "FileBrowser" );
759 //FileBrowser *fb = static_cast<FileBrowser *>( m_browsers->at( m_browserNames.indexOf( "FileBrowser" ) ) );
760 //fb->setUrl( KUrl("audiocd:/Wav/") );
764 void MainWindow::showScriptSelector() //SLOT
766 ScriptManager::instance()->show();
767 ScriptManager::instance()->raise();
770 void MainWindow::showQueueManager() //SLOT
772 if( QueueManagerNS::QueueManager::instance() )
774 QueueManagerNS::QueueManager::instance()->raise();
778 QueueManagerNS::QueueManager dialog
;
779 if( dialog
.exec() == QDialog::Accepted
)
785 void MainWindow::showStatistics() //SLOT
787 if( Statistics::instance() ) {
788 Statistics::instance()->raise();
795 void MainWindow::slotToggleFocus() //SLOT
798 // if( m_browsers->currentWidget() && ( Playlist::instance()->hasFocus() /*|| m_searchWidget->lineEdit()->hasFocus()*/ ) )
799 // m_browsers->currentWidget()->setFocus();
802 void MainWindow::slotToggleToolbar() //SLOT
804 m_controlBar
->setVisible( !m_controlBar
->isHidden() );
805 AmarokConfig::setShowToolbar( !AmarokConfig::showToolbar() );
806 Amarok::actionCollection()->action( "toggle_toolbar" )->setText( !m_controlBar
->isHidden() ? i18n("Hide Toolbar") : i18n("Show Toolbar") );
809 void MainWindow::toolsMenuAboutToShow() //SLOT
811 Amarok::actionCollection()->action( "equalizer" )->setEnabled( !EngineController::hasEngineProperty( "HasEqualizer" ) );
812 Amarok::actionCollection()->action( "rescan_collection" )->setEnabled( !ThreadManager::instance()->isJobPending( "CollectionScanner" ) );
816 #include <kwindowsystem.h>
818 * Show/hide playlist global shortcut and PlayerWindow PlaylistButton connect to this slot
820 * 1. hidden & iconified -> deiconify & show @n
821 * 2. hidden & deiconified -> show @n
822 * 3. shown & iconified -> deiconify @n
823 * 4. shown & deiconified -> hide @n
824 * 5. don't hide if there is no tray icon or playerWindow. todo (I can't be arsed) @n
826 * @note isMinimized() can only be true if the window isVisible()
827 * this has taken me hours to get right, change at your peril!
828 * there are more contingencies than you can believe
830 void MainWindow::showHide() //SLOT
833 const KWindowInfo info
= KWindowSystem::windowInfo( winId(), 0, 0 );
834 const uint desktop
= KWindowSystem::currentDesktop();
835 const bool isOnThisDesktop
= info
.isOnDesktop( desktop
);
836 const bool isShaded
= false;
840 KWindowSystem::clearState( winId(), NET::Shaded
);
844 if( !isOnThisDesktop
)
846 KWindowSystem::setOnDesktop( winId(), desktop
);
849 else if( !info
.isMinimized() && !isShaded
) setVisible( !isVisible() );
851 if( isVisible() ) KWindowSystem::unminimizeWindow( winId() );
853 setVisible( !isVisible() );
857 void MainWindow::activate()
860 const KWindowInfo info
= KWindowSystem::windowInfo( winId(), 0, 0 );
862 if( KWindowSystem::activeWindow() != winId())
864 else if( !info
.isMinimized() )
867 KWindowSystem::activateWindow( winId() );
873 bool MainWindow::isReallyShown() const
876 const KWindowInfo info
= KWindowSystem::windowInfo( winId(), 0, 0 );
877 return !isHidden() && !info
.isMinimized() && info
.isOnDesktop( KWindowSystem::currentDesktop() );
883 void MainWindow::createActions()
885 KActionCollection
* const ac
= actionCollection();
886 const EngineController
* const ec
= EngineController::instance();
888 KStandardAction::keyBindings( kapp
, SLOT( slotConfigShortcuts() ), ac
);
889 KStandardAction::preferences( kapp
, SLOT( slotConfigAmarok() ), ac
);
890 ac
->action(KStandardAction::name(KStandardAction::KeyBindings
))->setIcon( KIcon( Amarok::icon( "configure" ) ) );
891 ac
->action(KStandardAction::name(KStandardAction::Preferences
))->setIcon( KIcon( Amarok::icon( "configure" ) ) );
893 KStandardAction::quit( kapp
, SLOT( quit() ), ac
);
895 KAction
*action
= new KAction( KIcon( Amarok::icon( "files" ) ), i18n("&Add Media..."), this );
896 connect( action
, SIGNAL( triggered(bool) ), this, SLOT( slotAddLocation() ) );
897 ac
->addAction( "playlist_add", action
);
899 action
= new KAction( KIcon( Amarok::icon( "playlist_clear" ) ), i18nc( "clear playlist", "&Clear" ), this );
900 connect( action
, SIGNAL( triggered( bool ) ), The::playlistModel(), SLOT( clear() ) );
901 ac
->addAction( "playlist_clear", action
);
903 action
= new KAction( KIcon( Amarok::icon( "files" ) ), i18n("&Add Stream..."), this );
904 connect( action
, SIGNAL( triggered(bool) ), this, SLOT( slotAddStream() ) );
905 ac
->addAction( "stream_add", action
);
907 action
= new KAction( KIcon( Amarok::icon( "save" ) ), i18n("&Save Playlist As..."), this );
908 connect( action
, SIGNAL( triggered(bool) ), this, SLOT( savePlaylist() ) );
909 ac
->addAction( "playlist_save", action
);
911 KAction
*burn
= new KAction( KIcon(Amarok::icon( "burn" )), i18n( "Burn Current Playlist" ), this );
912 connect( burn
, SIGNAL( triggered(bool) ), SLOT( slotBurnPlaylist() ) );
913 burn
->setEnabled( K3bExporter::isAvailable() );
914 ac
->addAction( "playlist_burn", burn
);
916 KAction
*covermanager
= new KAction( KIcon(Amarok::icon( "covermanager" )), i18n( "Cover Manager" ), this );
917 connect( covermanager
, SIGNAL( triggered(bool) ), SLOT( slotShowCoverManager() ) );
918 ac
->addAction( "cover_manager", covermanager
);
920 KAction
*visuals
= new KAction( KIcon( Amarok::icon("visualizations") ), i18n("&Visualizations"), this );
921 // connect( visuals, SIGNAL( triggered(bool) ), Vis::Selector::instance(), SLOT( show() ) );
922 ac
->addAction( "visualizations", visuals
);
924 KAction
*equalizer
= new KAction( KIcon( Amarok::icon( "equalizer" ) ), i18n( "E&qualizer"), this );
925 connect( equalizer
, SIGNAL( triggered(bool) ), kapp
, SLOT( slotConfigEqualizer() ) );
926 ac
->addAction( "equalizer", equalizer
);
928 KAction
*toggleToolbar
= new KAction( this );
929 toggleToolbar
->setText( i18n("Hide Toolbar") );
931 //FIXME m_controlBar is initialised after the actions are created so we need to change the text of this action
932 //when the menu is shown
933 //toggleToolbar->setText( !m_controlBar->isHidden() ? i18n("Hide Toolbar") : i18n("Show Toolbar") );
934 connect( toggleToolbar
, SIGNAL( triggered(bool) ), SLOT( slotToggleToolbar() ) );
935 ac
->addAction( "toggle_toolbar", toggleToolbar
);
937 // KAction *update_podcasts = new KAction( this );
938 // update_podcasts->setText( i18n( "Update Podcasts" ) );
939 // //update_podcasts->setIcon( KIcon(Amarok::icon( "refresh" )) );
940 // ac->addAction( "podcasts_update", update_podcasts );
941 // connect(update_podcasts, SIGNAL(triggered(bool)),
942 // The::podcastCollection(), SLOT(slotUpdateAll()));
944 KAction
*playact
= new KAction( KIcon(Amarok::icon( "files" )), i18n("Play Media..."), this );
945 connect(playact
, SIGNAL(triggered(bool)), SLOT(slotPlayMedia()));
946 ac
->addAction( "playlist_playmedia", playact
);
948 KAction
*acd
= new KAction( KIcon( Amarok::icon( "album" ) ), i18n("Play Audio CD"), this );
949 connect(acd
, SIGNAL(triggered(bool)), SLOT(playAudioCD()));
950 ac
->addAction( "play_audiocd", acd
);
952 KAction
*script
= new KAction( KIcon(Amarok::icon( "scripts" )), i18n("Script Manager"), this );
953 connect(script
, SIGNAL(triggered(bool)), SLOT(showScriptSelector()));
954 ac
->addAction( "script_manager", script
);
956 KAction
*queue
= new KAction( KIcon( Amarok::icon( "queue" )), i18n( "Queue Manager" ), this );
957 connect(queue
, SIGNAL(triggered(bool)), SLOT(showQueueManager()));
958 ac
->addAction( "queue_manager", queue
);
960 KAction
*seekForward
= new KAction( KIcon( Amarok::icon( "fastforward" ) ), i18n("&Seek Forward"), this );
961 seekForward
->setShortcut( Qt::Key_Right
);
962 connect(seekForward
, SIGNAL(triggered(bool)), ec
, SLOT(seekForward()));
963 ac
->addAction( "seek_forward", seekForward
);
965 KAction
*seekBackward
= new KAction( KIcon( Amarok::icon( "rewind" ) ), i18n("&Seek Backward"), this );
966 seekForward
->setShortcut( Qt::Key_Left
);
967 connect(seekForward
, SIGNAL(triggered(bool)), ec
, SLOT(seekBackward()));
968 ac
->addAction( "seek_backward", seekBackward
);
970 KAction
*statistics
= new KAction( KIcon(Amarok::icon( "info" )), i18n( "Statistics" ), this );
971 connect(statistics
, SIGNAL(triggered(bool)), SLOT(showStatistics()));
972 ac
->addAction( "statistics", statistics
);
974 KAction
*update
= new KAction( KIcon(Amarok::icon( "refresh")), i18n( "Update Collection" ), this );
975 connect(update
, SIGNAL(triggered(bool)), CollectionDB::instance(), SLOT(scanModifiedDirs()));
976 ac
->addAction( "update_collection", update
);
978 KAction
*rescan
= new KAction( KIcon(Amarok::icon( "rescan")), i18n( "Rescan Collection" ), this );
979 connect(update
, SIGNAL(triggered(bool)), CollectionDB::instance(), SLOT(startScan()));
980 rescan
->setEnabled( !ThreadManager::instance()->isJobPending( "CollectionScanner" ) );
981 ac
->addAction( "rescan_collection", rescan
);
983 m_lastfmTags
<< "Alternative" << "Ambient" << "Chill Out" << "Classical" << "Dance"
984 << "Electronica" << "Favorites" << "Heavy Metal" << "Hip Hop" << "Indie Rock"
985 << "Industrial" << "Japanese" << "Pop" << "Psytrance" << "Rap" << "Rock"
986 << "Soundtrack" << "Techno" << "Trance";
988 KMenu
* playTagRadioMenu
= new KMenu( this );
989 playTagRadioMenu
->setTitle( i18n( "Global Tag Radio" ) );
990 foreach( QString lastfmTag
, m_lastfmTags
)
992 KAction
*lastfmAction
= new KAction( lastfmTag
, this );
993 connect( lastfmAction
, SIGNAL( triggered(bool) ), SLOT( playLastfmGlobaltag() ) );
994 playTagRadioMenu
->addAction( lastfmAction
);
997 KMenu
* addTagRadioMenu
= new KMenu( this );
998 addTagRadioMenu
->setTitle( i18n( "Global Tag Radio" ) );
999 foreach( QString lastfmTag
, m_lastfmTags
)
1001 KAction
*lastfmAction
= new KAction( lastfmTag
, this );
1002 connect( lastfmAction
, SIGNAL( triggered(bool) ), SLOT( addLastfmGlobaltag() ) );
1003 addTagRadioMenu
->addAction( lastfmAction
);
1006 KActionMenu
* playLastfm
= new KActionMenu( KIcon(Amarok::icon("audioscrobbler")), i18n( "Play las&t.fm Stream" ), ac
);
1007 KMenu
* playLastfmMenu
= playLastfm
->menu();
1008 playLastfmMenu
->addAction( i18n( "Personal Radio" ), this, SLOT( playLastfmPersonal() ) );
1009 playLastfmMenu
->addAction( i18n( "Neighbor Radio" ), this, SLOT( playLastfmNeighbor() ) );
1010 playLastfmMenu
->addAction( i18n( "Custom Station" ), this, SLOT( playLastfmCustom() ) );
1011 playLastfmMenu
->addMenu( playTagRadioMenu
);
1012 ac
->addAction( "lastfm_play", playLastfm
);
1014 KActionMenu
* addLastfm
= new KActionMenu( KIcon(Amarok::icon("audioscrobbler")), i18n( "Add las&t.fm Stream" ), ac
);
1015 KMenu
* addLastfmMenu
= addLastfm
->menu();
1016 addLastfmMenu
->addAction( i18n( "Personal Radio" ), this, SLOT( addLastfmPersonal() ) );
1017 addLastfmMenu
->addAction( i18n( "Neighbor Radio" ), this, SLOT( addLastfmNeighbor() ) );
1018 addLastfmMenu
->addAction( i18n( "Custom Station" ), this, SLOT( addLastfmCustom() ) );
1019 addLastfmMenu
->addMenu( addTagRadioMenu
);
1020 ac
->addAction( "lastfm_add", addLastfm
);
1022 KAction
*previous
= new KAction( this );
1023 previous
->setIcon( KIcon(Amarok::icon( "back" )) );
1024 previous
->setText( i18n( "Previous Track" ) );
1025 ac
->addAction( "prev", previous
);
1026 connect( previous
, SIGNAL(triggered(bool)), The::playlistModel(), SLOT( back() ) );
1028 KAction
*play
= new KAction( this );
1029 play
->setIcon( KIcon(Amarok::icon( "play" )) );
1030 play
->setText( i18n( "Play" ) );
1031 ac
->addAction( "play", play
);
1032 connect( play
, SIGNAL(triggered(bool)), ec
, SLOT( play() ));
1034 KAction
*pause
= new KAction( this );
1035 pause
->setIcon( KIcon(Amarok::icon( "pause" )) );
1036 pause
->setText( i18n( "Pause" ));
1037 ac
->addAction( "pause", pause
);
1038 connect( pause
, SIGNAL(triggered(bool)), ec
, SLOT( pause() ) );
1040 KAction
*next
= new KAction( this );
1041 next
->setIcon( KIcon(Amarok::icon( "next" )) );
1042 next
->setText( i18n( "Next Track" ) );
1043 ac
->addAction( "next", next
);
1044 connect( next
, SIGNAL(triggered(bool)), The::playlistModel(), SLOT( next() ) );
1046 KAction
*toggleFocus
= new KAction(i18n( "Toggle Focus" ), ac
);
1047 toggleFocus
->setShortcut( Qt::ControlModifier
+ Qt::Key_Tab
);
1048 connect( toggleFocus
, SIGNAL(triggered(bool)), SLOT( slotToggleFocus() ));
1050 new Amarok::MenuAction( ac
);
1051 new Amarok::StopAction( ac
);
1052 new Amarok::PlayPauseAction( ac
);
1053 new Amarok::RepeatAction( ac
);
1054 new Amarok::RandomAction( ac
);
1055 new Amarok::FavorAction( ac
);
1057 if( K3bExporter::isAvailable() )
1058 new Amarok::BurnMenuAction( ac
);
1060 ac
->setAssociatedWidget( this );
1063 void MainWindow::createMenus()
1065 m_menubar
= menuBar();//new MenuBar( this );
1067 //BEGIN Actions menu
1068 KMenu
*actionsMenu
= new KMenu( m_menubar
);
1069 actionsMenu
->setTitle( i18n("&Amarok") );
1070 actionsMenu
->addAction( actionCollection()->action("playlist_playmedia") );
1071 actionsMenu
->addAction( actionCollection()->action("lastfm_play") );
1072 actionsMenu
->addAction( actionCollection()->action("play_audiocd") );
1073 actionsMenu
->addSeparator();
1074 actionsMenu
->addAction( actionCollection()->action("prev") );
1075 actionsMenu
->addAction( actionCollection()->action("play_pause") );
1076 actionsMenu
->addAction( actionCollection()->action("stop") );
1077 actionsMenu
->addAction( actionCollection()->action("next") );
1078 actionsMenu
->addSeparator();
1079 actionsMenu
->addAction( actionCollection()->action(KStandardAction::name(KStandardAction::Quit
)) );
1082 //BEGIN Playlist menu
1083 KMenu
*playlistMenu
= new KMenu( m_menubar
);
1084 playlistMenu
->setTitle( i18n("&Playlist") );
1085 playlistMenu
->addAction( actionCollection()->action("playlist_add") );
1086 playlistMenu
->addAction( actionCollection()->action("stream_add") );
1087 playlistMenu
->addAction( actionCollection()->action("lastfm_add") );
1088 playlistMenu
->addAction( actionCollection()->action("playlist_save") );
1089 playlistMenu
->addAction( actionCollection()->action("playlist_burn") );
1090 playlistMenu
->addAction( actionCollection()->action("podcasts_update") );
1091 playlistMenu
->addSeparator();
1092 playlistMenu
->addAction( actionCollection()->action("playlist_undo") );
1093 playlistMenu
->addAction( actionCollection()->action("playlist_redo") );
1094 playlistMenu
->addSeparator();
1095 playlistMenu
->addAction( actionCollection()->action("playlist_clear") );
1096 playlistMenu
->addAction( actionCollection()->action("playlist_shuffle") );
1098 playlistMenu
->addSeparator();
1099 playlistMenu
->addAction( actionCollection()->action("queue_selected") );
1100 playlistMenu
->addAction( actionCollection()->action("playlist_remove_duplicates") );
1101 playlistMenu
->addAction( actionCollection()->action("playlist_select_all") );
1106 KMenu
*modeMenu
= new KMenu( m_menubar
);
1107 modeMenu
->setTitle( i18n("&Mode") );
1108 QAction
*repeat
= actionCollection()->action("repeat");
1109 connect( repeat
, SIGNAL(triggered( int ) ), The::playlistModel(), SLOT(playlistRepeatMode(int) ) );
1110 modeMenu
->addAction( repeat
);
1111 KSelectAction
*random
= static_cast<KSelectAction
*>( actionCollection()->action("random_mode") );
1112 modeMenu
->addAction( random
);
1113 random
->menu()->addSeparator();
1114 random
->menu()->addAction( actionCollection()->action("favor_tracks") );
1118 m_toolsMenu
= new KMenu( m_menubar
);
1119 m_toolsMenu
->setTitle( i18n("&Tools") );
1120 m_toolsMenu
->addAction( actionCollection()->action("cover_manager") );
1121 m_toolsMenu
->addAction( actionCollection()->action("queue_manager") );
1122 m_toolsMenu
->addAction( actionCollection()->action("visualizations") );
1123 m_toolsMenu
->addAction( actionCollection()->action("equalizer") );
1124 m_toolsMenu
->addAction( actionCollection()->action("script_manager") );
1125 m_toolsMenu
->addAction( actionCollection()->action("statistics") );
1126 m_toolsMenu
->addSeparator();
1127 m_toolsMenu
->addAction( actionCollection()->action("update_collection") );
1128 m_toolsMenu
->addAction( actionCollection()->action("rescan_collection") );
1130 #ifndef HAVE_LIBVISUAL
1131 actionCollection()->action( "visualizations" )->setEnabled( false );
1134 connect( m_toolsMenu
, SIGNAL( aboutToShow() ), SLOT( toolsMenuAboutToShow() ) );
1135 connect( m_toolsMenu
, SIGNAL( activated(int) ), SLOT( slotMenuActivated(int) ) );
1138 //BEGIN Settings menu
1139 m_settingsMenu
= new KMenu( m_menubar
);
1140 m_settingsMenu
->setTitle( i18n("&Settings") );
1141 //TODO use KStandardAction or KXmlGuiWindow
1143 m_settingsMenu
->addAction( actionCollection()->action( "toggle_toolbar" ) );
1144 m_settingsMenu
->addSeparator();
1148 // plug it first, as this item will be moved to the applications first menu
1149 m_settingsMenu
->addAction( actionCollection()->action(KStandardAction::name(KStandardAction::Preferences
)) );
1151 // m_settingsMenu->addAction( actionCollection()->action("options_configure_globals") );
1152 m_settingsMenu
->addAction( actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings
)) );
1153 m_settingsMenu
->addAction( actionCollection()->action(KStandardAction::name(KStandardAction::ConfigureToolbars
)) );
1154 m_settingsMenu
->addAction( actionCollection()->action(KStandardAction::name(KStandardAction::Preferences
)) );
1156 connect( m_settingsMenu
, SIGNAL( activated(int) ), SLOT( slotMenuActivated(int) ) );
1159 m_menubar
->addMenu( actionsMenu
);
1160 m_menubar
->addMenu( playlistMenu
);
1161 m_menubar
->addMenu( modeMenu
);
1162 m_menubar
->addMenu( m_toolsMenu
);
1163 m_menubar
->addMenu( m_settingsMenu
);
1164 m_menubar
->addMenu( Amarok::Menu::helpMenu() );
1169 #include "MainWindow.moc"