~ubuntu-branches/ubuntu/lucid/lastfm/lucid

« back to all changes in this revision

Viewing changes to src/container.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Pedro Fragoso
  • Date: 2007-12-31 09:49:54 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20071231094954-ix1amvcsj9pk61ya
Tags: 1:1.4.1.57486.dfsg-1ubuntu1
* Merge from Debian unstable (LP: #180254), remaining changes:
  - debian/rules;
    - Added dh_icons
  - Modify Maintainer value to match Debian-Maintainer-Field Spec

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
 *      Christian Muehlhaeuser, Last.fm Ltd <chris@last.fm>                *
4
4
 *      Erik Jaelevik, Last.fm Ltd <erik@last.fm>                          *
5
5
 *      Max Howell, Last.fm Ltd <max@last.fm>                              *
 
6
 *      Jono Cole, Last.fm Ltd <jono@last.fm>                              *
6
7
 *                                                                         *
7
8
 *   This program is free software; you can redistribute it and/or modify  *
8
9
 *   it under the terms of the GNU General Public License as published by  *
17
18
 *   You should have received a copy of the GNU General Public License     *
18
19
 *   along with this program; if not, write to the                         *
19
20
 *   Free Software Foundation, Inc.,                                       *
20
 
 *   51 Franklin Steet, Fifth Floor, Boston, MA  02111-1307, USA.          *
 
21
 *   51 Franklin Steet, Fifth Floor, Boston, MA  02110-1301, USA.          *
21
22
 ***************************************************************************/
22
23
 
 
24
#include "container.h"
 
25
 
23
26
#include "aboutdialog.h"
24
27
#include "autoupdater.h"
25
 
#include "browserthread.h"
26
28
#include "configwizard.h"
27
29
#include "confirmdialog.h"
28
 
#include "container.h"
29
 
#include "containerutils.h"
30
 
#include "controlinterface.h"
 
30
#include "MooseCommon.h"
31
31
#include "deleteuserdialog.h"
 
32
#include "failedlogindialog.h"
32
33
#include "interfaces/ExtensionInterface.h"
33
34
#include "iconshack.h"
34
 
#include "interfaces/InputInterface.h"
35
 
#include "itunesscript.h"
36
 
#include "logindialog.h"
37
 
#include "macstyleoverrides.h"
38
 
#include "mediadevices/mediadevice.h"
 
35
#include "lastfmapplication.h"
 
36
#include "LastMessageBox.h"
 
37
#include "logger.h"
 
38
#include "loginwidget.h"
 
39
#include "mediadevices/ipod/IpodDevice.h"
39
40
#include "metadata.h"
40
 
#include "interfaces/InputInterface.h"
 
41
#include "MetaDataWidget.h"
41
42
#include "playerlistener.h"
42
43
#include "Radio.h"
43
 
#include "recommenddialog.h"
 
44
#include "ShareDialog.h"
44
45
#include "settingsdialog.h"
 
46
#include "DiagnosticsDialog.h"
45
47
#include "Scrobbler-1.2.h"
46
 
#include "Settings.h"
 
48
#include "LastFmSettings.h"
 
49
#include "SideBarView.h"
47
50
#include "systray.h"
48
51
#include "tagdialog.h"
49
 
#include "interfaces/TranscodeInterface.h"
50
 
#include "recommenddialog.h"
51
52
#include "RestStateWidget.h"
52
53
#include "updatewizard.h"
 
54
#include "User.h"
53
55
#include "volumeslider.h"
54
56
#include "WebService.h"
55
57
#include "WebService/Request.h"
56
 
#include "winstyleoverrides.h"
57
 
#include "winutils.h"
58
 
#include "utils.h"
59
 
#include "LastMessageBox.h"
60
 
 
61
 
#include "sidebarextension/sidebartree.h"
62
 
#include "searchextension/searchextension.h"
63
 
#include "metadataextension/metadataextension.h"
64
 
 
65
 
Container *Container::s_instance = 0;
66
 
static const int k_statusBarFontSizeMac = 10;
67
 
 
68
 
// Station bar grey bg colour
69
 
static const QColor k_stationBarGreyTop = QColor( 0xba, 0xba, 0xba, 0xff );
70
 
static const QColor k_stationBarGreyMiddle = QColor( 0xe2, 0xe2, 0xe2, 0xff );
71
 
static const QColor k_stationBarGreyBottom = QColor( 0xff, 0xff, 0xff, 0xff );
72
 
 
73
 
// Track bar blue bg colour
74
 
static const QColor k_trackBarBkgrBlueTop = QColor( 0xeb, 0xf0, 0xf2, 0xff );
75
 
static const QColor k_trackBarBkgrBlueMiddle = QColor( 0xe5, 0xe9, 0xec, 0xff );
76
 
static const QColor k_trackBarBkgrBlueBottom = QColor( 0xdc, 0xe2, 0xe5, 0xff );
77
 
 
78
 
// Track bar progress bar colour
79
 
static const QColor k_trackBarProgressTop = QColor( 0xd6, 0xde, 0xe6, 0xff );
80
 
static const QColor k_trackBarProgressMiddle = QColor( 0xd0, 0xd9, 0xe2, 0xff );
81
 
static const QColor k_trackBarProgressBottom = QColor( 0xca, 0xd4, 0xdc, 0xff );
82
 
 
83
 
// Track bar scrobbled colour
84
 
static const QColor k_trackBarScrobbledTop = QColor( 0xba, 0xc7, 0xd7, 0xff );
85
 
static const QColor k_trackBarScrobbledMiddle = QColor( 0xb8, 0xc4, 0xd5, 0xff );
86
 
static const QColor k_trackBarScrobbledBottom = QColor( 0xb5, 0xc1, 0xd2, 0xff );
 
58
 
 
59
#include <QShortcut>
 
60
#include <QLabel>
 
61
#include <QCloseEvent>
 
62
#include <QDragMoveEvent>
 
63
#include <QDesktopServices>
 
64
#include <QFileDialog>
 
65
 
 
66
#ifndef Q_WS_MAC
 
67
    #include "winstyleoverrides.h"
 
68
#else
 
69
    #include "macstyleoverrides.h"
 
70
#endif
 
71
 
 
72
Container* Container::s_instance = 0;
87
73
 
88
74
 
89
75
Container::Container()
90
 
        : QMainWindow()
91
 
        , m_eUserInfo( 0 )
92
 
        , m_recommendDlg( new RecommendDialog( this ) )
93
 
        , m_userCheck( false )
94
 
        , m_sidebarEnabled( false )
95
 
        , m_stopped( true ) 
96
 
        , m_closingDown( false )
 
76
        : QMainWindow(),
 
77
          m_userCheck( false ),
 
78
          m_sidebarEnabled( false ),
 
79
          m_sidebarWidth( 190 )
 
80
#ifndef Q_WS_MAC
 
81
        , m_styleOverrides( 0 )
 
82
#endif
97
83
{
98
 
    //TODO REFACTOR! The size of this monster is scary.
99
 
 
100
 
    // HACK: this is just to make sure the web service gets created as part of
101
 
    // the main GUI thread. If we don't do this, it will get created as part
102
 
    // of the HttpInput initialisation and then it won't work because it's
103
 
    // done from a different thread.
104
 
    The::webService();
105
 
 
106
84
    s_instance = this;
107
 
 
108
 
    m_iControl = new ControlInterface( this );
109
 
    m_listener = new CPlayerListener( this );
 
85
    m_shareDialog = new ShareDialog( this );
110
86
    m_settingsDialog = new SettingsDialog( this );
 
87
    m_diagnosticsDialog = new DiagnosticsDialog( this );
111
88
    m_updater = new CAutoUpdater( this );
112
89
 
113
 
    m_scrobbler = new ScrobblerManager( this );
114
 
    connect( m_scrobbler, SIGNAL(status( int, QVariant )), SLOT(onScrobblerStatusChange( int, QVariant )) );
115
 
 
116
 
  #if QT_VERSION >= 0x040300
117
 
    setUnifiedTitleAndToolBarOnMac( true );
118
 
  #endif
119
 
 
120
 
    setWindowTitle( "Last.fm" ); //don't translate this!
121
 
    m_radio = new Radio( this );
122
 
 
 
90
    setupUi();
 
91
    setupTimeBar();
 
92
    setupTrayIcon();
 
93
    updateAppearance();
 
94
    applyPlatformSpecificTweaks();
 
95
    loadExtensions();
 
96
    setupConnections();
 
97
    restoreState();
 
98
}
 
99
 
 
100
 
 
101
void
 
102
Container::setupUi()
 
103
{
123
104
    ui.setupUi( this );
124
 
  #ifdef Q_WS_X11
125
 
    setWindowIcon( QIcon( dataPath( "icons/as.png" ) ) );
126
 
  #endif
127
 
    setMinimumSize( 551, 400 );
128
 
    setAcceptDrops( true );
129
 
 
130
 
    // Start helper daemon _before_ we bind any sockets, otherwise they might
131
 
    // end up being blocked by the child-process.
132
 
    installHelperApp();
133
 
 
134
 
//////
135
 
    // don't use CTRL as that varies by platform, it is easier to have a single
136
 
    // shortcut to tell all users
137
 
    QObject* openL = new QShortcut( QKeySequence( "Alt+Shift+L" ), this );
138
 
    QObject* openH = new QShortcut( QKeySequence( "Alt+Shift+H" ), this );
139
 
    QObject* openF = new QShortcut( QKeySequence( "Alt+Shift+F" ), this );
140
 
    
141
 
    connect( openL, SIGNAL(activated()), SLOT(onAltShiftL()) );
142
 
    connect( openH, SIGNAL(activated()), SLOT(onAltShiftH()) );
143
 
    connect( openF, SIGNAL(activated()), SLOT(onAltShiftF()) );
144
 
 
145
 
//////
146
 
    loadExtensions();
147
 
    initListener();
148
 
 
149
 
    // <ToolBar>
150
 
    QToolBar* toolbar = ui.toolbar = addToolBar( "Last.fm" );
151
 
    toolbar->setObjectName( "Lastfm_Main_Toolbar" );
152
 
    toolbar->setMovable( false );
153
 
    toolbar->setContextMenuPolicy( Qt::CustomContextMenu );
154
 
    toolbar->setToolButtonStyle( Qt::ToolButtonTextUnderIcon );
155
 
  #if defined( Q_WS_MAC ) && QT_VERSION >= 0x040300
156
 
        // using unified toolbar so don't do this
157
 
  #else
158
 
        toolbar->setAutoFillBackground( true );
159
 
  #endif
160
 
 
161
 
  #ifdef WIN32
162
 
        // Can't use 32 for height as labels get truncated on Win classic
163
 
        toolbar->setIconSize( QSize( 50, 34 ) );
164
 
  #else
165
 
        toolbar->setIconSize( QSize( 32, 32 ) );
166
 
  #endif
167
 
 
168
 
    ui.actionSidebar->setShortcut( tr( "Ctrl+F" ) );
169
 
    ui.actionSidebar->setToolTip( tr( "Show/hide my profile" ) );
170
 
  #ifdef WIN32
171
 
    ui.actionSidebar->setCheckable( true );
172
 
  #endif
173
 
    connect( ui.actionSidebar, SIGNAL(triggered()), SLOT(toggleSidebar()) );
174
 
 
175
 
    // Copy the menu action and add the icon to it. Don't want the icon in the
176
 
    // menu as it will obscure the tick.
177
 
    m_sidebarActionWithIcon = new QAction( toolbar );
178
 
    m_sidebarActionWithIcon->setIcon( QIcon( dataPath( "buttons/myprofile.png" ) ) );
179
 
    m_sidebarActionWithIcon->setText( tr( "My Profile" ) );
180
 
    m_sidebarActionWithIcon->setToolTip( tr( "Show/hide my profile" ) );
181
 
  #ifdef WIN32
182
 
    m_sidebarActionWithIcon->setCheckable( true );
183
 
  #endif
184
 
    connect( m_sidebarActionWithIcon, SIGNAL(triggered()), SLOT(toggleSidebar()) );
185
 
 
186
 
    toolbar->addAction( m_sidebarActionWithIcon );
187
 
    toolbar->addSeparator();
188
 
 
189
 
    //m_recommendAction  = toolbar->addAction( QIcon( dataPath( "buttons/recommend.png" ) ), tr( "Recommend" ), this, SLOT( showRecommendDialog() ) );
190
 
    ui.actionRecommend->setIcon( QIcon( dataPath( "buttons/recommend.png" ) ) );
191
 
    ui.actionRecommend->setShortcut( tr( "Ctrl+R" ) );
192
 
    ui.actionRecommend->setToolTip( tr( "Share what's currently playing with someone" ) );
193
 
    toolbar->addAction( ui.actionRecommend );
194
 
    connect( ui.actionRecommend, SIGNAL(triggered()), SLOT(showRecommendDialog()) );
195
 
 
196
 
    //m_tagAction = toolbar->addAction( QIcon( dataPath( "buttons/tag.png" ) ), tr( "Tag" ), this, SLOT( showTagDialog() ) );
197
 
    ui.actionTag->setIcon( QIcon( dataPath( "buttons/tag.png" ) ) );
198
 
    ui.actionTag->setShortcut( tr( "Ctrl+T" ) );
199
 
    ui.actionTag->setToolTip( tr( "Tag what's currently playing" ) );
200
 
    toolbar->addAction( ui.actionTag );
201
 
    connect( ui.actionTag, SIGNAL(triggered()), SLOT(showTagDialog()) );
202
 
 
203
 
    ui.actionAddToMyPlaylist->setIcon( QIcon( dataPath( "buttons/addToMyPlaylist.png" ) ) );
204
 
    ui.actionAddToMyPlaylist->setShortcut( tr( "Ctrl+P" ) );
205
 
    ui.actionAddToMyPlaylist->setToolTip( tr( "Add what's currently playing to your playlist" ) );
206
 
    toolbar->addAction( ui.actionAddToMyPlaylist );
207
 
    connect( ui.actionAddToMyPlaylist, SIGNAL( triggered() ),
208
 
             this,                     SLOT( addToMyPlaylist() ) );
209
 
 
210
 
    toolbar->addSeparator();
211
 
 
212
 
    //m_loveAction = toolbar->addAction( QIcon( dataPath( "buttons/love.png" ) ), tr( "Love" ), this, SLOT( love() ) );
213
 
    ui.actionLove->setIcon( QIcon( dataPath( "buttons/love.png" ) ) );
214
 
    ui.actionLove->setShortcut( tr( "Ctrl+L" ) );
215
 
    ui.actionLove->setToolTip( tr( "Express your love for the track that's playing" ) );
216
 
    toolbar->addAction( ui.actionLove );
217
 
    connect( ui.actionLove, SIGNAL( triggered() ),
218
 
             this,          SLOT( love() ) );
219
 
 
220
 
    //m_banAction  = toolbar->addAction( QIcon( dataPath( "buttons/ban.png" ) ), tr( "Ban" ), this, SLOT( ban() ) );
221
 
    ui.actionBan->setIcon( QIcon( dataPath( "buttons/ban.png" ) ) );
222
 
    ui.actionBan->setShortcut( tr( "Ctrl+B" ) );
223
 
    ui.actionBan->setToolTip( tr( "Don't ever play me this again" ) );
224
 
    toolbar->addAction( ui.actionBan );
225
 
    connect( ui.actionBan, SIGNAL( triggered() ),
226
 
             this,         SLOT( ban() ) );
227
 
 
228
 
    //m_skipAction = new QAction( QIcon( dataPath( "buttons/skip.png" ) ), tr( "Skip" ), this );
229
 
    ui.actionSkip->setIcon( QIcon( dataPath( "buttons/skip.png" ) ) );
230
 
    ui.actionSkip->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_Right ) ); //NOTE don't translate
231
 
    ui.actionSkip->setToolTip( tr( "Skip track" ) );
232
 
 
233
 
    //m_playAction  = new QAction( QIcon( dataPath( "buttons/play.png" ) ), tr( "Play" ), this );
234
 
    ui.actionPlay->setIcon( QIcon( dataPath( "buttons/play.png" ) ) );
235
 
    ui.actionPlay->setShortcut( Qt::Key_Space );
236
 
    ui.actionPlay->setToolTip( tr( "Resume last radio station" ) );
237
 
 
238
 
    //m_stopAction  = new QAction( QIcon( dataPath( "buttons/stop.png" ) ), tr( "Stop" ), this );
239
 
    ui.actionStop->setIcon( QIcon( dataPath( "buttons/stop.png" ) ) );
240
 
    ui.actionStop->setToolTip( tr( "Stop radio" ) );
241
 
 
242
 
    toolbar->addSeparator();
243
 
 
244
 
    toolbar->addAction( ui.actionPlay );
245
 
    toolbar->addAction( ui.actionStop );
246
 
    toolbar->addAction( ui.actionSkip );
247
 
 
248
 
  #ifdef Q_WS_MAC
249
 
    //ui.actionSettings->setText( tr( "Options" ) );
250
 
  #endif
251
 
    ui.actionSettings->setMenuRole( QAction::PreferencesRole );
252
 
    ui.actionSettings->setShortcut( tr( "Ctrl+O" ) );
253
 
    ui.actionToggleScrobbling->setShortcut( tr( "Ctrl+E" ) );
254
 
 
255
 
    // Help shortcut
256
 
  #ifdef Q_WS_MAC
257
 
    ui.actionFAQ->setShortcut( tr( "Ctrl+?" ) );
258
 
  #else
259
 
    ui.actionFAQ->setShortcut( tr( "F1") );
260
 
  #endif
261
 
 
262
 
    // Workaround for showing "Space" shortcut in the Mac menu    
263
 
  #ifdef Q_WS_MAC
264
 
    ui.actionPlay->setIconText( ui.actionPlay->text().replace( "&", "" ) );
265
 
    ui.actionStop->setIconText( ui.actionStop->text().replace( "&", "" ) );
266
 
 
267
 
    // For Systray we don't want to display the hotkey in the menu
268
 
    m_sysTrayActionPlay = new QAction( this );
269
 
    m_sysTrayActionStop = new QAction( this );
270
 
    m_sysTrayActions = new QActionGroup( this );
271
 
 
272
 
    m_sysTrayActions->addAction( m_sysTrayActionPlay );
273
 
    m_sysTrayActions->addAction( m_sysTrayActionStop );
274
 
 
275
 
    connect( m_sysTrayActionPlay,           SIGNAL( triggered() ),
276
 
             this,                            SLOT( play() ) );
277
 
    connect( m_sysTrayActionStop,           SIGNAL( triggered() ),
278
 
             this,                            SLOT( stop() ) );
279
 
 
280
 
    m_sysTrayActionPlay->setText( ui.actionPlay->text() ); 
281
 
    m_sysTrayActionStop->setText( ui.actionStop->text() );
282
 
    m_sysTrayActionPlay->setIcon( ui.actionPlay->icon() );
283
 
    m_sysTrayActionStop->setIcon( ui.actionStop->icon() );
284
 
 
285
 
    // the spaces align the word space to the right of the menu, there is apparently
286
 
    // no alternative on mac osx 10.4 and below. Without the extra text the space
287
 
    // shortcut is rendered as ' '. Sweet!
288
 
    ui.actionPlay->setText( ui.actionPlay->text() + "            " + tr("Space" ) );
289
 
    ui.actionStop->setText( ui.actionStop->text() + "            " + tr("Space" ) );
290
 
  #endif
291
105
 
292
106
    ui.actionCheckForUpdates->setMenuRole( QAction::ApplicationSpecificRole );
293
107
    ui.actionAboutLastfm->setMenuRole( QAction::AboutRole );
294
 
 
295
 
  #ifdef Q_WS_MAC
296
 
    ui.menuHelp->insertAction( ui.actionCheckForUpdates, ui.actionAboutLastfm ); // Change the order of these 2 entries on the mac
297
 
    
298
 
    //This menu item will not actually be displayed in the tools menu but will be inserted in the mac application menu.
299
 
    //This is done automatically by QT with any menu item named Exit or Quit
300
 
    ui.menuTools->addAction( tr( "Exit" ), this, SLOT( actualClose( ) ) );
301
 
  #else
302
 
    // Additional seperators for menu entries for the other platforms
303
 
    ui.menuHelp->insertSeparator( ui.actionCheckForUpdates );
304
 
    ui.menuHelp->insertSeparator( ui.actionAboutLastfm );
305
 
  #endif
306
 
 
307
 
    //NOTE don't translate these! It breaks the shortcut due to the arrow key string getting translated
308
 
    ui.actionVolumeUp->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_Up ) );
309
 
    ui.actionVolumeDown->setShortcut( Qt::CTRL + Qt::Key_Down );
310
 
  #ifdef Q_WS_MAC
311
 
    ui.actionMute->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_Down );
312
 
  #else
313
 
    ui.actionMute->setShortcut( tr( "Ctrl+M" ) );
314
 
  #endif
315
 
 
316
 
  #ifdef Q_WS_X11
317
 
    delete ui.actionCheckForUpdates;
318
 
  #endif
319
 
 
320
 
    statusBar()->addPermanentWidget( ui.scrobbleLabel = new ScrobbleLabel );
321
 
 
322
 
    ui.actionToggleScrobbling->setCheckable( true );
323
 
    ui.actionToggleDiscoveryMode->setCheckable( true );
324
 
 
325
 
    // <PlayControls>
326
 
    QWidget *playControls = new QWidget( toolbar );
327
 
    ui_playcontrols.setupUi( playControls );
328
 
 
329
 
    QAction *playControlsAction = toolbar->addWidget( playControls );
330
 
    playControlsAction->setVisible( true );
331
 
 
332
 
    // </PlayControls>
333
 
    // </ToolBar>
334
 
 
335
 
  #ifndef Q_WS_MAC
336
 
    QMenu* fileMenu = new QMenu( tr( "&File" ), this );
337
 
    QAction* a = fileMenu->addAction( tr( "&Send to Tray" ), this, SLOT( close() ) );
338
 
    a->setToolTip( tr( "Send application to system tray" ) );
339
 
    fileMenu->addSeparator();
340
 
    QAction* exitAction = fileMenu->addAction( tr( "E&xit" ), this, SLOT( actualClose() ) );
341
 
    
342
 
    setAttribute( Qt::WA_MacNoClickThrough );
343
 
  #ifdef Q_WS_X11
344
 
    exitAction->setShortcut( tr( "CTRL+Q" ) );
345
 
    exitAction->setText( tr("&Quit") );
346
 
  #endif
347
 
    menuBar()->insertMenu( menuBar()->actions().at( 0 ), fileMenu );
348
 
  #endif
349
 
 
350
 
    QShortcut *s = new QShortcut( this );
351
 
    s->setKey( QKeySequence( "CTRL+W" ) );
352
 
    connect( s, SIGNAL(activated()), SLOT(hide()) );
353
 
 
354
 
    ui.actionTag->setEnabled( false );
355
 
    ui.actionRecommend->setEnabled( false );
356
 
    ui.actionLove->setEnabled( false );
357
 
    ui.actionBan->setEnabled( false );
358
 
    ui.actionAddToMyPlaylist->setEnabled( false );
359
 
    ui.actionSkip->setEnabled( false );
360
 
    ui.actionStop->setEnabled( false );
361
 
    ui.actionStop->setVisible( false );
362
 
 
363
 
    ui.stack->setAcceptDrops( false );
364
 
 
365
 
  #ifdef Q_WS_MAC
366
 
    {
367
 
        QApplication::setStyle( new MacStyleOverrides() );
368
 
 
369
 
        QPalette p = ui.centralwidget->palette();
370
 
        p.setColor( QPalette::Window, QColor( 0xe9, 0xe9, 0xe9 ) );
371
 
        ui.centralwidget->setPalette( p );
372
 
        p.setColor( QPalette::Button, QColor( 0xe9, 0xe9, 0xe9 ) );
373
 
 
374
 
        // Hacky hackety McHackerson
375
 
        QLinearGradient grad( 0.5, 0.0, 0.5, 20.0 );
376
 
        grad.setColorAt( 0.0, QColor( 0x8c, 0x8c, 0x8c ) );
377
 
        grad.setColorAt( 0.05, QColor( 0xf7, 0xf7, 0xf7 ) );
378
 
        grad.setColorAt( 1.0, QColor( 0xe4, 0xe4, 0xe4 ) );
379
 
        QBrush b( grad );
380
 
        p = statusBar()->palette();
381
 
        p.setBrush( QPalette::Window, b );
382
 
        p.setBrush( QPalette::Base, b );
383
 
        p.setColor( QPalette::Text, QColor( 0x59, 0x59, 0x59 ) );
384
 
        p.setColor( QPalette::WindowText, QColor( 0x59, 0x59, 0x59 ) );
385
 
        statusBar()->setPalette( p );
386
 
        ui.scrobbleLabel->label()->setPalette( p );
387
 
        
388
 
        // mac specific mainwindow adjustments
389
 
        QFrame *hline = new QFrame;
390
 
        hline->setFrameStyle( QFrame::HLine | QFrame::Plain );
391
 
        p.setColor( QPalette::WindowText, QColor( 140, 140, 140 ) );
392
 
        hline->setPalette( p );
393
 
        ui.vboxLayout1->insertWidget( ui.vboxLayout1->indexOf( ui.stack ), hline );
394
 
        ui.stack->setFrameStyle( QFrame::NoFrame );
395
 
 
396
 
        //alter the spacings and that
397
 
        ui.vboxLayout2->setMargin( 14 );
398
 
        for (int x = 0; x < ui.vboxLayout1->count(); ++x)
399
 
            if (ui.vboxLayout1->itemAt( x )->spacerItem())
400
 
                delete ui.vboxLayout1->takeAt( x );
401
 
 
402
 
        QFont f = statusBar()->font();
403
 
        f.setPixelSize( k_statusBarFontSizeMac );
404
 
        statusBar()->setFont( f );
405
 
        ui.scrobbleLabel->label()->setFont( f );
406
 
    }
407
 
 
408
 
  #elif defined WIN32
409
 
    // This is in order to remove the borders around the status bar widgets
410
 
    QString styleName = QApplication::style()->objectName();
411
 
    if( styleName == "windowsxp" )
412
 
    {
413
 
        statusBar()->setStyle( new WinXPStyleOverrides() );
414
 
    }
415
 
    else
416
 
    {
417
 
        statusBar()->setStyle( new WinStyleOverrides() );
418
 
 
419
 
        ui.stack->layout()->setMargin( 1 );
420
 
    }
421
 
 
422
 
  #elif defined Q_WS_X11
423
 
    statusBar()->setStyle( new WinXPStyleOverrides() );
424
 
 
425
 
    //aesthetics, separates the statusbar and central widget slightly
426
 
    static_cast<QVBoxLayout*>(ui.centralwidget->layout())->addSpacing( 2 );
427
 
  #endif
428
 
 
 
108
    ui.actionSettings->setMenuRole( QAction::PreferencesRole );
 
109
    ui.actionStop->setVisible( false );
 
110
    ui.statusbar->addPermanentWidget( ui.scrobbleLabel = new ScrobbleLabel );
 
111
 
 
112
////// PlayControls
 
113
    QWidget *playControls = new QWidget;
 
114
    ui.playcontrols.setupUi( playControls );
 
115
    ui.toolbar->addWidget( playControls );
 
116
 
 
117
////// splitter
 
118
    ui.splitter->setCollapsible( 0, false );
 
119
    ui.splitter->setCollapsible( 1, false );
 
120
    ui.splitter->setStretchFactor( 0, 0 );
 
121
    ui.splitter->setStretchFactor( 1, 1 );
 
122
 
 
123
////// force a white background as we don't support colour variants, soz 
 
124
    QPalette p = centralWidget()->palette();
 
125
    p.setBrush( QPalette::Base, Qt::white );
 
126
    p.setBrush( QPalette::Text, Qt::black );
 
127
    centralWidget()->setPalette( p );
 
128
 
 
129
////// Main Widgets
 
130
    ui.restStateWidget = new RestStateWidget( this );
 
131
    ui.metaDataWidget = new MetaDataWidget( this );
 
132
 
 
133
////// SideBar
 
134
    ui.sidebar = new SideBarTree( this );
 
135
    ui.sidebarFrame->layout()->addWidget( ui.sidebar );
 
136
    connect( ui.sidebar, SIGNAL(statusMessage( QString )), SLOT(statusMessage( QString )) );
 
137
    connect( ui.sidebar, SIGNAL(plsShowRestState()), SLOT(showRestState()) );
 
138
    connect( ui.sidebar, SIGNAL(plsShowNowPlaying()), SLOT(showMetaDataWidget()) );
 
139
 
 
140
////// ui.stack
 
141
    ui.stack->setBackgroundRole( QPalette::Base );
 
142
    ui.stack->addWidget( ui.restStateWidget );
 
143
    ui.stack->addWidget( ui.metaDataWidget );
 
144
 
 
145
#ifdef HIDE_RADIO
 
146
    ui.playcontrols.volume->setVisible( false );
 
147
    ui.menuControls->menuAction()->setVisible( false );
 
148
    ui.actionPlay->setVisible( false );
 
149
    ui.actionStop->setVisible( false );
 
150
    ui.actionSkip->setVisible( false );
 
151
    ui.actionBan->setVisible( false );
 
152
    ui.actionPlaylist->setVisible( false );
 
153
    ui.actionToggleDiscoveryMode->setVisible( false );
 
154
    ui.actionVolumeUp->setVisible( false );
 
155
    ui.actionVolumeDown->setVisible( false );
 
156
    ui.actionMute->setVisible( false );
 
157
 
 
158
    ui.stack->removeWidget( ui.restStateWidget );
 
159
#endif
 
160
 
 
161
    if (qApp->arguments().contains( "--debug" ))
 
162
        ui.menuHelp->addAction( "kr4sh pls, kthxbai", this, SLOT(crash()) );
 
163
 
 
164
    ui.restStateWidget->setFocus();
 
165
}
 
166
 
 
167
 
 
168
void
 
169
Container::setupTimeBar()
 
170
{
 
171
    // Station bar grey bg colour
 
172
    const QColor k_stationBarGreyTop( 0xba, 0xba, 0xba, 0xff );
 
173
    const QColor k_stationBarGreyMiddle( 0xe2, 0xe2, 0xe2, 0xff );
 
174
    const QColor k_stationBarGreyBottom( 0xff, 0xff, 0xff, 0xff );
 
175
 
 
176
    // Track bar blue bg colour
 
177
    const QColor k_trackBarBkgrBlueTop( 0xeb, 0xf0, 0xf2, 0xff );
 
178
    const QColor k_trackBarBkgrBlueMiddle( 0xe5, 0xe9, 0xec, 0xff );
 
179
    const QColor k_trackBarBkgrBlueBottom( 0xdc, 0xe2, 0xe5, 0xff );
 
180
 
 
181
    // Track bar progress bar colour
 
182
    const QColor k_trackBarProgressTop( 0xd6, 0xde, 0xe6, 0xff );
 
183
    const QColor k_trackBarProgressMiddle( 0xd0, 0xd9, 0xe2, 0xff );
 
184
    const QColor k_trackBarProgressBottom( 0xca, 0xd4, 0xdc, 0xff );
 
185
 
 
186
    // Track bar scrobbled colour
 
187
    const QColor k_trackBarScrobbledTop( 0xba, 0xc7, 0xd7, 0xff );
 
188
    const QColor k_trackBarScrobbledMiddle( 0xb8, 0xc4, 0xd5, 0xff );
 
189
    const QColor k_trackBarScrobbledBottom( 0xb5, 0xc1, 0xd2, 0xff );
 
190
 
 
191
    ////// song bar and scrobble bar
429
192
    ui.songTimeBar->setEnabled( false );
430
 
    ui.songTimeBar->setItemType( ItemTrack );
 
193
    ui.songTimeBar->setItemType( UnicornEnums::ItemTrack );
431
194
 
432
195
    QLinearGradient trackBarBkgrGradient( 0, 0, 0, ui.songTimeBar->height() );
433
196
    trackBarBkgrGradient.setColorAt( 0, k_trackBarBkgrBlueTop );
451
214
    ui.songTimeBar->setForegroundGradient( trackBarProgressGradient );
452
215
    ui.songTimeBar->setScrobbledGradient( trackBarScrobbledGradient );
453
216
 
454
 
    connect( this,           SIGNAL( newSong( MetaData ) ),
455
 
             ui.songTimeBar, SLOT( setNewTrack( const MetaData& ) ) );
456
 
 
457
217
    ui.stationTimeBar->setProgressEnabled( false );
458
218
    ui.stationTimeBar->setVisible( false );
459
 
    ui.stationTimeBar->setItemType( ItemStation );
 
219
    ui.stationTimeBar->setItemType( UnicornEnums::ItemStation );
460
220
 
461
221
    QLinearGradient stationBarGradient( 0, 0, 0, ui.stationTimeBar->height() );
462
222
    stationBarGradient.setColorAt( 0, k_stationBarGreyTop );
464
224
    stationBarGradient.setColorAt( 0.14, k_stationBarGreyBottom );
465
225
    stationBarGradient.setColorAt( 1, k_stationBarGreyBottom );
466
226
 
 
227
    ui.stationTimeBar->setBackgroundGradient( stationBarGradient );
 
228
    ui.stationTimeBar->setStopWatch( &The::radio().stationStopWatch() );
 
229
 
467
230
    if ( qApp->arguments().contains( "--sanity" ) )
468
231
        stationBarGradient = trackBarBkgrGradient;
469
 
    ui.stationTimeBar->setBackgroundGradient( stationBarGradient );
470
 
    ui.stationTimeBar->setStopWatch( &m_radio->stationStopWatch() );
471
 
 
472
 
//////
473
 
  #ifdef Q_WS_MAC
474
 
    // on mac qt returns height without unified toolbar height included :(
475
 
    QByteArray geometry = The::settings().containerGeometry();
476
 
    if (geometry.isEmpty())
477
 
        resize( width(), 496 );
 
232
}
 
233
 
 
234
 
 
235
void
 
236
Container::setupTrayIcon()
 
237
{
 
238
    Q_DEBUG_BLOCK;
 
239
 
 
240
    QMenu *menu = new QMenu( this );
 
241
    menu->addAction( tr("Open"), this, SLOT(restoreWindow()) );
 
242
 
 
243
  #ifdef Q_WS_MAC
 
244
    menu->addSeparator();
 
245
    menu->addAction( ui.actionSettings );
 
246
  #endif
 
247
    menu->addSeparator();
 
248
    menu->addAction( ui.actionDashboard );
 
249
    menu->addAction( ui.actionToggleScrobbling );
 
250
    (menu->addAction( tr("Change User") ))->setMenu( ui.menuUser );
 
251
    menu->addSeparator();
 
252
    menu->addAction( ui.actionShare );
 
253
    menu->addAction( ui.actionTag );
 
254
    menu->addAction( ui.actionPlaylist );
 
255
    menu->addSeparator();
 
256
    menu->addAction( ui.actionLove );
 
257
    menu->addAction( ui.actionBan );
 
258
  #ifndef HIDE_RADIO
 
259
    menu->addSeparator();
 
260
    menu->addAction( ui.actionPlay );
 
261
    menu->addAction( ui.actionStop );
 
262
    menu->addAction( ui.actionSkip );
 
263
  #endif
 
264
    menu->addSeparator();
 
265
    menu->addAction( ui.actionQuit );
 
266
 
 
267
  #ifdef Q_WS_MAC
 
268
    // strangely text is amended in Application menu, but nowhere else
 
269
    ui.actionSettings->setText( tr("Preferences...") );
 
270
    ui.actionQuit->setText( tr("Quit Last.fm") );
 
271
  #endif
 
272
 
 
273
    m_trayIcon = new TrayIcon( this );
 
274
    m_trayIcon->setContextMenu( menu );
 
275
 
 
276
    connect( m_trayIcon,
 
277
             SIGNAL(activated( QSystemTrayIcon::ActivationReason )),
 
278
             SLOT(onTrayIconActivated( QSystemTrayIcon::ActivationReason )) );
 
279
 
 
280
    connect( &The::settings(),
 
281
             SIGNAL(userSettingsChanged( LastFmUserSettings& )),
 
282
             m_trayIcon,
 
283
             SLOT(setUser( LastFmUserSettings& )) );
 
284
 
 
285
  #ifdef Q_WS_MAC
 
286
    // FIXME this is due to a Qt bug (presumably) on Mac that doesn't raise the
 
287
    // application even if you show a dialog via an action in your application
 
288
    connect( menu, SIGNAL(triggered( QAction* )), SLOT(raise()) );
 
289
  #endif
 
290
}
 
291
 
 
292
 
 
293
void
 
294
Container::applyPlatformSpecificTweaks()
 
295
{
 
296
#ifdef Q_WS_X11
 
297
    ui.actionCheckForUpdates->setVisible( false );
 
298
    ui.actionQuit->setShortcut( tr( "CTRL+Q" ) );
 
299
    ui.actionQuit->setText( tr("&Quit") );
 
300
 
 
301
    m_styleOverrides = new WinXPStyleOverrides();
 
302
    ui.statusbar->setStyle( m_styleOverrides ); //no ugly surrounding lines
 
303
 
 
304
    setWindowIcon( QIcon( MooseUtils::dataPath( "icons/as.png" ) ) );
 
305
    //aesthetics, separates the statusbar and central widget slightly
 
306
    static_cast<QVBoxLayout*>(ui.centralwidget->layout())->addSpacing( 2 );
 
307
#else
 
308
    ui.actionScrobbleManualIPod->setVisible( false );
 
309
#endif
 
310
 
 
311
#ifdef WIN32
 
312
    // Can't use 32 for height as labels get truncated on Win classic
 
313
    ui.toolbar->setIconSize( QSize( 50, 34 ) );
 
314
    ui.menuTools->insertSeparator( ui.actionPlaylist );
 
315
 
 
316
    // This is in order to remove the borders around the status bar widgets
 
317
    if ( qApp->style()->objectName() != "windowsxp" )
 
318
    {
 
319
        m_styleOverrides = new WinStyleOverrides();
 
320
        statusBar()->setStyle( m_styleOverrides );
 
321
        ui.stack->layout()->setMargin( 1 );
 
322
    }
478
323
    else
479
 
        restoreGeometry( geometry );
480
 
  #else
481
 
    restoreGeometry( The::settings().containerGeometry() );
482
 
  #endif
483
 
    setWindowState( The::settings().containerWindowState() );
 
324
    {
 
325
        m_styleOverrides = new WinXPStyleOverrides();
 
326
        statusBar()->setStyle( m_styleOverrides );
 
327
    }
 
328
#endif
484
329
 
485
 
//////
486
 
  #ifdef Q_WS_MAC
 
330
#ifdef Q_WS_MAC
 
331
    setUnifiedTitleAndToolBarOnMac( true );
 
332
    ui.toolbar->setAttribute( Qt::WA_MacNoClickThrough );
 
333
    ui.actionFAQ->setShortcut( tr( "Ctrl+?" ) );
487
334
    ui.splitter->setHandleWidth( 1 );
488
 
  #endif
489
 
      
490
 
    ui.splitter->setCollapsible( 0, false );
491
 
    ui.splitter->setCollapsible( 1, false );
492
 
    ui.splitter->setStretchFactor( 0, 0 );
493
 
    ui.splitter->setStretchFactor( 1, 1 );
 
335
    ui.actionMute->setShortcut( QKeySequence( Qt::CTRL + Qt::ALT + Qt::Key_Down ) );
 
336
    ui.menuFile->menuAction()->setVisible( false );
 
337
 
 
338
    // Qt 4.3.1, setSeparatorsCollapsible doesn't work as advertised :(
 
339
    foreach (QAction* a, ui.menuHelp->findChildren<QAction*>())
 
340
        if (a->isSeparator())
 
341
            delete a;
 
342
 
 
343
    qApp->setStyle( new MacStyleOverrides() );
 
344
 
 
345
    QPalette p = ui.centralwidget->palette();
 
346
    p.setColor( QPalette::Window, QColor( 0xe9, 0xe9, 0xe9 ) );
 
347
    ui.centralwidget->setPalette( p );
 
348
 
 
349
    p = statusBar()->palette();
 
350
    p.setColor( QPalette::WindowText, QColor( 0x59, 0x59, 0x59 ) );
 
351
    ui.scrobbleLabel->label()->setPalette( p );
 
352
 
 
353
    QLinearGradient g( 0.5, 0.0, 0.5, 20.0 );
 
354
    g.setColorAt( 0.0, QColor( 0x8c, 0x8c, 0x8c ) );
 
355
    g.setColorAt( 0.05, QColor( 0xf7, 0xf7, 0xf7 ) );
 
356
    g.setColorAt( 1.0, QColor( 0xe4, 0xe4, 0xe4 ) );
 
357
    p.setBrush( QPalette::Window, QBrush( g ) );
 
358
    ui.statusbar->setPalette( p );
 
359
 
 
360
    // mac specific mainwindow adjustments
 
361
    QFrame *hline = new QFrame;
 
362
    hline->setFrameStyle( QFrame::HLine | QFrame::Plain );
 
363
    p.setColor( QPalette::WindowText, QColor( 140, 140, 140 ) );
 
364
    hline->setPalette( p );
 
365
    ui.vboxLayout1->insertWidget( ui.vboxLayout1->indexOf( ui.stack ), hline );
 
366
    ui.stack->setFrameStyle( QFrame::NoFrame );
 
367
 
 
368
    //alter the spacings and that
 
369
    ui.vboxLayout2->setMargin( 14 );
 
370
    for (int x = 0; x < ui.vboxLayout1->count(); ++x)
 
371
        if (ui.vboxLayout1->itemAt( x )->spacerItem())
 
372
            delete ui.vboxLayout1->takeAt( x );
 
373
 
 
374
    QFont f = ui.statusbar->font();
 
375
    f.setPixelSize( 10 );
 
376
    ui.statusbar->setFont( f );
 
377
    ui.scrobbleLabel->label()->setFont( f );
 
378
#endif
 
379
 
 
380
#ifndef WIN32
 
381
    ui.actionGetPlugin->setEnabled( false );
 
382
    ui.actionGetPlugin->setVisible( false );
 
383
#endif
 
384
}
 
385
 
 
386
 
 
387
void
 
388
Container::setupConnections()
 
389
{
 
390
    connect( ui.actionDashboard, SIGNAL(triggered()), SLOT(gotoProfile()) );
 
391
    connect( ui.actionSettings, SIGNAL(triggered()), SLOT(showSettingsDialog()) );
 
392
    connect( ui.actionGetPlugin, SIGNAL(triggered()), SLOT(getPlugin()) );
 
393
    connect( ui.actionCheckForUpdates, SIGNAL(triggered()), SLOT(checkForUpdates()) );
 
394
    connect( ui.actionAddUser, SIGNAL(triggered()), SLOT(addUser()) );
 
395
    connect( ui.actionDeleteUser, SIGNAL(triggered()), SLOT(deleteUser()) );
 
396
    connect( ui.actionToggleScrobbling, SIGNAL(triggered()), SLOT(toggleScrobbling()) );
 
397
    connect( ui.actionToggleDiscoveryMode, SIGNAL(triggered()), SLOT(toggleDiscoveryMode()) );
 
398
    connect( ui.actionAboutLastfm, SIGNAL(triggered()), SLOT(about()) );
 
399
    connect( ui.menuUser, SIGNAL(aboutToShow()), SLOT(onAboutToShowUserMenu()) );
 
400
    connect( ui.menuUser, SIGNAL(triggered( QAction* )), SLOT(onUserSelected( QAction* )) );
 
401
    connect( ui.actionSkip, SIGNAL(triggered()), SLOT(skip()) );
 
402
    connect( ui.actionStop, SIGNAL(triggered()), SLOT(stop()) );
 
403
    connect( ui.actionPlay, SIGNAL(triggered()), SLOT(play()) );
 
404
    connect( ui.scrobbleLabel, SIGNAL(clicked()), SLOT(toggleScrobbling()) );
 
405
    connect( ui.actionVolumeUp, SIGNAL(triggered()), SLOT(volumeUp()) );
 
406
    connect( ui.actionVolumeDown, SIGNAL(triggered()), SLOT(volumeDown()) );
 
407
    connect( ui.actionMute, SIGNAL(triggered()), SLOT(mute()) );
 
408
    connect( ui.actionFAQ, SIGNAL(triggered()), SLOT(showFAQ()) );
 
409
    connect( ui.actionForums, SIGNAL(triggered()), SLOT(showForums()) );
 
410
    connect( ui.actionInviteAFriend, SIGNAL(triggered()), SLOT(inviteAFriend()) );
 
411
    connect( ui.actionDiagnostics, SIGNAL(triggered()), SLOT(showDiagnosticsDialog()) );
 
412
    connect( ui.actionSendToTray, SIGNAL(triggered()), SLOT(close()) );
 
413
    connect( ui.actionQuit, SIGNAL(triggered()), SLOT(quit()) );
 
414
    connect( ui.playcontrols.volume, SIGNAL(valueChanged( int )), &The::radio(), SLOT(setVolume( int )) );
 
415
    connect( The::webService(), SIGNAL(success( Request* )), SLOT(webServiceSuccess( Request* )) );
 
416
    connect( The::webService(), SIGNAL(failure( Request* )), SLOT(webServiceFailure( Request* )), Qt::QueuedConnection );
 
417
    connect( &The::settings(), SIGNAL( userSettingsChanged( LastFmUserSettings& ) ), SLOT( updateUserStuff( LastFmUserSettings& ) ) );
 
418
    connect( &The::settings(), SIGNAL( appearanceSettingsChanged() ), SLOT( updateAppearance() ) );
 
419
    connect( m_updater, SIGNAL( updateCheckDone( bool, bool, QString ) ), SLOT( updateCheckDone( bool, bool, QString ) ) );
 
420
    connect( ui.stack, SIGNAL(currentChanged( int )), SIGNAL(stackIndexChanged( int )) );
 
421
    connect( ui.actionMyProfile, SIGNAL(triggered()), SLOT(toggleSidebar()) );
 
422
    connect( ui.actionPlaylist, SIGNAL(triggered()), SLOT(addToMyPlaylist()) );
 
423
    connect( ui.actionTag, SIGNAL(triggered()), SLOT(showTagDialog()) );
 
424
    connect( ui.actionShare, SIGNAL(triggered()), SLOT(showShareDialog()) );
 
425
    connect( ui.actionLove, SIGNAL( triggered() ), SLOT(love()) );
 
426
    connect( ui.actionBan, SIGNAL(triggered()), SLOT(ban()) );
 
427
    connect( ui.metaDataWidget, SIGNAL(tagButtonClicked()), SLOT(showTagDialogMD()) );
 
428
    connect( ui.metaDataWidget, SIGNAL(urlHovered( QString )), SLOT(displayUrlInStatusBar( QString )) );
 
429
 
 
430
    connect( ui.actionScrobbleManualIPod, SIGNAL(triggered()), SLOT(scrobbleManualIpod()) );
 
431
 
 
432
////// important connections
 
433
    connect( &The::scrobbler(),
 
434
             SIGNAL(status( int, QVariant )),
 
435
             SLOT(onScrobblerStatusChange( int, QVariant )) );
 
436
    connect( &The::radio(),
 
437
             SIGNAL(error( RadioError, QString )),
 
438
             SLOT(onRadioError( RadioError, QString )), Qt::QueuedConnection /* crash if direct connect */ );
 
439
    connect( &The::radio(),
 
440
             SIGNAL(stateChanged( RadioState )),
 
441
             SLOT(onRadioStateChanged( RadioState )) );
 
442
    connect( &The::radio(),
 
443
             SIGNAL( buffering( int, int ) ),
 
444
             SLOT( onRadioBuffering( int, int ) ) );
 
445
 
 
446
    connect( qApp, SIGNAL(event( int, QVariant )), SLOT(onAppEvent( int, QVariant )) );
 
447
 
 
448
////// we don't use CTRL because that varies by platform, NOTE don't tr()!
 
449
    QObject* hides  = new QShortcut( QKeySequence( "Ctrl+W" ), this );
 
450
    QObject* openL = new QShortcut( QKeySequence( "Alt+Shift+L" ), this );
 
451
    QObject* openF = new QShortcut( QKeySequence( "Alt+Shift+F" ), this );
 
452
    connect( hides, SIGNAL(activated()), SLOT(hide()) );
 
453
    connect( openL, SIGNAL(activated()), SLOT(onAltShiftL()) );
 
454
    connect( openF, SIGNAL(activated()), SLOT(onAltShiftF()) );
 
455
 
 
456
#ifndef Q_WS_X11 // No helper on unix!
 
457
    QObject* openH = new QShortcut( QKeySequence( "Alt+Shift+H" ), this );
 
458
    connect( openH, SIGNAL(activated()), SLOT(onAltShiftH()) );
 
459
#endif
 
460
#ifdef WIN32
 
461
    QObject* openP = new QShortcut( QKeySequence( "Alt+Shift+P" ), this );
 
462
    connect( openP, SIGNAL( activated() ), SLOT( onAltShiftP() ) );
 
463
#endif
 
464
}
 
465
 
 
466
 
 
467
void
 
468
Container::restoreState()
 
469
{
 
470
    //NOTE it is important that the connections are done first
 
471
    ui.playcontrols.volume->setValue( The::settings().volume() );
 
472
 
494
473
    QByteArray state = The::settings().splitterState();
495
 
    if (state.size())
 
474
    if ( state.size() )
 
475
    {
496
476
        ui.splitter->restoreState( state );
497
 
        
498
 
    #ifdef Q_WS_MAC
 
477
 
 
478
        m_sidebarWidth = The::settings().sidebarWidth();
 
479
 
 
480
        if ( m_sidebarWidth > 0 )
 
481
        {
 
482
            LOGL( 3, "Restoring sidebar width: " << m_sidebarWidth );
 
483
            ui.splitter->setSizes( QList<int>() << m_sidebarWidth );
 
484
        }
 
485
    }
 
486
#ifdef WIN32
 
487
    else
 
488
        ui.splitter->setSizes( QList<int>() << 176 );
 
489
#endif
 
490
 
 
491
#ifdef Q_WS_MAC
499
492
    //HACK for osx since the splitter is undetectable for most users
500
493
    QList<int> sizes = ui.splitter->sizes();
501
494
    if (sizes.count() == 2 && sizes[0] < 100)
504
497
        sizes[0] = 190;
505
498
        ui.splitter->setSizes( sizes );
506
499
    }
507
 
    #endif
508
 
 
509
 
 
510
 
////// force a white background as we don't support colour variants, soz 
511
 
    QPalette p = centralWidget()->palette();
512
 
    p.setBrush( QPalette::Base, Qt::white );
513
 
    centralWidget()->setPalette( p );
514
 
 
515
 
//////
516
 
    initTray();
517
 
 
518
 
////// GUI slots
519
 
    connect( ui.actionDashboard,            SIGNAL( triggered() ),
520
 
             this,                            SLOT( gotoProfile() ) );
521
 
    connect( ui.actionSettings,             SIGNAL( triggered() ),
522
 
             this,                            SLOT( showSettingsDialog() ) );
523
 
    connect( ui.actionGetPlugin,            SIGNAL( triggered() ),
524
 
             this,                            SLOT( getPlugin() ) );
525
 
  #ifndef Q_WS_X11
526
 
    connect( ui.actionCheckForUpdates,      SIGNAL( triggered() ),
527
 
             this,                            SLOT( checkForUpdates() ) );
528
 
  #endif
529
 
    connect( ui.actionAddUser,              SIGNAL( triggered() ),
530
 
             this,                            SLOT( addUser() ) );
531
 
    connect( ui.actionDeleteUser,           SIGNAL( triggered() ),
532
 
             this,                            SLOT( deleteUser() ) );
533
 
    connect( ui.actionToggleScrobbling,     SIGNAL( triggered() ),
534
 
             this,                            SLOT( toggleScrobbling() ) );
535
 
    connect( ui.actionToggleDiscoveryMode,  SIGNAL( triggered() ),
536
 
             this,                            SLOT( toggleDiscoveryMode() ) );
537
 
    connect( ui.actionAboutLastfm,          SIGNAL( triggered() ),
538
 
             this,                            SLOT( about() ) );
539
 
    connect( ui.menuUser,                   SIGNAL( aboutToShow() ),
540
 
             this,                            SLOT( showUserMenu() ) );
541
 
    connect( ui.menuUser,                   SIGNAL( triggered( QAction* ) ),
542
 
             this,                            SLOT( userSelected( QAction* ) ) );
543
 
    connect( ui.actionSkip,                 SIGNAL( triggered() ),
544
 
             this,                            SLOT( skip() ) );
545
 
    connect( ui.actionStop,                 SIGNAL( triggered() ),
546
 
             this,                            SLOT( stop() ) );
547
 
    connect( ui.actionPlay,                 SIGNAL( triggered() ),
548
 
             this,                            SLOT( play() ) );
549
 
    connect( ui_playcontrols.volume,        SIGNAL( valueChanged( int ) ),
550
 
             m_radio,                         SLOT( setVolume( int ) ) );
551
 
    connect( ui.scrobbleLabel,              SIGNAL( clicked() ),
552
 
             this,                            SLOT( toggleScrobbling() ) );
553
 
    connect( ui.actionVolumeUp,             SIGNAL( triggered() ),
554
 
             this,                            SLOT( volumeUp() ) );
555
 
    connect( ui.actionVolumeDown,           SIGNAL( triggered() ),
556
 
             this,                            SLOT( volumeDown() ) );
557
 
    connect( ui.actionMute,                 SIGNAL( triggered() ),
558
 
             this,                            SLOT( mute() ) );
559
 
    connect( ui.actionFAQ,                  SIGNAL( triggered() ),
560
 
             this,                            SLOT( showFAQ() ) );
561
 
    connect( ui.actionForums,               SIGNAL( triggered() ),
562
 
             this,                            SLOT( showForums() ) );
563
 
    connect( ui.actionInviteAFriend,        SIGNAL( triggered() ),
564
 
             this,                            SLOT( inviteAFriend() ) );
565
 
 
566
 
    // Services & Extensions
567
 
    //connect( The::webService(), SIGNAL(handshakeResult( Handshake* )), SLOT(handshakeResult( Handshake* )) );
568
 
    connect( The::webService(), SIGNAL(success( Request* )), SLOT(webServiceSuccess( Request* )) );
569
 
    connect( The::webService(), SIGNAL(failure( Request* )), SLOT(webServiceFailure( Request* )), Qt::QueuedConnection );
570
 
 
571
 
    connect( m_radio,      SIGNAL( buffering( int, int ) ),
572
 
             this,           SLOT( onRadioBuffering( int, int ) ) );
573
 
    connect( m_radio,      SIGNAL( stateChanged( RadioState ) ),
574
 
             this,           SLOT( onRadioStateChange( RadioState ) ) );
575
 
    connect( m_radio,      SIGNAL( error( RadioError, const QString& ) ),
576
 
             this,           SLOT( onRadioError( RadioError, const QString& ) ), Qt::QueuedConnection );
577
 
 
578
 
    connect( &The::settings(), SIGNAL( userSettingsChanged( UserSettings& ) ),
579
 
             this,               SLOT( updateUserStuff( UserSettings& ) ) );
580
 
    connect( &The::settings(), SIGNAL( doReconnect() ),
581
 
             this,               SLOT( initServices() ) );
582
 
 
583
 
    // Auto updater
584
 
    connect( m_updater, SIGNAL( updateCheckDone( bool, bool, QString ) ),
585
 
             this,        SLOT( updateCheckDone( bool, bool, QString ) ) );
586
 
 
587
 
    // update volume _after_ signals have been connected
588
 
    ui_playcontrols.volume->setValue( The::settings().volume() );
589
 
 
590
 
  #ifdef HIDE_RADIO
591
 
    ui_playcontrols.volume->setVisible( false );
592
 
 
593
 
    ui.actionPlay->setVisible( false );
594
 
    ui.actionStop->setVisible( false );
595
 
    ui.actionSkip->setVisible( false );
596
 
    ui.actionAddToMyPlaylist->setVisible( false );
597
 
    ui.actionBan->setVisible( false );
598
 
    ui.actionVolumeUp->setVisible( false );
599
 
    ui.actionVolumeDown->setVisible( false );
600
 
    ui.actionMute->setVisible( false );
601
 
    ui.actionToggleDiscoveryMode->setVisible( false );
602
 
    ui.menuControls->menuAction()->setVisible( false );
603
 
    ui.stack->setCurrentIndex( 0 );
604
 
 
605
 
    #ifdef Q_WS_MAC
606
 
        m_sysTrayActions->setVisible( false );
607
 
    #endif
608
 
  #endif
609
 
 
610
 
  #ifndef WIN32
611
 
    ui.actionGetPlugin->setEnabled( false );
612
 
    ui.actionGetPlugin->setVisible( false );
613
 
  #else
614
 
    ui.menuTools->insertSeparator( ui.actionAddToMyPlaylist );
615
 
  #endif
616
 
 
617
 
  #ifdef Q_WS_MAC
618
 
    new ITunesScript( this, m_listener );
619
 
  #endif
620
 
 
621
 
  #ifdef QT_NO_DEBUG
622
 
    if (qApp->arguments().contains( "--debug" ))
623
 
  #endif
624
 
        // Add crash option to menu!
625
 
    ui.menuHelp->addAction( "kr4sh pls, kthxbai", this, SLOT(crash()) );
626
 
 
627
 
  #ifdef MONITOR_STREAMING
628
 
    ui.menuHelp->addAction( "Stream Monitor", m_radio, SLOT( activateStreamMonitor() ) );
629
 
  #endif
630
 
  
631
 
    connect( ui.stack, SIGNAL(currentChanged( int )), SIGNAL(stackIndexChanged( int )) );
632
 
  
633
 
//////
634
 
    m_showRestStateTimer = new QTimer( this );
635
 
    m_showRestStateTimer->setSingleShot( true );
636
 
    m_showRestStateTimer->setInterval( 200 );
637
 
    connect( m_showRestStateTimer, SIGNAL(timeout()), SLOT(onRestStateTimerTimeout()) );
638
 
 
639
 
    // Remove the path to the app and parse
640
 
    QStringList params = qApp->arguments().mid( 1 );
641
 
    m_iControl->parseCommands( params );
 
500
 
 
501
    // on mac qt returns height without unified toolbar height included :(
 
502
    // so first time we have to do it like this
 
503
    QByteArray geometry = The::settings().containerGeometry();
 
504
    if (geometry.isEmpty())
 
505
        resize( width(), 496 );
 
506
    else
 
507
        restoreGeometry( geometry );
 
508
#else
 
509
    restoreGeometry( The::settings().containerGeometry() );
 
510
#endif
 
511
    setWindowState( The::settings().containerWindowState() );
642
512
}
643
513
 
644
514
 
645
515
void
646
 
Container::initServices()
 
516
Container::loadExtensions()
647
517
{
648
 
    Q_DEBUG_BLOCK;
649
 
 
650
 
    UserSettings& user = The::settings().currentUser();
651
 
    LOG( 3, "Starting app for user: " << user.username() << "\n" );
652
 
 
653
 
    // we must restore state here as we save it in toggleSidebar in order to get
654
 
    // round the bug in Qt where saveState for the splitter is lost for hidden widgets
655
 
    m_sidebarEnabled = !user.sidebarEnabled();
656
 
    toggleSidebar();
657
 
    
658
 
    updateUserStuff( user );
659
 
    statusBar()->showMessage( tr( "Connecting to server..." ) );
660
 
 
661
 
//////// We now have a current user
662
 
    QString username = user.username();
663
 
    QString password = user.password();
664
 
    QString version = The::settings().version();
665
 
 
666
 
    m_radio->init( username, password, version );
667
 
 
668
 
////// shut down the current listener, this will cause a scrobble if required
669
 
    // also it makes sense as the new user should start with the chance to scrobble
670
 
    // the current track if applicable
671
 
    if (m_listener->GetActivePlayer())
 
518
  #ifdef WIN32
 
519
    // Hack to get it working with VS2005
 
520
    QString path = qApp->applicationDirPath();
 
521
  #else
 
522
    QString path = qApp->applicationDirPath() + "/extensions";
 
523
  #endif
 
524
 
 
525
  #ifndef QT_NO_DEBUG
 
526
    path += "/debug";
 
527
  #endif
 
528
 
 
529
    QDir d( path );
 
530
    foreach( QString fileName, d.entryList( QDir::Files ) )
672
531
    {
673
 
        CPlayerCommand command( PCMD_STOP, m_listener->GetActivePlayer()->GetID(), m_listener->GetNowPlaying() );
674
 
        m_listener->Handle( command );
675
 
        
676
 
        command.mCmd = PCMD_START;
677
 
        m_listener->Handle( command );
 
532
        if ( !fileName.startsWith( EXTENSION_PREFIX ) )
 
533
            continue;
 
534
        if ( !QLibrary::isLibrary( d.absoluteFilePath( fileName ) ) )
 
535
            continue;
 
536
 
 
537
        LOGL( 3, "Trying to load extension: " << fileName );
 
538
        QObject* plugin = QPluginLoader( d.absoluteFilePath( fileName ) ).instance();
 
539
        if ( !plugin ) {
 
540
            LOGL( 1, "Failed to load " << fileName );
 
541
            continue;
 
542
        }
 
543
 
 
544
        LOGL( 3, "Extension loaded." );
 
545
        ExtensionInterface* iExtension = qobject_cast<ExtensionInterface *>( plugin );
 
546
        if ( iExtension )
 
547
        {
 
548
            if ( iExtension->hasSettingsPane() )
 
549
            {
 
550
                // Add it to Options
 
551
                LOGL( 3, "Loaded a settings extension" );
 
552
                m_settingsDialog->addExtension( iExtension );
 
553
            }
 
554
            QSettings* us = new CurrentUserSettings( this );
 
555
            iExtension->setSettings( us );
 
556
        }
678
557
    }
679
 
 
680
 
//////
681
 
    // initialise the scrobbler to the new user, handshake is delayed until current users scrobble completes
682
 
    Scrobbler::Init init;
683
 
    init.username = username;
684
 
    init.client_version = version;
685
 
    init.password = password;
686
 
 
687
 
    m_scrobbler->handshake( init );
688
 
 
689
 
//////
690
 
    // enable again when handshake succeeds
691
 
    ui.actionPlay->setEnabled( false );
692
 
 
693
 
#ifndef HIDE_RADIO
694
 
    m_rest_state_widget->setPlayEnabled( false );
695
 
 
696
 
    m_rest_state_widget->updatePlayerNames();
697
 
#endif
698
558
}
699
559
 
700
560
 
701
561
Container::~Container()
702
562
{
703
 
    Q_DEBUG_BLOCK;
704
 
    
705
 
    delete m_metaDataExtension;
706
 
}
707
 
 
708
 
 
709
 
void
710
 
Container::initListener()
711
 
{
712
 
    connect( m_listener, SIGNAL( trackChanged( TrackInfo, bool ) ),
713
 
             this,         SLOT( setNewSong( TrackInfo, bool ) ), Qt::QueuedConnection );
714
 
 
715
 
    connect( m_listener, SIGNAL( trackScrobbled( const TrackInfo& ) ),
716
 
             this,         SLOT( onTrackScrobbled( const TrackInfo& ) ), Qt::QueuedConnection );
717
 
 
718
 
    connect( m_listener, SIGNAL( exceptionThrown( QString ) ),
719
 
             this,         SLOT( listenerException( QString ) ) );
720
 
 
721
 
    // Start listener worker thread
722
 
    m_listener->start();
723
 
}
724
 
 
725
 
 
726
 
void
727
 
Container::dragMoveEvent( QDragMoveEvent* event )
728
 
{
729
 
  #ifndef HIDE_RADIO
730
 
    QString url = event->mimeData()->urls().value( 0 ).toString();
731
 
    if ( url.startsWith( "lastfm://" ) )
732
 
    {
733
 
        event->acceptProposedAction();
734
 
    }
735
 
    else
736
 
        event->ignore();
737
 
  #endif // HIDE_RADIO
738
 
}
739
 
 
740
 
 
741
 
void
742
 
Container::dragEnterEvent( QDragEnterEvent* event )
743
 
{
744
 
  #ifndef HIDE_RADIO
745
 
   QString url = event->mimeData()->urls().value( 0 ).toString();
746
 
    if ( url.startsWith( "lastfm://" ) )
747
 
    {
748
 
        event->acceptProposedAction();
749
 
    }
750
 
    else
751
 
        event->ignore();
752
 
  #endif // HIDE_RADIO
753
 
}
754
 
 
755
 
 
756
 
void
757
 
Container::dropEvent( QDropEvent* event )
758
 
{
759
 
  #ifndef HIDE_RADIO
760
 
    QString url = event->mimeData()->urls().value( 0 ).toString();
761
 
    if ( url.startsWith( "lastfm://" ) )
762
 
    {
763
 
        m_radio->playStation( StationUrl( url ) );
764
 
    }
765
 
  #endif // HIDE_RADIO
 
563
    LOGL( 3, "Saving app state" );
 
564
 
 
565
    // don't save if hidden as Qt is shit and this doesn't work, we save 
 
566
    // just before hiding too to ensure we always have the state saved correctly
 
567
    if ( m_sidebarEnabled )
 
568
    {
 
569
        // HACK: workaround for Qt occasionally returning width 75 when it shouldn't
 
570
        int widthQtThinksItIs = ui.splitter->sizes().first();
 
571
        int actualWidth = widthQtThinksItIs == 75 ? m_sidebarWidth : widthQtThinksItIs;
 
572
 
 
573
        LOGL( 3, "Saving sidebar width: " << actualWidth );
 
574
        The::settings().setSidebarWidth( actualWidth );
 
575
 
 
576
        The::settings().setSplitterState( ui.splitter->saveState() );
 
577
    }
 
578
 
 
579
    The::settings().setContainerWindowState( windowState() );
 
580
    The::settings().setVolume( ui.playcontrols.volume->value() );
 
581
    The::currentUser().setSidebarEnabled( m_sidebarEnabled );
 
582
 
 
583
    LOGL( 3, "Saving config" );
 
584
    QSettings().sync();
 
585
 
 
586
#ifndef Q_WS_MAC
 
587
    delete m_styleOverrides;
 
588
#endif
 
589
}
 
590
 
 
591
 
 
592
void
 
593
Container::quit()
 
594
{
 
595
    if ( ConfirmDialog::quit( this ) )
 
596
    {
 
597
        qApp->quit();
 
598
    }
766
599
}
767
600
 
768
601
 
769
602
void
770
603
Container::closeEvent( QCloseEvent *event )
771
604
{
 
605
    bool quit = false;
 
606
 
772
607
    #ifdef Q_WS_MAC
773
608
    if ( !event->spontaneous() )
774
 
    {
775
 
        actualClose( false /*don't confirm with user*/ );
 
609
        quit = true;
 
610
    else
 
611
    #endif
 
612
 
 
613
    // ELSE FOR OSX ABOVE!
 
614
    if ( !The::settings().showTrayIcon() )
 
615
    {
 
616
        #ifndef Q_WS_X11
 
617
        if ( !ConfirmDialog::hide( this ) )
 
618
        {
 
619
            event->ignore();
 
620
            return;
 
621
        }
 
622
        #else
 
623
        quit = true;
 
624
        #endif
 
625
    }
 
626
 
 
627
    if ( quit )
 
628
    {
 
629
        qApp->quit();
 
630
        event->accept();
 
631
    }
 
632
    else
 
633
    {
 
634
        // Just minimise to tray
 
635
        minimiseToTray();
776
636
        event->ignore();
777
 
        return;
778
 
    }
779
 
    #endif
780
 
 
781
 
    // Just minimise to tray
782
 
    minimiseToTray();
783
 
    event->ignore();
784
 
}
785
 
 
786
 
 
787
 
bool
788
 
Container::event( QEvent* e )
789
 
{
790
 
    #ifdef Q_WS_MAC
791
 
        #if QT_VERSION >= 0x00040300
792
 
        //TODO remove when Qt is fixed!
793
 
        if ( e->type() == QEvent::Resize )
794
 
        {
795
 
            // hack as Qt 4.3.1 is b0rked for unified toolbars
796
 
            ui.toolbar->setMaximumWidth( width() );
797
 
        }
798
 
        #endif
799
 
    #endif
800
 
    
801
 
    if (e->type() == QEvent::Move || e->type() == QEvent::Resize)
802
 
    {
803
 
        // Again, Qt is broken, if maximised the saveGeometry function fails to
804
 
        // save the geometry for the non-maximised state. So instead we must save it
805
 
        // for *every* resize and move event. Yay!
806
 
 
807
 
        if (windowState() != Qt::WindowMaximized)
808
 
            The::settings().setContainerGeometry( saveGeometry() );
809
 
    }
810
 
 
811
 
    return QMainWindow::event( e );
812
 
}
813
 
 
814
 
 
815
 
void
816
 
Container::actualClose( bool confirm )
817
 
{
818
 
    Q_DEBUG_BLOCK;
819
 
 
820
 
    bool okToClose = true;
821
 
 
822
 
    if ( confirm )
823
 
    {
824
 
        QString m = tr( "Really quit Last.fm? Any music you listen to will not be scrobbled "
825
 
                        "to your profile." );
826
 
        ConfirmDialog dlg( ConfirmDialog::Quit, m, this );
827
 
        okToClose = dlg.exec() == QDialog::Accepted;
828
 
    }
829
 
 
830
 
    if ( okToClose )
831
 
    {
832
 
        hide(); //makes it look like we have closed already to user
833
 
 
834
 
        m_closingDown = true;
835
 
 
836
 
        // This allows any extensions (i.e. Skype) to clear its stuff. Bit of a hack.
837
 
        emit newSong( MetaData() );
838
 
 
839
 
        // EJ TODO FIXME: Can't do this as the radio's stopping is asynchronous
840
 
        // and it will lead to the radio calling the listener after it's died.
841
 
        // If we're not doing this however, we will get an assert about a thread
842
 
        // quitting (the StopWatch inside the radio) improperly.
843
 
        // Need to solve the whole shutdown thing in a more elegant way.
844
 
        m_radio->stop();
845
 
 
846
 
        LOGL( Logger::Debug, "Radio state at shutdown: " <<
847
 
            CUtils::radioState2String( m_radio->state() ) );
848
 
 
849
 
        // Bottom line here is that we must wait until the radio thread has stopped
850
 
        // and it has dealt with all outstanding events.
851
 
        do
852
 
        {
853
 
            QApplication::processEvents();
854
 
            #ifdef WIN32
855
 
                Sleep( 10 );
856
 
            #else
857
 
                usleep( 100 );
858
 
            #endif
859
 
        }
860
 
        while ( m_radio->state() != State_Stopped &&
861
 
                m_radio->state() != State_Uninitialised &&
862
 
                m_radio->state() != State_Handshaking &&
863
 
                m_radio->state() != State_Handshaken );
864
 
 
865
 
        LOG( 3, "Saving appState\n" );
866
 
 
867
 
        // don't save if hidden as Qt is shit and this doesn't work, we save 
868
 
        // just before hiding too to ensure we always have the state saved correctly
869
 
        if (m_sidebarEnabled)
870
 
            The::settings().setSplitterState( ui.splitter->saveState() );
871
 
 
872
 
        The::settings().setContainerWindowState( windowState() );
873
 
 
874
 
        The::settings().setVolume( ui_playcontrols.volume->value() );
875
 
        The::currentUser().setSidebarEnabled( m_sidebarEnabled );
876
 
 
877
 
        LOG( 3, "Saving config\n" );
878
 
        The::settings().save();
879
 
 
880
 
        LOG( 3, "Shutting down listener\n" );
881
 
        qDebug() << "Shutting down listener";
882
 
        m_listener->Stop();
883
 
 
884
 
        LOG( 3, "Calling exit\n" );
885
 
        QApplication::exit( 0 );
886
 
 
887
 
        qApp->sendPostedEvents( &The::scrobbler(), 0 /*all event types*/ );
888
637
    }
889
638
}
890
639
 
922
671
 
923
672
 
924
673
void
 
674
Container::dragMoveEvent( QDragMoveEvent* event )
 
675
{
 
676
  #ifndef HIDE_RADIO
 
677
    QString url = event->mimeData()->urls().value( 0 ).toString();
 
678
    if ( url.startsWith( "lastfm://" ) )
 
679
    {
 
680
        event->acceptProposedAction();
 
681
    }
 
682
    else
 
683
        event->ignore();
 
684
  #endif // HIDE_RADIO
 
685
}
 
686
 
 
687
 
 
688
void
 
689
Container::dragEnterEvent( QDragEnterEvent* event )
 
690
{
 
691
  #ifndef HIDE_RADIO
 
692
   QString url = event->mimeData()->urls().value( 0 ).toString();
 
693
    if ( url.startsWith( "lastfm://" ) )
 
694
    {
 
695
        event->acceptProposedAction();
 
696
    }
 
697
    else
 
698
        event->ignore();
 
699
  #endif // HIDE_RADIO
 
700
}
 
701
 
 
702
 
 
703
void
 
704
Container::dropEvent( QDropEvent* event )
 
705
{
 
706
  #ifndef HIDE_RADIO
 
707
    QString url = event->mimeData()->urls().value( 0 ).toString();
 
708
    if ( url.startsWith( "lastfm://" ) )
 
709
    {
 
710
        The::radio().playStation( StationUrl( url ) );
 
711
    }
 
712
  #endif // HIDE_RADIO
 
713
}
 
714
 
 
715
 
 
716
bool
 
717
Container::event( QEvent* e )
 
718
{
 
719
    #ifdef Q_WS_MAC
 
720
    //TODO remove when Qt is fixed!
 
721
    if ( e->type() == QEvent::Resize )
 
722
    {
 
723
        // hack as Qt 4.3.1 is b0rked for unified toolbars
 
724
        ui.toolbar->setMaximumWidth( width() );
 
725
    }
 
726
    #endif
 
727
 
 
728
    if ( e->type() == QEvent::Move || e->type() == QEvent::Resize )
 
729
    {
 
730
        // Again, Qt is broken, if maximised the saveGeometry function fails to
 
731
        // save the geometry for the non-maximised state. So instead we must save it
 
732
        // for *every* resize and move event. Yay!
 
733
 
 
734
        if (windowState() != Qt::WindowMaximized)
 
735
            The::settings().setContainerGeometry( saveGeometry() );
 
736
    }
 
737
 
 
738
    if ( e->type() == QEvent::Show )
 
739
    {
 
740
        emit becameVisible();
 
741
    }
 
742
 
 
743
    return QMainWindow::event( e );
 
744
}
 
745
 
 
746
 
 
747
void
925
748
Container::toggleDiscoveryMode()
926
749
{
927
750
    bool enabled = ui.actionToggleDiscoveryMode->isChecked();
928
 
    m_radio->setDiscoveryMode( enabled );
929
 
}
930
 
 
931
 
 
932
 
void
933
 
Container::loadExtensions()
934
 
{
935
 
    m_metaDataExtension = 0;
936
 
 
937
 
    m_sidebar = new SideBarTree;
938
 
    ui.sidebarFrame->layout()->addWidget( m_sidebar );
939
 
    connect( m_sidebar, SIGNAL(statusMessage( QString )), SLOT(statusMessage( QString )) );
940
 
    connect( m_sidebar, SIGNAL(plsShowRestState()), SLOT(showRestState()) );
941
 
    connect( m_sidebar, SIGNAL(plsShowNowPlaying()), SLOT(showMetaDataWidget()) );
942
 
 
943
 
    LOGL( 3, "Added sidebar extension to GUI." );
944
 
 
945
 
    ui.stack->setBackgroundRole( QPalette::Base );
946
 
 
947
 
#ifndef HIDE_RADIO
948
 
    ui.stack->addWidget( m_rest_state_widget = new RestStateWidget );
949
 
    m_rest_state_widget->setFocus();
950
 
 
951
 
    connect( this,              SIGNAL(playStarted()),
952
 
             m_rest_state_widget, SLOT(clear()) );
953
 
 
954
 
    connect( &The::settings(),  SIGNAL(userSwitched( UserSettings& )),
955
 
             m_rest_state_widget, SLOT(clear()) );
956
 
#endif
957
 
 
958
 
 
959
 
    m_metaDataExtension = new MetaDataExtension;
960
 
    m_metaDataExtension->setOwner( ui.stack );
961
 
    ui.stack->addWidget( m_metaDataExtension->gui() );
962
 
 
963
 
    connect( this,                SIGNAL( newSong( MetaData ) ),
964
 
             m_metaDataExtension, SLOT( setMetaData( MetaData ) ) );
965
 
 
966
 
    connect( m_metaDataExtension, SIGNAL( trackModerated( MetaData ) ),
967
 
             ui.songTimeBar,      SLOT( setNewTrack( MetaData ) ) );
968
 
 
969
 
    connect( m_metaDataExtension, SIGNAL( trackModerated( MetaData ) ),
970
 
             this,                SLOT( updateWindowTitle( MetaData ) ) );
971
 
 
972
 
    connect( m_metaDataExtension, SIGNAL( tagButtonClicked() ),
973
 
             this,                SLOT( showTagDialogMD() ) );
974
 
 
975
 
    connect( m_metaDataExtension, SIGNAL( urlHovered( QString ) ),
976
 
             this,                SLOT( displayUrlInStatusBar( QString ) ) );
977
 
 
978
 
    LOGL( 3, "Added metadata extension to GUI." );
979
 
 
980
 
    QString dirPath;
981
 
  #ifdef WIN32
982
 
    // Hack to get it working with VS2005
983
 
    dirPath = qApp->applicationDirPath();
984
 
  #else
985
 
    dirPath = qApp->applicationDirPath() + "/extensions";
986
 
  #endif
987
 
 
988
 
  #ifndef QT_NO_DEBUG
989
 
    dirPath += "/debug";
990
 
  #endif
991
 
 
992
 
    QDir extensionsDir( dirPath );
993
 
 
994
 
    foreach ( QString fileName, extensionsDir.entryList( QDir::Files ) )
995
 
    {
996
 
        if ( !fileName.startsWith( EXTENSION_PREFIX ) || !QLibrary::isLibrary( extensionsDir.absoluteFilePath( fileName ) ) )
997
 
            continue;
998
 
 
999
 
        LOGL( 3, "Trying to load extension: " << fileName );
1000
 
        QObject* plugin = QPluginLoader( extensionsDir.absoluteFilePath( fileName ) ).instance();
1001
 
        if ( plugin )
1002
 
        {
1003
 
            LOGL( 3, "Extension loaded." );
1004
 
            ExtensionInterface *iExtension = qobject_cast<ExtensionInterface *>( plugin );
1005
 
            if ( iExtension )
1006
 
            {
1007
 
                if ( iExtension->name() == "UserInfo Extension" )
1008
 
                {
1009
 
                    m_eUserInfo = iExtension;
1010
 
                }
1011
 
 
1012
 
                else if ( iExtension->name() == "Growl Notifier Extension" )
1013
 
                {
1014
 
                    // Add it to Options
1015
 
                    LOGL( 3, "Loaded a settings extension" );
1016
 
                    m_settingsDialog->addExtension( iExtension );
1017
 
 
1018
 
                    connect( m_metaDataExtension, SIGNAL( metadataFetched( MetaData ) ),
1019
 
                             iExtension, SLOT( notify( MetaData ) ) );
1020
 
 
1021
 
                    m_metaDataExtension->setAlwaysFetchMetaData( true );
1022
 
                }
1023
 
 
1024
 
                else if ( iExtension->hasSettingsPane() )
1025
 
                {
1026
 
                    // Add it to Options
1027
 
                    LOGL( 3, "Loaded a settings extension" );
1028
 
                    m_settingsDialog->addExtension( iExtension );
1029
 
 
1030
 
                    connect( this,     SIGNAL( newSong( MetaData ) ),
1031
 
                             iExtension, SLOT( notify( MetaData ) ) );
1032
 
                }
1033
 
            }
1034
 
        } // endif plugin
1035
 
        else
1036
 
        {
1037
 
            LOGL( 1, "Failed to load " << fileName );
1038
 
        }
1039
 
    }
 
751
    The::radio().setDiscoveryMode( enabled );
1040
752
}
1041
753
 
1042
754
 
1046
758
    QUrl realUrl( QUrl::fromEncoded( url.toLatin1() ) );
1047
759
    QString msg = realUrl.toString();
1048
760
 
1049
 
    if ( msg != statusBar()->currentMessage() )
1050
 
    {
1051
 
        statusBar()->showMessage( msg );
1052
 
    }
 
761
    statusBar()->showMessage( msg );
1053
762
}
1054
763
 
1055
764
 
1058
767
{
1059
768
  #ifndef HIDE_RADIO
1060
769
 
1061
 
    QString dialogTitle = tr( "Last.fm" );
 
770
    QString msgboxTitle = tr("Error");
1062
771
 
1063
772
    switch( error )
1064
773
    {
1067
776
        case Request_BadResponseCode:
1068
777
        case Request_HostNotFound:
1069
778
        case Request_NoResponse:
1070
 
            statusBar()->showMessage( message );
1071
 
            break;
1072
 
 
1073
 
        case Handshake_WrongUserNameOrPassword:
1074
 
            LastMessageBox::critical( dialogTitle, message );
1075
 
            statusBar()->showMessage( message );
1076
 
 
 
779
        {
 
780
            statusBar()->showMessage( message );
 
781
        }
 
782
        break;
 
783
 
 
784
        case Request_ProxyAuthenticationRequired:
 
785
        {
 
786
            //TEMP:
 
787
            FailedLoginDialog( this ).exec();
 
788
        }
 
789
        break;
 
790
 
 
791
        case Request_WrongUserNameOrPassword:
 
792
        {
 
793
            statusBar()->showMessage( message );
1077
794
            showSettingsDialog();
1078
 
            break;
 
795
        }
 
796
        break;
1079
797
 
1080
798
        case Handshake_Banned:
 
799
        {
1081
800
            //TODO make a nicer shutdown (or start upgrade process) via a signal to the container
1082
 
            LastMessageBox::critical( dialogTitle, message );
 
801
            LastMessageBox::critical( tr("Bad Version"), message );
1083
802
            qApp->quit();
1084
 
            break;
 
803
        }
 
804
        break;
1085
805
 
1086
806
        // Critical box
1087
807
        case Handshake_SessionFailed:
1089
809
        case Radio_NoSoundcard:
1090
810
        case Radio_PlaybackError:
1091
811
        case Radio_UnknownError:
 
812
        {
1092
813
            statusBar()->showMessage( "" );
1093
 
            LastMessageBox::critical( dialogTitle, message );
1094
 
 
1095
 
            break;
 
814
            LastMessageBox::critical( msgboxTitle, message );
 
815
        }
 
816
        break;
1096
817
 
1097
818
        // Info box
1098
819
        case Radio_SkipLimitExceeded:
1113
834
        case Radio_TooManyRetries:
1114
835
        case Radio_ConnectionRefused:
1115
836
        case Playlist_RecSysDown:
1116
 
            statusBar()->showMessage( "" );
1117
 
            LastMessageBox::information( dialogTitle, message );
1118
 
            break;
 
837
        {
 
838
            statusBar()->clearMessage();
 
839
            LastMessageBox::information( msgboxTitle, message );
 
840
        }
 
841
        break;
1119
842
 
1120
843
        default:
1121
844
            Q_ASSERT( !"Is it correct to leave some unhandled?" );
1153
876
        centralWidget()->setContentsMargins( 0, 0, 5, 0 ); //aesthetics
1154
877
        #endif
1155
878
        icon = IconShack::instance().GetGoodUserIconCollapsed( The::currentUser().icon() );
 
879
        ui.splitter->setSizes( QList<int>() << m_sidebarWidth );
1156
880
    }
1157
881
 
1158
 
    ui.actionSidebar->setChecked( m_sidebarEnabled );
1159
 
    m_sidebarActionWithIcon->setChecked( m_sidebarEnabled  );
1160
 
    m_sidebar->parentWidget()->setVisible( m_sidebarEnabled );
1161
 
    m_sidebarActionWithIcon->setIcon( icon );
 
882
    ui.actionMyProfile->setChecked( m_sidebarEnabled );
 
883
    ui.actionMyProfile->setIcon( icon );
 
884
    ui.sidebar->parentWidget()->setVisible( m_sidebarEnabled );
1162
885
}
1163
886
 
1164
887
 
1166
889
Container::getPlugin()
1167
890
{
1168
891
    ConfigWizard( this, ConfigWizard::Plugin ).exec();
1169
 
 
1170
 
#ifndef HIDE_RADIO
1171
 
    m_rest_state_widget->updatePlayerNames();
1172
 
#endif
1173
 
}
1174
 
 
1175
 
 
1176
 
void
1177
 
Container::addMediaDevice( const QString& uid )
1178
 
{
1179
 
    ConfigWizard cw( this, ConfigWizard::MediaDevice, uid );
1180
 
    cw.setWindowFlags( Qt::Sheet );
1181
 
    if ( !cw.isWizardRunning() )
1182
 
        cw.exec();
 
892
    ui.restStateWidget->updatePlayerNames();
1183
893
}
1184
894
 
1185
895
 
1234
944
 
1235
945
 
1236
946
void
1237
 
Container::listenerException( QString msg )
1238
 
{
1239
 
  #ifndef LASTFM_MULTI_PROCESS_HACK
1240
 
    // Can't do much else than fire up a dialog box of doom at this stage
1241
 
    LastMessageBox::critical( tr( "Plugin Listener Error" ), msg );
1242
 
  #else
1243
 
    // don't make debugging a pita, clearly this isn't meant for release builds though
1244
 
    statusMessage( msg );
1245
 
  #endif
1246
 
}
1247
 
 
1248
 
 
1249
 
void
1250
947
Container::showFAQ()
1251
948
{
1252
 
    new BrowserThread( "http://" + The::settings().localizedHostName() + "/help/faq/" );
 
949
    QDesktopServices::openUrl( "http://" + UnicornUtils::localizedHostName( The::settings().appLanguage() ) + "/help/faq/" );
1253
950
}
1254
951
 
1255
952
 
1256
953
void
1257
954
Container::showForums()
1258
955
{
1259
 
    new BrowserThread( "http://www.last.fm/forum/34905/" );
 
956
    QDesktopServices::openUrl( "http://" + UnicornUtils::localizedHostName( The::settings().appLanguage() ) + "/forum/34905/" );
1260
957
}
1261
958
 
1262
959
 
1263
960
void
1264
961
Container::inviteAFriend()
1265
962
{
1266
 
    QString const user = CUtils::UrlEncodeItem( The::currentUsername() );
 
963
    QByteArray user = QUrl::toPercentEncoding( The::settings().currentUsername() );
1267
964
 
1268
 
    new BrowserThread( "http://" + The::settings().localizedHostName() + "/user/" + user + "/inviteafriend/" );
 
965
    QDesktopServices::openUrl( "http://" + UnicornUtils::localizedHostName( The::settings().appLanguage() ) + "/user/" + user + "/inviteafriend/" );
1269
966
}
1270
967
 
1271
968
 
1273
970
void
1274
971
Container::about()
1275
972
{
1276
 
    AboutDialog( this ).exec();
1277
 
}
1278
 
 
1279
 
 
1280
 
void
1281
 
Container::initTray()
1282
 
{
1283
 
    m_sysTray = new SysTray( this );
1284
 
    m_sysTray->show();
1285
 
 
1286
 
    // Connect user change to tray
1287
 
    connect( &The::settings(), SIGNAL(userSettingsChanged( UserSettings& )),
1288
 
             m_sysTray,          SLOT(setUser( UserSettings& )) );
1289
 
    connect( this,             SIGNAL(newSong( MetaData )),
1290
 
             m_sysTray,          SLOT(setTrack( MetaData )) );
1291
 
}
1292
 
 
1293
 
 
1294
 
void
1295
 
Container::showUserMenu()
1296
 
{
 
973
    AboutDialog* d = findChild<AboutDialog*>();
 
974
    if ( !d )
 
975
    {
 
976
        (d = new AboutDialog( this ))->show();
 
977
        d->setAttribute( Qt::WA_DeleteOnClose );
 
978
    }
 
979
    else
 
980
        d->raise();
 
981
}
 
982
 
 
983
 
 
984
void
 
985
Container::onAboutToShowUserMenu()
 
986
{
 
987
    Q_DEBUG_BLOCK;
 
988
 
1297
989
    // Work out whether we have old items to delete
1298
990
    QList<QAction*> actions = ui.menuUser->actions();
1299
991
    if ( actions.size() != 3 )
1315
1007
        action->setData( "user" );
1316
1008
        action->setText( username );
1317
1009
        action->setCheckable( true );
1318
 
        if ( username == currentUser )
1319
 
        {
1320
 
            action->setChecked( true );
1321
 
        }
 
1010
        action->setChecked( username == currentUser );
1322
1011
 
1323
1012
        ui.menuUser->addAction( action );
1324
1013
    }
1326
1015
 
1327
1016
 
1328
1017
void
1329
 
Container::userSelected( QAction* action )
 
1018
Container::onUserSelected( QAction* action )
1330
1019
{
1331
1020
    The::settings().currentUser().setSidebarEnabled( m_sidebarEnabled );
1332
1021
 
1337
1026
 
1338
1027
        if (!The::settings().user( username ).rememberPass())
1339
1028
        {
1340
 
            LoginDialog( this, LoginWidget::LOGIN, username ).exec();
 
1029
            LoginWidget( this, LoginWidget::LOGIN, username ).createDialog().exec();
1341
1030
        }
1342
1031
        else
1343
1032
        {
1344
 
            The::settings().setCurrentUsername( username );
1345
 
            The::settings().save( true, false );
 
1033
            The::app().setUser( username );
1346
1034
        }
1347
1035
    }
1348
1036
}
1351
1039
void
1352
1040
Container::addUser()
1353
1041
{
1354
 
    LoginDialog( this, LoginWidget::ADD_USER ).exec();
 
1042
    LoginWidget( this, LoginWidget::ADD_USER ).createDialog().exec();
1355
1043
}
1356
1044
 
1357
1045
 
1391
1079
 
1392
1080
 
1393
1081
void
1394
 
Container::updateUserStuff( UserSettings& user )
 
1082
Container::updateUserStuff( LastFmUserSettings& user )
1395
1083
{
1396
1084
    Q_DEBUG_BLOCK;
1397
1085
 
1398
 
    //FIXME called twice on startup!
1399
 
 
1400
 
    updateWindowTitle( m_metaData );
 
1086
    updateWindowTitle( The::app().currentTrack() );
1401
1087
 
1402
1088
    QPixmap pix = m_sidebarEnabled
1403
1089
            ? IconShack::instance().GetGoodUserIconCollapsed( user.icon() )
1404
1090
            : IconShack::instance().GetGoodUserIconExpanded( user.icon() );
1405
 
    m_sidebarActionWithIcon->setIcon( QIcon( pix ) );
 
1091
    ui.actionMyProfile->setIcon( QIcon( pix ) );
1406
1092
 
1407
1093
    bool const enabled = user.isLogToProfile();
1408
1094
 
1409
1095
    ui.songTimeBar->setScrobblingEnabled( enabled );
1410
1096
    ui.actionToggleScrobbling->setChecked( enabled );
1411
1097
    ui.actionToggleDiscoveryMode->setChecked( user.isDiscovery() );
1412
 
    
 
1098
 
1413
1099
    ui.scrobbleLabel->setEnabled( enabled );
1414
1100
}
1415
1101
 
1416
1102
 
1417
1103
void
1418
 
Container::onRadioBuffering( int size,
1419
 
                             int total )
 
1104
Container::updateAppearance()
 
1105
{
 
1106
    Q_DEBUG_BLOCK;
 
1107
 
 
1108
    if ( !The::settings().showTrayIcon() )
 
1109
        m_trayIcon->hide();
 
1110
    else
 
1111
        m_trayIcon->show();
 
1112
}
 
1113
 
 
1114
 
 
1115
void
 
1116
Container::onRadioBuffering( int size, int total )
1420
1117
{
1421
1118
  #ifndef HIDE_RADIO
1422
1119
 
1437
1134
 
1438
1135
 
1439
1136
void
1440
 
Container::setNewSong( TrackInfo data, bool songStarted )
1441
 
{
1442
 
    // The assumption that a new track has just started can't be made here.
1443
 
    // It could just be that the radio was stopped and an underlying local
1444
 
    // track became visible.
1445
 
 
1446
 
    m_metaData = data;
1447
 
 
1448
 
    // Reset GUI
1449
 
    ui.songTimeBar->clear();
1450
 
    ui.songTimeBar->setEnabled( false );
1451
 
    ui.songTimeBar->setClockText( "" );
1452
 
 
1453
 
    ui.actionTag->setEnabled( false );
1454
 
    ui.actionRecommend->setEnabled( false );
1455
 
    ui.actionLove->setEnabled( false );
1456
 
    ui.actionAddToMyPlaylist->setEnabled( false );
1457
 
 
1458
 
    if ( !songStarted )
1459
 
    {
1460
 
        // Playback stopped
1461
 
        m_stopped = true;
1462
 
        emit newSong( m_metaData ); //empty TrackInfo object
1463
 
 
1464
 
        // we disable this sometimes from ChangeStationRequest results
1465
 
        // but enable it again when we stop
1466
 
        ui.actionToggleDiscoveryMode->setEnabled( true );
1467
 
 
1468
 
        // we use a timer as some players say they stop before starting a new
1469
 
        // track, which results in us flickering between the two widgets, and 
1470
 
        // that looks shite
1471
 
        if (ui.stack->currentIndex() != 0)
1472
 
            m_showRestStateTimer->start();
1473
 
    }
1474
 
    else if ( m_metaData.scrobblableStatus() != TrackInfo::OkToScrobble &&
1475
 
              m_metaData.source() != TrackInfo::Radio )
1476
 
    {
1477
 
        // Invalid track, all we do is update the song bar
1478
 
        displayUnscrobblableStatus( m_metaData.scrobblableStatus() );
1479
 
        m_metaDataExtension->clear();
1480
 
    }
1481
 
    else
1482
 
    {
1483
 
        // Playback started
1484
 
 
1485
 
        if (The::currentUser().isLogToProfile())
1486
 
            The::scrobbler().nowPlaying( data );
1487
 
 
1488
 
        // The reason for these extra checks is that one user had
1489
 
        // crashes here (bug #56). This could happen if the player listener
1490
 
        // receives a START, emits a queued signal, then immediately
1491
 
        // receives a STOP. If the first queued signal doesn't get here
1492
 
        // until after the STOP, GetActivePlayer will return NULL.
1493
 
        CPlayerConnection* plyr = m_listener->GetActivePlayer();
1494
 
        if ( plyr == NULL )
1495
 
        {
1496
 
            LOG( 1, "Caught a NULL player in setNewSong, not setting stopwatch" );
1497
 
        }
1498
 
        else
1499
 
        {
1500
 
            StopWatch& watch = plyr->GetStopWatch();
1501
 
            ui.songTimeBar->setStopWatch( &watch );
1502
 
            ui.songTimeBar->setReverse( true );
1503
 
            //ui.songTimeBar->setMaximum( data.scrobbleTime() );
1504
 
            if ( plyr->IsScrobbled() )
1505
 
            {
1506
 
                setTrackScrobbled();
1507
 
            }
1508
 
            else
1509
 
            {
1510
 
                ui.songTimeBar->setClockText( "" );
1511
 
            }
1512
 
        }
1513
 
 
1514
 
        ui.actionTag->setEnabled( true );
1515
 
        ui.actionRecommend->setEnabled( true );
1516
 
        ui.actionLove->setEnabled( true );
1517
 
        ui.actionAddToMyPlaylist->setEnabled( true );
1518
 
 
1519
 
        QHash<QString, QString> itemdata;
1520
 
        itemdata.insert( "artist", data.artist() );
1521
 
        itemdata.insert( "track", data.track() );
1522
 
        ui.songTimeBar->setItemData( itemdata );
1523
 
 
1524
 
        // Only display scrobble timer if we're logging to profile and the track
1525
 
        // is scrobblable.
1526
 
        bool showScrobbleTimer = The::settings().currentUser().isLogToProfile()
1527
 
                         && m_metaData.scrobblableStatus() == TrackInfo::OkToScrobble;
1528
 
        ui.songTimeBar->setProgressEnabled( showScrobbleTimer );
1529
 
        ui.songTimeBar->setClockEnabled( true );
1530
 
        ui.songTimeBar->setMode( m_metaData.source() );
1531
 
 
1532
 
        if ( m_stopped )
1533
 
        {
1534
 
            m_stopped = false;
1535
 
            emit playStarted();
1536
 
 
1537
 
            ui.stack->setCurrentIndex( 1 );
1538
 
            m_showRestStateTimer->stop();
1539
 
        }
1540
 
 
1541
 
        // Clear any status messages on the start of a new track or it will look odd
1542
 
        if ( data.source() != TrackInfo::Radio )
1543
 
        {
1544
 
            statusBar()->clearMessage();
1545
 
        }
1546
 
 
1547
 
        emit newSong( m_metaData );
1548
 
    }
1549
 
 
1550
 
    updateWindowTitle( m_metaData );
1551
 
}
1552
 
 
1553
 
 
1554
 
void
1555
 
Container::displayUnscrobblableStatus( TrackInfo::ScrobblableStatus status )
1556
 
{
1557
 
    Q_ASSERT( status != TrackInfo::OkToScrobble );
1558
 
 
1559
 
    QString text = tr( "Can't scrobble: %1" );
1560
 
 
1561
 
    switch ( status )
1562
 
    {
1563
 
        case TrackInfo::NoTimeStamp:
1564
 
        {
1565
 
            text = text.arg( tr( "missing start time" ) );
1566
 
        }
1567
 
        break;
1568
 
 
1569
 
        case TrackInfo::TooShort:
1570
 
        {
1571
 
            text = text.arg( tr( "track too short" ) );
1572
 
        }
1573
 
        break;
1574
 
 
1575
 
        case TrackInfo::ArtistNameMissing:
1576
 
        case TrackInfo::TrackNameMissing:
1577
 
        {
1578
 
            text = text.arg( tr( "artist or title missing from ID3 tag" ) );
1579
 
        }
1580
 
        break;
1581
 
 
1582
 
        case TrackInfo::ExcludedDir:
1583
 
        {
1584
 
            text = tr( "Won't scrobble: track is in directory set to not scrobble" );
1585
 
        }
1586
 
        break;
1587
 
 
1588
 
        case TrackInfo::ArtistInvalid:
1589
 
        {
1590
 
            text = text.arg( tr( "invalid artist name" ) );
1591
 
        }
1592
 
        break;
1593
 
 
1594
 
        default:
1595
 
            Q_ASSERT( !"Shouldn't happen" );
1596
 
    }
1597
 
 
1598
 
    ui.songTimeBar->setText( text );
1599
 
}
1600
 
 
1601
 
 
1602
 
void
1603
1137
Container::onScrobblerStatusChange( int code, const QVariant& data )
1604
1138
{
1605
1139
    switch (code)
1606
1140
    {
1607
1141
        case Scrobbler::ErrorBadAuthorisation:
1608
 
            //TODO this can prolly be removed, but is untested and NOW is bug fix branch
1609
 
            m_sysTray->setUser( The::currentUser() );
1610
 
 
1611
1142
            //NOTE radio is likely to fail auth too, so don't show 2 dialogs
1612
1143
            statusBar()->showMessage( tr("Your username and password are incorrect") );
1613
1144
            break;
1614
1145
 
1615
1146
        case Scrobbler::Connecting:
1616
 
            statusBar()->showMessage( tr("Connecting to Last.fm...") );
 
1147
            //TODO status system with temp messages or queue or something
 
1148
            //statusBar()->showMessage( tr("Connecting to Last.fm...") );
1617
1149
            break;
1618
1150
 
1619
1151
        case Scrobbler::Handshaken:
1620
 
            //FIXME we have to to do this due to BadAuthorisation state
1621
 
            //TODO see above, prolly can be removed?
1622
 
            m_sysTray->setUser( The::currentUser() );
1623
1152
            break;
1624
1153
 
1625
1154
        case Scrobbler::Scrobbling:
1626
 
            statusBar()->showMessage( tr( "Scrobbling %n track(s)...", "", data.toInt() ) );
 
1155
            // we say submitting because we say "scrobbled" at 50%
 
1156
            statusBar()->showMessage( tr( "Submitting %n scrobble(s)...", "", data.toInt() ) );
1627
1157
            break;
1628
1158
 
1629
1159
        case Scrobbler::TracksScrobbled:
1630
 
            statusBar()->showMessage( tr( "%n track(s) scrobbled", "", data.toInt() ) );
 
1160
            // we say submitting because we say "scrobbled" at 50%
 
1161
            statusBar()->showMessage( tr( "%n scrobble(s) submitted", "", data.toInt() ) );
1631
1162
            break;
1632
1163
 
1633
1164
        case Scrobbler::TracksNotScrobbled:
1634
 
            statusBar()->showMessage( tr( "%n track(s) will be scrobbled later", "", data.toInt() ) );
 
1165
            statusBar()->showMessage( tr( "%n scrobble(s) will be submitted later", "", data.toInt() ) );
1635
1166
            break;
1636
1167
 
1637
1168
        case Scrobbler::ErrorBannedClient:
1638
 
            LastMessageBox::critical( tr("Error"), tr( "This software is too old, please upgrade." ) );
 
1169
            LastMessageBox::critical( tr("Old Version"), tr( "This software is too old, please upgrade." ) );
1639
1170
            break;
1640
1171
 
1641
1172
        case Scrobbler::ErrorBadTime:
1642
 
            LastMessageBox::critical( tr("Error"), tr( "Either your computer's clock is inaccurate or your timezone is set incorrectly. Last.fm cannot authorise any scrobbling. :(" ) );
 
1173
            LastMessageBox::critical( tr("Error"), 
 
1174
                tr( "<p>Last.fm cannot authorise any scrobbling! :("
 
1175
                    "<p>It appears your computer disagrees with us about what the time is."
 
1176
                    "<p>If you are sure the time is right, check the date is correct and check your "
 
1177
                       "timezone is not set to something miles away, like Mars." 
 
1178
                    "<p>We're sorry about this restriction, but we impose it to help prevent "
 
1179
                       "scrobble spamming." ) );
1643
1180
            break;
1644
1181
    }
1645
1182
}
1646
1183
 
1647
1184
 
1648
1185
void
1649
 
Container::onTrackScrobbled( const TrackInfo& track )
1650
 
{
1651
 
    Q_DEBUG_BLOCK;
1652
 
 
1653
 
    //TODO move into sidebar itself
1654
 
 
1655
 
    setTrackScrobbled();
1656
 
 
1657
 
    Track t;
1658
 
    t.setArtist( track.artist() );
1659
 
    t.setTitle( track.track() );
1660
 
 
1661
 
    m_sidebar->addRecentlyPlayedTrack( t );
1662
 
}
1663
 
 
1664
 
 
1665
 
void
1666
 
Container::setTrackScrobbled()
1667
 
{
1668
 
    ui.songTimeBar->pushClockText( tr( "scrobbled" ), 5 );
1669
 
}
1670
 
 
1671
 
 
1672
 
void
1673
1186
Container::showTagDialog( int defaultTagType )
1674
1187
{
1675
 
    TagDialog d( defaultTagType, this );
1676
 
    d.setSong( m_metaData );
 
1188
    TagDialog d( The::app().currentTrack(), this );
 
1189
    if (defaultTagType >= 0)
 
1190
        d.setTaggingType( defaultTagType );
1677
1191
    d.exec();
1678
1192
}
1679
1193
 
1688
1202
void
1689
1203
Container::play()
1690
1204
{
1691
 
    m_radio->resumeStation();
 
1205
    The::radio().resumeStation();
1692
1206
}
1693
1207
 
1694
1208
 
1695
1209
void
1696
1210
Container::stop()
1697
1211
{
1698
 
    m_radio->stop();
 
1212
    The::radio().stop();
1699
1213
}
1700
1214
 
1701
1215
 
1702
1216
void
1703
1217
Container::volumeUp()
1704
1218
{
1705
 
    if ( ui_playcontrols.volume->value() != 100 )
 
1219
    if ( ui.playcontrols.volume->value() != 100 )
1706
1220
    {
1707
 
        if ( ui_playcontrols.volume->value() + 5 > 100 )
1708
 
            ui_playcontrols.volume->setValue( 100 );
 
1221
        if ( ui.playcontrols.volume->value() + 5 > 100 )
 
1222
            ui.playcontrols.volume->setValue( 100 );
1709
1223
        else
1710
 
            ui_playcontrols.volume->setValue( ui_playcontrols.volume->value() + 5 );
 
1224
            ui.playcontrols.volume->setValue( ui.playcontrols.volume->value() + 5 );
1711
1225
    }
1712
1226
}
1713
1227
 
1715
1229
void
1716
1230
Container::volumeDown()
1717
1231
{
1718
 
    if ( ui_playcontrols.volume->value() != 0 )
 
1232
    if ( ui.playcontrols.volume->value() != 0 )
1719
1233
    {
1720
 
        if ( ui_playcontrols.volume->value() - 5 < 0 )
1721
 
            ui_playcontrols.volume->setValue( 0 );
 
1234
        if ( ui.playcontrols.volume->value() - 5 < 0 )
 
1235
            ui.playcontrols.volume->setValue( 0 );
1722
1236
        else
1723
 
            ui_playcontrols.volume->setValue( ui_playcontrols.volume->value() - 5 );
 
1237
            ui.playcontrols.volume->setValue( ui.playcontrols.volume->value() - 5 );
1724
1238
    }
1725
1239
}
1726
1240
 
1728
1242
void
1729
1243
Container::mute()
1730
1244
{
1731
 
    if ( ui_playcontrols.volume->value() != 0 )
 
1245
    if ( ui.playcontrols.volume->value() != 0 )
1732
1246
    {
1733
 
        m_lastVolume = ui_playcontrols.volume->value();
1734
 
        ui_playcontrols.volume->setValue( 0 );
 
1247
        m_lastVolume = ui.playcontrols.volume->value();
 
1248
        ui.playcontrols.volume->setValue( 0 );
1735
1249
    }
1736
1250
    else
1737
 
        ui_playcontrols.volume->setValue( m_lastVolume );
 
1251
        ui.playcontrols.volume->setValue( m_lastVolume );
1738
1252
}
1739
1253
 
1740
1254
 
1742
1256
Container::addToMyPlaylist()
1743
1257
{
1744
1258
    // Make copy in case dialog stays on screen across track change
1745
 
    MetaData track = m_metaData;
 
1259
    MetaData track = The::app().currentTrack();
1746
1260
 
1747
 
    QString confirmMsg = QString( tr( "Are you sure you want to add %1 to your playlist?" ) )
1748
 
        .arg( track.track() );
1749
 
    ConfirmDialog dlg( ConfirmDialog::AddToMyPlaylist, confirmMsg, this );
1750
 
    if ( dlg.exec() == QDialog::Accepted )
 
1261
    if (ConfirmDialog::playlist( track, this ))
1751
1262
    {
1752
 
        ui.actionAddToMyPlaylist->setEnabled( false );
1753
 
        
 
1263
        ui.actionPlaylist->setEnabled( false );
1754
1264
        (new AddToMyPlaylistRequest( track ))->start();
1755
1265
    }
1756
1266
}
1757
1267
 
 
1268
 
1758
1269
void
1759
1270
Container::love()
1760
1271
{
1761
 
    // used by scrobble submission
1762
 
    m_metaData.setUserActionFlag( TrackInfo::Loved );
1763
 
    m_scrobbler->scrobble( m_metaData ); //HACK won't be scrobbled yet.
1764
 
    
1765
1272
    // Make copy in case dialog stays on screen across track change
1766
 
    MetaData track = m_metaData;
1767
 
 
1768
 
    QString confirmMsg = QString( tr( "Are you sure you want to add %1 to your loved tracks?" ) )
1769
 
                         .arg( track.track() );
1770
 
    ConfirmDialog dlg( ConfirmDialog::Love, confirmMsg, this );
1771
 
 
1772
 
    if ( dlg.exec() == QDialog::Accepted )
 
1273
    MetaData track = The::app().currentTrack();
 
1274
 
 
1275
    if (ConfirmDialog::love( track, this ))
1773
1276
    {
1774
1277
        ui.actionLove->setEnabled( false );
1775
1278
 
1776
1279
        (new LoveRequest( track ))->start();
1777
1280
    }
 
1281
 
 
1282
    // HACK: Only reason it's OK to do this is because the scrobbler will
 
1283
    // make sure to not scrobble duplicates.
 
1284
    track.setRatingFlag( TrackInfo::Loved );
 
1285
    The::app().scrobbler().scrobble( track );
1778
1286
}
1779
1287
 
1780
1288
 
1781
1289
void
1782
1290
Container::ban()
1783
1291
{
1784
 
    // used by scrobble submission
1785
 
    m_metaData.setUserActionFlag( TrackInfo::Banned );    
1786
 
    m_scrobbler->scrobble( m_metaData ); //HACK won't be scrobbled yet.
1787
 
 
1788
1292
    // Make copy in case dialog stays on screen across track change
1789
 
    MetaData track = m_metaData;
1790
 
 
1791
 
    QString confirmMsg = QString( tr( "Are you sure you want to ban %1?" ) )
1792
 
        .arg( track.track() );
1793
 
    ConfirmDialog dlg( ConfirmDialog::Ban, confirmMsg, this );
1794
 
 
1795
 
    if ( dlg.exec() == QDialog::Accepted )
 
1293
    MetaData track = The::app().currentTrack();
 
1294
 
 
1295
    if (ConfirmDialog::ban( track, this ))
1796
1296
    {
1797
1297
        ui.actionSkip->setEnabled( false );
1798
1298
        ui.actionLove->setEnabled( false );
1799
1299
        ui.actionBan->setEnabled( false );
1800
 
        ui.actionAddToMyPlaylist->setEnabled( false );
 
1300
        ui.actionPlaylist->setEnabled( false );
1801
1301
 
1802
1302
        (new BanRequest( track ))->start();
1803
1303
 
1804
1304
        // Listener needs to know about banned tracks so that we can prevent
1805
1305
        // them from getting submitted.
1806
 
        CPlayerConnection* player = m_listener->GetActivePlayer();
 
1306
        CPlayerConnection* player = The::app().listener().GetActivePlayer();
1807
1307
        if ( player != NULL )
1808
1308
        {
1809
1309
            player->ban();
1810
1310
        }
1811
1311
 
1812
 
        m_radio->skip();
 
1312
        // HACK: Only reason it's OK to do this is because the scrobbler will
 
1313
        // make sure to not scrobble duplicates.
 
1314
        track.setRatingFlag( TrackInfo::Banned );
 
1315
        The::app().scrobbler().scrobble( track );
 
1316
 
 
1317
        The::radio().skip();
1813
1318
    }
1814
1319
}
1815
1320
 
1820
1325
    ui.actionSkip->setEnabled( false );
1821
1326
    ui.actionLove->setEnabled( false );
1822
1327
    ui.actionBan->setEnabled( false );
1823
 
    ui.actionAddToMyPlaylist->setEnabled( false );
 
1328
    ui.actionPlaylist->setEnabled( false );
1824
1329
 
1825
1330
    ui.songTimeBar->clear();
1826
1331
    ui.songTimeBar->setEnabled( false );
1827
 
    ui.songTimeBar->setClockText( "" );
1828
1332
    ui.songTimeBar->setText( tr( "Skipping..." ) );
1829
1333
 
1830
 
////// HACK to allow us to set the skip flag for the scrobble
1831
 
    // See ScrobbleManager::scrobble()
1832
 
    m_metaData.setUserActionFlag( TrackInfo::Skipped );
1833
 
    m_scrobbler->scrobble( m_metaData );
1834
 
 
1835
 
//////
1836
 
    m_radio->skip();
1837
 
}
1838
 
 
1839
 
 
1840
 
void
1841
 
Container::switchPlayButton( bool enabled )
1842
 
{
1843
 
    if ( enabled )
1844
 
    {
1845
 
        ui.actionStop->setVisible( false );
1846
 
        ui.actionPlay->setVisible( true );
1847
 
 
1848
 
        ui.actionPlay->setShortcut( Qt::Key_Space );
1849
 
        ui.actionStop->setShortcut( QKeySequence() );
1850
 
    }
1851
 
    else
1852
 
    {
1853
 
        ui.actionPlay->setVisible( false );
1854
 
        ui.actionStop->setVisible( true );
1855
 
 
1856
 
        ui.actionPlay->setShortcut( QKeySequence() );
1857
 
        ui.actionStop->setShortcut( Qt::Key_Space );
1858
 
    }
1859
 
 
1860
 
    #ifdef Q_WS_MAC
1861
 
        m_sysTrayActionPlay->setVisible( ui.actionPlay->isVisible() );
1862
 
        m_sysTrayActionStop->setVisible( ui.actionStop->isVisible() );
1863
 
    #endif
1864
 
}
1865
 
 
1866
 
 
1867
 
void
1868
 
Container::onRadioStateChange( RadioState newState )
1869
 
{
1870
 
  #ifndef HIDE_RADIO
1871
 
 
1872
 
    switch ( newState )
1873
 
    {
1874
 
        case State_Uninitialised:
1875
 
        {
1876
 
            // Nothing
1877
 
        }
1878
 
        break;
1879
 
 
1880
 
        case State_Handshaking:
1881
 
        {
1882
 
            statusBar()->showMessage( tr( "Contacting radio service..." ) );
1883
 
        }
1884
 
        break;
1885
 
 
1886
 
        case State_Handshaken:
1887
 
        {
1888
 
            LOGL( 3, "Radio streamer handshake successful." );
1889
 
            statusBar()->showMessage( tr( "Radio service initialised" ) );
1890
 
 
1891
 
            // Play is disabled in ctor so enable it when handshake is complete
1892
 
            // to indicate we can now start radio.
1893
 
            switchPlayButton( true );
1894
 
            ui.actionPlay->setEnabled( true );
1895
 
 
1896
 
            #ifndef HIDE_RADIO
1897
 
            m_rest_state_widget->setPlayEnabled( true );
1898
 
            #endif
1899
 
 
1900
 
            // do we have to load a station since we got started with a cli-arg?
1901
 
            m_iControl->setHandshaked();
1902
 
            if ( m_preloadStation.contains( "lastfm://" ) )
1903
 
            {
1904
 
                m_radio->playStation( m_preloadStation );
1905
 
                m_preloadStation.clear();
1906
 
            }
1907
 
            else if ( The::settings().currentUser().resumePlayback() &&
1908
 
                     !The::settings().currentUser().resumeStation().isEmpty() )
1909
 
            {
1910
 
                m_radio->playStation( The::settings().currentUser().resumeStation() );
1911
 
            }
1912
 
 
1913
 
            #ifndef Q_WS_X11
1914
 
            if ( !The::settings().isFirstRun() )
1915
 
                // Don't do this until here as we won't have received a base host
1916
 
                // earlier.
1917
 
                checkForUpdates( false );
1918
 
            #endif
1919
 
        }
1920
 
        break;
1921
 
 
1922
 
        case State_ChangingStation:
1923
 
        {
1924
 
            statusBar()->showMessage( tr("Starting station %1...").arg( m_radio->stationUrl() ) );
1925
 
 
1926
 
            showMetaDataWidget();
1927
 
            m_metaDataExtension->displayTuningIn();
1928
 
 
1929
 
            if ( m_radio->stationUrl().isPlaylist() )
1930
 
                ui.stationTimeBar->setText( tr( "Connecting to playlist..." ) );
1931
 
            else
1932
 
                ui.stationTimeBar->setText( tr( "Starting station..." ) );
1933
 
 
1934
 
            ui.stationTimeBar->setClockText( "" );
1935
 
            ui.stationTimeBar->setEnabled( true );
1936
 
            ui.stationTimeBar->setVisible( true );
1937
 
 
1938
 
            switchPlayButton( false );
1939
 
            ui.actionStop->setEnabled( true );
1940
 
        }
1941
 
        break;
1942
 
 
1943
 
        case State_FetchingPlaylist:
1944
 
        {
1945
 
            // Need to apply this again in case we ran out of playlist
1946
 
            switchPlayButton( false );
1947
 
            ui.actionStop->setEnabled( true );
1948
 
 
1949
 
            showMetaDataWidget();
1950
 
            m_metaDataExtension->displayTuningIn();
1951
 
 
1952
 
            statusBar()->showMessage( tr( "Retrieving playlist..." ) );
1953
 
        }
1954
 
        break;
1955
 
 
1956
 
        case State_FetchingStream:
1957
 
        {
1958
 
            statusBar()->showMessage( tr( "Retrieving stream..." ) );
1959
 
 
1960
 
            // We now have a station name. Bit ugly this, maybe change to a signal?
1961
 
            QString stationText = tr( "Station: %1" ).arg( m_radio->stationName() );
1962
 
            ui.stationTimeBar->setText( stationText );
1963
 
 
1964
 
            // Need to apply this again in case we resumed
1965
 
            switchPlayButton( false );
1966
 
            ui.actionStop->setEnabled( true );
1967
 
 
1968
 
            ui.stationTimeBar->setEnabled( true );
1969
 
            ui.stationTimeBar->setVisible( true );
1970
 
        }
1971
 
        break;
1972
 
 
1973
 
        case State_StreamFetched:
1974
 
        {
1975
 
            // Resuming not possible until we've fetched a stream for the first time
1976
 
            // TODO: this is actually bollocks because the stream could have been a preview
1977
 
            ui.actionPlay->setEnabled( true );
1978
 
            #ifndef HIDE_RADIO
1979
 
            m_rest_state_widget->setPlayEnabled( true );
1980
 
            #endif
1981
 
        }
1982
 
        break;
1983
 
 
1984
 
        case State_Buffering:
1985
 
        {
1986
 
            // Status bar message handled in onRadioBuffering
1987
 
        }
1988
 
        break;
1989
 
 
1990
 
        case State_Streaming:
1991
 
        {
1992
 
            ui.actionSkip->setEnabled( true );
1993
 
            ui.actionBan->setEnabled( true );
1994
 
        }
1995
 
        break;
1996
 
 
1997
 
        case State_Skipping:
1998
 
        {
1999
 
            ui.actionSkip->setEnabled( false );
2000
 
            ui.actionBan->setEnabled( false );
2001
 
        }
2002
 
        break;
2003
 
 
2004
 
        case State_Stopping:
2005
 
        {
2006
 
            ui.stationTimeBar->clear();
2007
 
            ui.stationTimeBar->setEnabled( false );
2008
 
            ui.stationTimeBar->setVisible( false );
2009
 
 
2010
 
            ui.actionStop->setEnabled( false );
2011
 
            ui.actionSkip->setEnabled( false );
2012
 
            ui.actionBan->setEnabled( false );
2013
 
 
2014
 
            ui.stack->setCurrentIndex( 0 );
2015
 
        }
2016
 
        break;
2017
 
 
2018
 
        case State_Stopped:
2019
 
        {
2020
 
            switchPlayButton( true );
2021
 
            m_stopped = true;
2022
 
            statusBar()->showMessage( "" );
2023
 
        }
2024
 
        break;
2025
 
 
2026
 
        default:
2027
 
            Q_ASSERT( !"Undefined state case reached in onRadioStateChanged!" );
2028
 
    }
2029
 
 
2030
 
  #endif // HIDE_RADIO
 
1334
    // HACK: Only reason it's OK to do this is because the scrobbler will
 
1335
    // make sure to not scrobble duplicates.
 
1336
    MetaData track = The::app().currentTrack();
 
1337
    track.setRatingFlag( TrackInfo::Skipped );
 
1338
    The::app().scrobbler().scrobble( track );
 
1339
 
 
1340
    The::radio().skip();
2031
1341
}
2032
1342
 
2033
1343
 
2034
1344
void
2035
1345
Container::gotoProfile()
2036
1346
{
2037
 
    QString const user = CUtils::UrlEncodeItem( The::settings().currentUsername() );
2038
 
    QString const dashUrl = "http://" + The::settings().localizedHostName() + "/user/" + user;
 
1347
    QByteArray user = QUrl::toPercentEncoding( The::settings().currentUsername() );
2039
1348
 
2040
 
    new BrowserThread( dashUrl );
 
1349
    // This will redirect to the language version the user last used.
 
1350
    //FIXME actually it doesn't
 
1351
    QDesktopServices::openUrl( "http://" + UnicornUtils::localizedHostName( The::settings().appLanguage() ) + "/user/" + user );
2041
1352
}
2042
1353
 
2043
1354
 
2050
1361
 
2051
1362
 
2052
1363
void
2053
 
Container::showRecommendDialog()
2054
 
{
2055
 
    m_recommendDlg->setSong( m_metaData );
2056
 
    m_recommendDlg->exec();
2057
 
}
2058
 
 
2059
 
 
2060
 
void
2061
 
Container::showSettingsDialog()
2062
 
{
2063
 
    m_settingsDialog->exec();
 
1364
Container::showShareDialog()
 
1365
{
 
1366
    m_shareDialog->setSong( The::app().currentTrack() );
 
1367
    m_shareDialog->exec();
 
1368
}
 
1369
 
 
1370
 
 
1371
void
 
1372
Container::showSettingsDialog( int startPage )
 
1373
{
 
1374
    m_settingsDialog->exec( startPage );
 
1375
}
 
1376
 
 
1377
 
 
1378
void
 
1379
Container::showDiagnosticsDialog()
 
1380
{
 
1381
    m_diagnosticsDialog->show();
 
1382
}
 
1383
 
 
1384
 
 
1385
void
 
1386
Container::scrobbleManualIpod()
 
1387
{
 
1388
    QObject* o = QPluginLoader( MooseUtils::servicePath( "Ipod_device" ) ).instance();
 
1389
    MyMediaDeviceInterface* plugin = qobject_cast<MyMediaDeviceInterface*>(o);
 
1390
 
 
1391
    if (plugin)
 
1392
    {
 
1393
        QString path;
 
1394
 
 
1395
        QString const settings_path = "IpodDevice/" + plugin->uniqueId() + "/MountLocation";
 
1396
        path = QFileDialog::getExistingDirectory(
 
1397
                                     this,
 
1398
                                     tr( "Where is your iPod mounted?" ),
 
1399
                                     QSettings().value( settings_path, "/" ).toString() );
 
1400
        if (path.isEmpty())
 
1401
            return;
 
1402
 
 
1403
        QSettings().setValue( settings_path, path );
 
1404
 
 
1405
        plugin->setMountPath( path );
 
1406
 
 
1407
        qApp->setOverrideCursor( Qt::WaitCursor );
 
1408
        QList<TrackInfo> tracks = plugin->tracksToScrobble();
 
1409
        qApp->restoreOverrideCursor();
 
1410
 
 
1411
        qDebug() << "Manual iPod scrobbling found" << tracks.count();
 
1412
 
 
1413
        if (tracks.count())
 
1414
        {
 
1415
            ScrobbleCache::mediaDeviceCache( The::user().name() ).append( tracks );
 
1416
            The::app().sendToInstance( "container://checkScrobblerCache/" + The::user().name() );
 
1417
        }
 
1418
        else if (plugin->error().isEmpty())
 
1419
        {
 
1420
            LastMessageBox::information(
 
1421
                    tr("Nothing to Scrobble"),
 
1422
                    tr("You have not played anything since you last scrobbled this iPod.") );
 
1423
        }
 
1424
        else
 
1425
            LastMessageBox::information( tr("Plugin Error"), plugin->error() );
 
1426
    }
 
1427
    else
 
1428
        LastMessageBox::warning(
 
1429
                tr("Warning"),
 
1430
                tr("There was an error loading the IpodDevice plugin.") );
 
1431
 
 
1432
    delete o;
 
1433
}
 
1434
 
 
1435
 
 
1436
void
 
1437
Container::onTrayIconActivated( QSystemTrayIcon::ActivationReason reason )
 
1438
{
 
1439
    // typical linux behavior is single clicking tray icon toggles the main window
 
1440
 
 
1441
    #ifdef Q_WS_X11
 
1442
    if (reason == QSystemTrayIcon::Trigger)
 
1443
        toggleWindowVisibility();
 
1444
    #else
 
1445
    if (reason == QSystemTrayIcon::DoubleClick)
 
1446
        toggleWindowVisibility();
 
1447
    #endif
2064
1448
}
2065
1449
 
2066
1450
 
2072
1456
#endif
2073
1457
 
2074
1458
void
2075
 
Container::toggleWindowVisibility() 
 
1459
Container::toggleWindowVisibility()
2076
1460
{
2077
1461
    //TODO really we should check to see if the window itself is obscured?
2078
1462
    // hard to say as exact desire of user is a little hard to predict.
2163
1547
            statusBar()->showMessage( tr( "You banned this track." ) );
2164
1548
            break;
2165
1549
 
 
1550
        case TypeChangeStation:
 
1551
            // disable discovery mode if we aren't actually being discoverable
 
1552
            // this isn't really perfect since really we should just alert the user
 
1553
            // that it isn't working, but hell that's hard to do right
 
1554
            ui.actionToggleDiscoveryMode->setEnabled( static_cast<ChangeStationRequest*>(r)->discoverable()
 
1555
                                                      && The::user().isSubscriber() );
 
1556
            break;
 
1557
 
2166
1558
        case TypeUnBan:
2167
1559
            statusBar()->showMessage( tr( "You unbanned this track." ) );
2168
1560
            break;
2171
1563
            statusBar()->showMessage( tr( "This track has been added to your playlist." ) );
2172
1564
            break;
2173
1565
 
2174
 
        case TypeChangeStation:
2175
 
            // disable discovery mode if we aren't actually being discoverable
2176
 
            // this isn't really perfect since really we should just alert the user
2177
 
            // that it isn't working, but hell that's hard to do right
2178
 
            ui.actionToggleDiscoveryMode->setEnabled( static_cast<ChangeStationRequest*>(r)->discoverable() );
 
1566
        case TypeProxyTest:
 
1567
#if QT_VERSION >= 0x040300
 
1568
            if( r->resultCode() == Request_ProxyAuthenticationRequired )
 
1569
            {
 
1570
                LastMessageBox::information( "Proxy Authentication Required",
 
1571
                                             "The proxy autodetection has detected a proxy server but does not have enough information to authenticate.\n\n"
 
1572
                                             "Please set the proxy settings to manual and enter the username and password required." );
 
1573
 
 
1574
                showSettingsDialog( 3 );
 
1575
 
 
1576
 
 
1577
            }
 
1578
#endif
2179
1579
            break;
2180
1580
 
2181
1581
        default:
2204
1604
 
2205
1605
 
2206
1606
void
2207
 
Container::onRestStateTimerTimeout()
2208
 
{
2209
 
    if (!m_metaDataExtension->isTuningIn())
2210
 
        showRestState();
2211
 
}
2212
 
 
2213
 
 
2214
 
void
2215
 
Container::showRestState()
2216
 
{
2217
 
    ui.stack->setCurrentIndex( 0 );
2218
 
}
2219
 
 
2220
 
 
2221
 
void
2222
1607
Container::onAltShiftL()
2223
1608
{
2224
1609
    #ifdef WIN32
2225
1610
        // The QDesktopServices call doesn't work on Windows
2226
 
        QString file = savePath( "container.log" );
 
1611
        QString file = Logger::GetLogger().GetFilePath();
2227
1612
        ShellExecuteW(0, 0, (TCHAR*)file.utf16(), 0, 0, SW_SHOWNORMAL);
2228
1613
    #else
2229
 
        //FIXME get path from Logger instance, DRY!
2230
 
        QDesktopServices::openUrl( QUrl::fromLocalFile( savePath( "container.log" ) ) );
 
1614
        QDesktopServices::openUrl( QUrl::fromLocalFile( Logger::GetLogger().GetFilePath() ) );
2231
1615
    #endif
2232
1616
}
2233
1617
 
2237
1621
{
2238
1622
    #ifdef WIN32
2239
1623
        // The QDesktopServices call doesn't work on Windows
2240
 
        QString file = savePath( "lastfmhelper.log" );
 
1624
        QString file = MooseUtils::logPath( "LastFmHelper.log" );
2241
1625
        ShellExecuteW(0, 0, (TCHAR*)file.utf16(), 0, 0, SW_SHOWNORMAL);
2242
 
    #else
2243
 
        //FIXME get path from Helper, DRY!
2244
 
        QDesktopServices::openUrl( QUrl::fromLocalFile( savePath( "lastfmhelper.log" ) ) );
 
1626
    #elif defined Q_WS_MAC
 
1627
        QDesktopServices::openUrl( QUrl::fromLocalFile( MooseUtils::logPath( "LastFmHelper.log" ) ) );
2245
1628
    #endif
 
1629
 
 
1630
    // no helper for unix!
2246
1631
}
2247
1632
 
2248
1633
 
2251
1636
{
2252
1637
    #ifdef WIN32
2253
1638
        // The QDesktopServices call doesn't work on Windows
2254
 
        QString file = savePath( "" );
 
1639
        QString file = MooseUtils::logPath( "" );
2255
1640
        ShellExecuteW(0, 0, (TCHAR*)file.utf16(), 0, 0, SW_SHOWNORMAL);
2256
1641
    #else
2257
 
        QDesktopServices::openUrl( QUrl::fromLocalFile( savePath( "" ) ) );
 
1642
        QDesktopServices::openUrl( QUrl::fromLocalFile( MooseUtils::logPath( "" ) ) );
2258
1643
    #endif
2259
1644
}
2260
1645
 
2261
1646
 
2262
 
bool
2263
 
Container::isTuningInOrPlaying() const //TODO REMOVE
2264
 
{
2265
 
    if (!m_metaData.isEmpty())
2266
 
        return true;
2267
 
 
2268
 
    if (!m_radio)
2269
 
        return false;
2270
 
 
2271
 
    switch (m_radio->state())
 
1647
#ifdef WIN32
 
1648
#include <shlobj.h>
 
1649
#include <shfolder.h>
 
1650
void
 
1651
Container::onAltShiftP()
 
1652
{
 
1653
    wchar_t path_[MAX_PATH];
 
1654
    SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, 0, NULL, path_ );
 
1655
    
 
1656
    QString path = QString::fromUtf16( (const ushort*)path_ );
 
1657
    path += "\\Last.fm\\Client";
 
1658
 
 
1659
    ShellExecuteW( 0, 0, (TCHAR*)path.utf16(), 0, 0, SW_SHOWNORMAL );
 
1660
}
 
1661
#endif
 
1662
 
 
1663
 
 
1664
void
 
1665
Container::onAppEvent( int event, const QVariant& data )
 
1666
{
 
1667
    //Do not respond to any events if there is no user logged in
 
1668
    if( !&The::user() )
 
1669
        return;
 
1670
        
 
1671
    switch ( event )
2272
1672
    {
2273
 
        case State_Uninitialised:
2274
 
        case State_Handshaking:
2275
 
        case State_Handshaken:
2276
 
        case State_Stopping:
2277
 
        case State_Stopped:
2278
 
            return false;
2279
 
        
2280
 
        case State_ChangingStation:
2281
 
        case State_FetchingPlaylist:
2282
 
        case State_FetchingStream:
2283
 
        case State_StreamFetched:
2284
 
        case State_Buffering:
2285
 
        case State_Streaming:
2286
 
        case State_Skipping:
2287
 
            return true;
 
1673
        case Event::UserChanged:
 
1674
        {
 
1675
            // we must restore state here as we save it in toggleSidebar in order to get
 
1676
            // round the bug in Qt where saveState for the splitter is lost for hidden widgets
 
1677
            m_sidebarEnabled = !The::user().settings().sidebarEnabled();
 
1678
            toggleSidebar();
 
1679
 
 
1680
            ui.restStateWidget->clear();
 
1681
            ui.restStateWidget->updatePlayerNames();
 
1682
 
 
1683
            // this call is redundant. Settings's userSettingsChanged will be emitted when switching the user!
 
1684
//             updateUserStuff( The::user().settings() );
 
1685
 
 
1686
            #ifndef HIDE_RADIO
 
1687
            statusBar()->showMessage( tr( "Contacting radio service..." ) );
 
1688
            #endif
 
1689
        }
 
1690
        break;
 
1691
 
 
1692
        case Event::UserHandshaken:
 
1693
        {
 
1694
            ui.actionToggleDiscoveryMode->setEnabled( The::user().isSubscriber() );
 
1695
            ui.restStateWidget->setPlayEnabled( true );
 
1696
            statusBar()->showMessage( tr( "Radio service initialised" ) );
 
1697
 
 
1698
            #ifndef Q_WS_X11
 
1699
            if ( !The::settings().isFirstRun() )
 
1700
                // Don't do this until here as we won't have received a base host earlier.
 
1701
                checkForUpdates( false );
 
1702
            #endif
 
1703
        }
 
1704
        break;
 
1705
 
 
1706
        case Event::TuningIn:
 
1707
        {
 
1708
            statusBar()->showMessage( tr("Starting station %1...").arg( The::radio().stationUrl() ) );
 
1709
 
 
1710
            showMetaDataWidget();
 
1711
            ui.metaDataWidget->displayTuningIn();
 
1712
 
 
1713
            ui.restStateWidget->clear();
 
1714
 
 
1715
            if ( The::radio().stationUrl().isPlaylist() )
 
1716
                ui.stationTimeBar->setText( tr( "Connecting to playlist..." ) );
 
1717
            else
 
1718
                ui.stationTimeBar->setText( tr( "Starting station..." ) );
 
1719
 
 
1720
            ui.songTimeBar->clear();
 
1721
            ui.stationTimeBar->setClockText( "" );
 
1722
            ui.stationTimeBar->setEnabled( true );
 
1723
            ui.stationTimeBar->setVisible( true );
 
1724
        }
 
1725
        break;
 
1726
 
 
1727
        case Event::PlaybackStarted:
 
1728
        {
 
1729
            TrackInfo track = data.value<TrackInfo>();
 
1730
 
 
1731
            if (track.source() != TrackInfo::Radio)
 
1732
            {
 
1733
                // with radio we already changed to this, and there is a noticeable
 
1734
                // gap between tuning in and starting playback so the user may have
 
1735
                // already switched back to the change station tab
 
1736
                showMetaDataWidget();
 
1737
            }
 
1738
        }
 
1739
        // continue!!
 
1740
 
 
1741
        case Event::TrackChanged:
 
1742
        {
 
1743
            TrackInfo metadata = data.value<TrackInfo>();
 
1744
 
 
1745
            LOGL( 4, "Event::TrackChanged " << metadata.toString() << " " << metadata.durationString() );
 
1746
            ui.songTimeBar->setTrack( metadata );
 
1747
            m_trayIcon->setTrack( metadata );
 
1748
 
 
1749
            if ( MooseUtils::scrobblableStatus( metadata ) != MooseEnums::OkToScrobble &&
 
1750
                 metadata.source() != TrackInfo::Radio )
 
1751
            {
 
1752
                ui.actionTag->setEnabled( false );
 
1753
                ui.actionShare->setEnabled( false );
 
1754
                ui.actionLove->setEnabled( false );
 
1755
            }
 
1756
            else
 
1757
            {
 
1758
                // The reason for these extra checks is that one user had
 
1759
                // crashes here (bug #56). This could happen if the player listener
 
1760
                // receives a START, emits a queued signal, then immediately
 
1761
                // receives a STOP. If the first queued signal doesn't get here
 
1762
                // until after the STOP, GetActivePlayer will return NULL.
 
1763
                CPlayerConnection* plyr = The::app().listener().GetActivePlayer();
 
1764
                if ( !plyr )
 
1765
                {
 
1766
                    LOG( 1, "Caught a NULL player in setNewSong, not setting stopwatch" );
 
1767
                }
 
1768
                else
 
1769
                {
 
1770
                    StopWatch& watch = plyr->GetStopWatch();
 
1771
                    ui.songTimeBar->setStopWatch( &watch );
 
1772
                    ui.songTimeBar->setReverse( true );
 
1773
 
 
1774
                    if ( plyr->IsScrobbled() )
 
1775
                        ui.songTimeBar->pushClockText( tr( "scrobbled" ), 5 );
 
1776
                    else
 
1777
                        ui.songTimeBar->setClockText( "" );
 
1778
                }
 
1779
 
 
1780
                ui.actionShare->setEnabled( true );
 
1781
                ui.actionTag->setEnabled( true );
 
1782
                ui.actionPlaylist->setEnabled( true );
 
1783
                ui.actionLove->setEnabled( true );
 
1784
                ui.songTimeBar->setEnabled( true );
 
1785
 
 
1786
                // Clear any status messages on the start of a new track or it will look odd
 
1787
                if ( metadata.source() != TrackInfo::Radio )
 
1788
                {
 
1789
                    statusBar()->clearMessage();
 
1790
                }
 
1791
            }
 
1792
 
 
1793
            ui.metaDataWidget->setTrackInfo( metadata );
 
1794
            updateWindowTitle( metadata );
 
1795
        }
 
1796
        break;
 
1797
 
 
1798
        case Event::TrackMetaDataAvailable:
 
1799
        {
 
1800
            MetaData metadata = data.value<MetaData>();
 
1801
 
 
1802
            updateWindowTitle( metadata );
 
1803
            ui.metaDataWidget->setTrackMetaData( metadata );
 
1804
 
 
1805
            LOGL( 4, "Event::TrackMetaDataAvailable " << metadata.toString() << " " << metadata.durationString() );
 
1806
            ui.songTimeBar->setTrack( metadata );
 
1807
        }
 
1808
        break;
 
1809
 
 
1810
        case Event::ArtistMetaDataAvailable:
 
1811
        {
 
1812
            ui.metaDataWidget->setArtistMetaData( data.value<MetaData>() );
 
1813
        }
 
1814
        break;
 
1815
 
 
1816
        case Event::PlaybackEnded:
 
1817
        {
 
1818
            // we disable this sometimes from ChangeStationRequest results
 
1819
            // but enable it again when we stop
 
1820
            ui.actionToggleDiscoveryMode->setEnabled( The::user().isSubscriber() );
 
1821
 
 
1822
            statusBar()->clearMessage();
 
1823
            showRestState();
 
1824
            #ifdef HIDE_RADIO
 
1825
                ui.metaDataWidget->displayNotListening();
 
1826
            #endif
 
1827
 
 
1828
            ui.actionShare->setEnabled( false );
 
1829
            ui.actionTag->setEnabled( false  );
 
1830
            ui.actionPlaylist->setEnabled( false );
 
1831
            ui.actionLove->setEnabled( false );
 
1832
 
 
1833
            ui.songTimeBar->setEnabled( false );
 
1834
            ui.songTimeBar->setClockText( "" );
 
1835
            ui.songTimeBar->setClockEnabled( false );
 
1836
            ui.songTimeBar->setText( "" );
 
1837
 
 
1838
            updateWindowTitle( MetaData() );
 
1839
            m_trayIcon->setTrack( TrackInfo() );
 
1840
        }
 
1841
        break;
 
1842
 
 
1843
        case Event::ScrobblePointReached:
 
1844
        {
 
1845
            TrackInfo const track = data.value<TrackInfo>();
 
1846
 
 
1847
            ui.songTimeBar->pushClockText( tr("scrobbled"), 5 );
 
1848
 
 
1849
            Track t;
 
1850
            t.setArtist( track.artist() );
 
1851
            t.setTitle( track.track() );
 
1852
 
 
1853
            ui.sidebar->addRecentlyPlayedTrack( t );
 
1854
        }
 
1855
        break;
 
1856
 
 
1857
        case Event::MediaDeviceTrackScrobbled:
 
1858
        {
 
1859
            TrackInfo const track = data.value<TrackInfo>();
 
1860
 
 
1861
            Track t;
 
1862
            t.setArtist( track.artist() );
 
1863
            t.setTitle( track.track() );
 
1864
 
 
1865
            ui.sidebar->addRecentlyPlayedTrack( t );
 
1866
        }
 
1867
        break;
 
1868
 
 
1869
        case Event::PlaybackPaused:
 
1870
        {
 
1871
            qDebug() << "Paused";
 
1872
        }
 
1873
        break;
 
1874
 
 
1875
        case Event::PlaybackUnpaused:
 
1876
        {
 
1877
            qDebug() << "Unpaused";
 
1878
        }
 
1879
        break;
2288
1880
    }
2289
 
    
2290
 
    return false; //--warning for visual studio
2291
1881
}
2292
1882
 
 
1883
 
2293
1884
void
2294
1885
Container::restoreWindow()
2295
1886
{
2296
 
                showNormal();
2297
 
                activateWindow();
2298
 
                raise();
 
1887
    showNormal();
 
1888
    activateWindow();
 
1889
    raise();
 
1890
}
 
1891
 
 
1892
 
 
1893
/** here we control the radio specific ui components */
 
1894
void
 
1895
Container::onRadioStateChanged( RadioState newState )
 
1896
{
 
1897
    int const state = (int)newState; //use an int so gcc doesn't give us a warning
 
1898
 
 
1899
#ifndef HIDE_RADIO
 
1900
    switch (state)
 
1901
    {
 
1902
        case State_Stopped:
 
1903
            ui.stationTimeBar->clear();
 
1904
            ui.stationTimeBar->setEnabled( false );
 
1905
            ui.stationTimeBar->hide();
 
1906
            break;
 
1907
 
 
1908
        case State_FetchingStream:
 
1909
            // We now have a station name.
 
1910
            ui.stationTimeBar->setText( tr("Station: %1").arg( The::radio().stationName() ) );
 
1911
            ui.stationTimeBar->setEnabled( true );
 
1912
            ui.stationTimeBar->show();
 
1913
 
 
1914
            statusBar()->showMessage( tr( "Retrieving stream..." ) );
 
1915
            break;
 
1916
 
 
1917
        case State_FetchingPlaylist:
 
1918
            ui.metaDataWidget->displayTuningIn();
 
1919
 
 
1920
            statusBar()->showMessage( tr( "Retrieving playlist..." ) );
 
1921
            break;
 
1922
    }
 
1923
 
 
1924
    /** skip/ban */
 
1925
    switch (state)
 
1926
    {
 
1927
        case State_Streaming:
 
1928
            ui.actionSkip->setEnabled( true );
 
1929
            ui.actionBan->setEnabled( true );
 
1930
            break;
 
1931
 
 
1932
        default:
 
1933
            ui.actionSkip->setEnabled( false );
 
1934
            ui.actionBan->setEnabled( false );
 
1935
            break;
 
1936
    }
 
1937
 
 
1938
    // disable actions during certain transitionary, etc. states
 
1939
    switch (state)
 
1940
    {
 
1941
        case State_Uninitialised:
 
1942
        case State_Skipping:
 
1943
        case State_Stopping:
 
1944
        case State_Handshaking:
 
1945
            ui.actionPlay->setEnabled( false );
 
1946
            ui.actionStop->setEnabled( false );
 
1947
            break;
 
1948
 
 
1949
        default:
 
1950
            ui.actionPlay->setEnabled( true );
 
1951
            ui.actionStop->setEnabled( true );
 
1952
            break;
 
1953
    }
 
1954
 
 
1955
    // set the play/stop actions to the correct states
 
1956
    switch (state)
 
1957
    {
 
1958
        case State_Uninitialised:
 
1959
        case State_Handshaking:
 
1960
        case State_Handshaken:
 
1961
        case State_Stopping:
 
1962
        case State_Stopped:
 
1963
            ui.actionPlay->setVisible( true );
 
1964
            ui.actionPlay->setShortcut( Qt::Key_Space );
 
1965
            ui.actionStop->setVisible( false );
 
1966
            ui.actionStop->setShortcut( QKeySequence() );
 
1967
            break;
 
1968
 
 
1969
        default:
 
1970
            ui.actionPlay->setVisible( false );
 
1971
            ui.actionPlay->setShortcut( QKeySequence() );
 
1972
            ui.actionStop->setVisible( true );
 
1973
            ui.actionStop->setShortcut( Qt::Key_Space );
 
1974
            break;
 
1975
    }
 
1976
 
 
1977
  #endif // HIDE_RADIO
 
1978
}
 
1979
 
 
1980
 
 
1981
std::vector<CPluginInfo>& 
 
1982
Container::getPluginList()
 
1983
 
1984
    return m_updater->getPluginList();
2299
1985
}
2300
1986
 
2301
1987
 
2310
1996
  #ifdef Q_WS_MAC
2311
1997
    QLabel* l;
2312
1998
    hbox->addWidget( l = new QLabel );
2313
 
    l->setPixmap( QPixmap( dataPath( "icons/scrobbling_graphic.png" ) ) );
2314
 
  #endif    
 
1999
    l->setPixmap( QPixmap( MooseUtils::savePath( "icons/scrobbling_graphic.png" ) ) );
 
2000
  #endif
2315
2001
    hbox->addWidget( m_image = new QLabel );
2316
 
    
 
2002
 
2317
2003
    setEnabled( false );
2318
2004
    setToolTip( tr( "Click to enable/disable scrobbling" ) );
2319
2005
    setAutoFillBackground( false );
2320
2006
}
2321
2007
 
 
2008
 
2322
2009
void
2323
2010
ScrobbleLabel::setEnabled( bool const on )
2324
2011
{
2325
 
    QIcon icon( dataPath( "icons/scrobble16.png" ) );
 
2012
    QIcon icon( MooseUtils::dataPath( "icons/scrobble16.png" ) );
2326
2013
 
2327
2014
    m_label->setText( ' ' + tr("Scrobbling %1").arg( on ? tr("on") : tr("off") ) + ' ' );
2328
2015
    m_image->setPixmap( icon.pixmap( 16, 16, on ? QIcon::Normal : QIcon::Disabled ) );
2329
2016
}
2330
 
///////////////////////////////////////////////////////////////////////////////>
2331
 
 
2332
 
 
2333
 
///////////////////////////////////////////////////////////////////////////////>
 
2017
 
 
2018
 
 
2019
///////////////////////////////////////////////////////////////////////////////>
 
2020
 
2334
2021
namespace The
2335
2022
{
2336
 
    /// these exist so you don't have to include container.h and radio.h to use
2337
 
    /// the radio, etc. only radio.h
2338
 
 
2339
 
    Radio &radio() { return Container::instance().radio(); }
2340
 
    ScrobblerManager& scrobbler() { return Container::instance().scrobbler(); }
2341
 
    RecommendDialog& recommendDialog() { return Container::instance().recommendDialog(); }
 
2023
    ShareDialog& shareDialog()
 
2024
    {
 
2025
        return The::container().shareDialog();
 
2026
    }
2342
2027
}
2343
 
 
2344
 
#undef CAST