~ubuntu-branches/ubuntu/lucid/exaile/lucid

« back to all changes in this revision

Viewing changes to xlgui/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Starr-Bochicchio
  • Date: 2010-02-12 19:51:01 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20100212195101-8jt3tculxcl92e6v
Tags: 0.3.1~b1-0ubuntu1
* New upstream release.
* Adjust exaile.install for new plugins.
* debian/control:
 - Drop unneeded python-dev Build-Dep.
 - Bump Standards-Version to 3.8.4 
* debian/rules: No empty po files to delete.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008-2009 Adam Olsen 
2
 
#
3
 
# Copyright (C) 2008-2009 Adam Olsen 
 
1
# Copyright (C) 2008-2009 Adam Olsen
4
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
17
15
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
16
#
19
17
#
20
 
# The developers of the Exaile media player hereby grant permission 
21
 
# for non-GPL compatible GStreamer and Exaile plugins to be used and 
22
 
# distributed together with GStreamer and Exaile. This permission is 
23
 
# above and beyond the permissions granted by the GPL license by which 
24
 
# Exaile is covered. If you modify this code, you may extend this 
25
 
# exception to your version of the code, but you are not obligated to 
26
 
# do so. If you do not wish to do so, delete this exception statement 
27
 
# from your version.
28
 
#
29
 
#
30
 
# The developers of the Exaile media player hereby grant permission 
31
 
# for non-GPL compatible GStreamer and Exaile plugins to be used and 
32
 
# distributed together with GStreamer and Exaile. This permission is 
33
 
# above and beyond the permissions granted by the GPL license by which 
34
 
# Exaile is covered. If you modify this code, you may extend this 
35
 
# exception to your version of the code, but you are not obligated to 
36
 
# do so. If you do not wish to do so, delete this exception statement 
 
18
# The developers of the Exaile media player hereby grant permission
 
19
# for non-GPL compatible GStreamer and Exaile plugins to be used and
 
20
# distributed together with GStreamer and Exaile. This permission is
 
21
# above and beyond the permissions granted by the GPL license by which
 
22
# Exaile is covered. If you modify this code, you may extend this
 
23
# exception to your version of the code, but you are not obligated to
 
24
# do so. If you do not wish to do so, delete this exception statement
37
25
# from your version.
38
26
 
39
27
__all__ = ['main', 'panel', 'playlist']
40
28
 
 
29
import logging
 
30
import os
 
31
import urlparse
 
32
 
 
33
import gobject
 
34
import gtk
 
35
 
41
36
from xl.nls import gettext as _
42
 
import gtk, gtk.glade, gobject, logging, os, urlparse
 
37
logger = logging.getLogger(__name__)
43
38
from xl import xdg, common, event, metadata, settings, playlist as _xpl
 
39
try:
 
40
    import gtk.glade
 
41
    gtk.glade.textdomain('exaile')
 
42
    if xdg.local_hack:
 
43
        gtk.glade.bindtextdomain('exaile', os.path.join(xdg.exaile_dir, 'po'))
 
44
except ImportError:
 
45
    logger.warning(
 
46
        "Failed to import gtk.glade, interface "
 
47
        "will not be fully translated.")
44
48
 
45
 
from xlgui import commondialogs, cover 
 
49
from xlgui import commondialogs, cover
46
50
from xlgui import devices, guiutil, icons, prefs, queue
47
51
 
48
 
logger = logging.getLogger(__name__)
49
52
 
50
53
###
51
54
# Set up xl/event to work with the gtk event loop
65
68
    """
66
69
    _main = None
67
70
    def __init__(self, exaile):
68
 
        """ 
 
71
        """
69
72
            Initializes the GUI
70
73
 
71
74
            @param exaile: The Exaile instance
72
75
        """
73
 
        import xl.main as xlmain
74
76
        from xlgui import main, panel, tray, progress
75
77
        from xlgui.panel import collection, radio, playlists, files
76
78
 
78
80
        self.first_removed = False
79
81
        self.tray_icon = None
80
82
        self.panels = {}
81
 
        self.xml = gtk.glade.XML(xdg.get_data_path("glade/main.glade"),
82
 
            'ExaileWindow', 'exaile')
83
 
        self.progress_box = self.xml.get_widget('progress_box')
 
83
        self.builder = gtk.Builder()
 
84
        self.builder.add_from_file(xdg.get_data_path("ui/main.ui"))
 
85
        self.progress_box = self.builder.get_object('progress_box')
84
86
        self.progress_manager = progress.ProgressManager(self.progress_box)
85
87
 
86
88
        self.icons = icons.IconManager()
98
100
                xdg.get_data_path('images'))
99
101
 
100
102
        logger.info("Loading main window...")
101
 
        self.main = main.MainWindow(self, self.xml,
 
103
        self.main = main.MainWindow(self, self.builder,
102
104
            exaile.collection, exaile.player, exaile.queue, exaile.covers)
103
 
        self.panel_notebook = self.xml.get_widget('panel_notebook')
104
 
        self.play_toolbar = self.xml.get_widget('play_toolbar')
 
105
        self.panel_notebook = self.builder.get_object('panel_notebook')
 
106
        self.play_toolbar = self.builder.get_object('play_toolbar')
105
107
 
106
108
        logger.info("Loading panels...")
107
 
        self.last_selected_panel = settings.get_option(
108
 
            'gui/last_selected_panel', None)
109
109
        self.panels['collection'] = collection.CollectionPanel(self.main.window,
110
110
            exaile.collection, _show_collection_empty_message=True)
111
 
        self.panels['radio'] = radio.RadioPanel(self.main.window, exaile.collection, 
 
111
        self.panels['radio'] = radio.RadioPanel(self.main.window, exaile.collection,
112
112
            exaile.radio, exaile.stations)
113
 
        self.panels['playlists'] = playlists.PlaylistsPanel(self.main.window, 
 
113
        self.panels['playlists'] = playlists.PlaylistsPanel(self.main.window,
114
114
            exaile.playlists, exaile.smart_playlists, exaile.collection)
115
115
        self.panels['files'] = files.FilesPanel(self.main.window, exaile.collection)
116
116
 
117
117
        for panel in ('collection', 'radio', 'playlists', 'files'):
118
118
            self.add_panel(*self.panels[panel].get_panel())
119
119
 
120
 
        try:
121
 
            selected_panel = self.panels[self.last_selected_panel]._child
122
 
            selected_panel_num = self.panel_notebook.page_num(selected_panel)
123
 
            self.panel_notebook.set_current_page(selected_panel_num)
124
 
        except KeyError:
125
 
            pass
126
 
 
127
120
        # add the device panels
128
121
        for device in self.exaile.devices.list_devices():
129
122
            if device.connected:
130
123
                self.add_device_panel(None, None, device)
131
 
       
 
124
 
132
125
        logger.info("Connecting panel events...")
133
126
        self.main._connect_panel_events()
134
127
 
135
 
        logger.info("Connecting main window events...")
136
 
        self._connect_events()
137
 
 
138
128
        if settings.get_option('gui/use_tray', False):
139
129
            self.tray_icon = tray.TrayIcon(self.main)
140
130
 
141
 
        event.add_callback(self._on_quit_application, 'quit_application')
142
 
 
143
131
        self.device_panels = {}
144
132
        event.add_callback(self.add_device_panel, 'device_connected')
145
133
        event.add_callback(self.remove_device_panel, 'device_disconnected')
 
134
        event.add_callback(self.on_gui_loaded, 'gui_loaded')
146
135
 
147
136
        logger.info("Done loading main window...")
148
137
        Main._main = self
149
138
 
150
 
    def _connect_events(self):
151
 
        """
152
 
            Connects the various events to their handlers
153
 
        """
154
 
        self.xml.signal_autoconnect({
155
 
            'on_about_item_activate': self.show_about_dialog,
156
 
            'on_scan_collection_item_activate': self.on_rescan_collection,
157
 
            'on_randomize_playlist_item_activate': self.on_randomize_playlist,
158
 
            'on_collection_manager_item_activate': self.collection_manager,
159
 
            'on_goto_playing_track_activate': self.on_goto_playing_track,
160
 
            'on_queue_manager_item_activate': self.queue_manager,
161
 
            'on_preferences_item_activate': lambda *e: self.show_preferences(),
162
 
            'on_device_manager_item_activate': lambda *e: self.show_devices(),
163
 
            'on_cover_manager_item_activate': self.show_cover_manager,
164
 
            'on_open_item_activate': self.open_dialog,
165
 
            'on_open_url_item_activate': self.open_url,
166
 
            'on_export_current_playlist_activate': self.export_current_playlist,
167
 
            'on_panel_notebook_switch_page': self.on_panel_switch,
168
 
            'on_track_properties_activate':self.on_track_properties,
169
 
            'on_clear_playlist_item_activate': self.main.on_clear_playlist,
170
 
        })
171
 
 
172
 
    def _on_quit_application(self, event, sender, data):
173
 
        """
174
 
            Updates settings affected by GUI interaction
175
 
        """
176
 
        if not self.last_selected_panel:
177
 
            self.last_selected_panel = 0
178
 
        settings.set_option('gui/last_selected_panel', self.last_selected_panel)
179
 
        
180
139
    def export_current_playlist(self, *e):
181
140
        pl = self.main.get_current_playlist ().playlist
182
141
        name = pl.get_name() + ".m3u"
183
 
        
 
142
 
184
143
        dialog = commondialogs.FileOperationDialog(_("Export current playlist..."),
185
144
            None, gtk.FILE_CHOOSER_ACTION_SAVE,
186
145
            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
206
165
                except _xpl.InvalidPlaylistTypeException:
207
166
                    commondialogs.error(None, _('Invalid file extension, file not saved'))
208
167
        dialog.destroy()
209
 
        
 
168
 
210
169
    def open_url(self, *e):
211
170
        """
212
171
            Displays a dialog to open a url
215
174
        _('Open URL'))
216
175
        dialog.set_transient_for(self.main.window)
217
176
        dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
218
 
        
 
177
 
219
178
        result = dialog.run()
220
179
        dialog.hide()
221
180
        if result == gtk.RESPONSE_OK:
230
189
            self.main.window, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
231
190
                gtk.STOCK_OPEN, gtk.RESPONSE_OK))
232
191
        dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
 
192
        dialog.set_local_only(False) # enable gio
 
193
        dialog.set_select_multiple(True)
233
194
 
234
195
        supported_file_filter = gtk.FileFilter()
235
196
        supported_file_filter.set_name(_("Supported Files"))
240
201
        all_file_filter = gtk.FileFilter()
241
202
        all_file_filter.set_name(_("All Files"))
242
203
 
243
 
        for ext in metadata.SUPPORTED_MEDIA:
244
 
            supported_file_filter.add_pattern('*' + ext)
245
 
            audio_file_filter.add_pattern('*' + ext)
 
204
        for ext in metadata.formats.keys():
 
205
            supported_file_filter.add_pattern('*.' + ext)
 
206
            audio_file_filter.add_pattern('*.' + ext)
246
207
 
247
208
        playlist_file_types = ('m3u', 'pls', 'asx', 'xspf')
248
209
        for playlist_file_type in playlist_file_types:
260
221
        dialog.hide()
261
222
        if result == gtk.RESPONSE_OK:
262
223
            files = dialog.get_filenames()
 
224
            for f in files:
 
225
                self.open_uri(f, play=False)
 
226
 
 
227
    def open_dir(self, *e):
 
228
        """
 
229
            Shows a dialog for opening (multiple) directories
 
230
        """
 
231
 
 
232
        dialog = gtk.FileChooserDialog(_("Choose a file to open"),
 
233
            self.main.window, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
 
234
                gtk.STOCK_OPEN, gtk.RESPONSE_OK))
 
235
        dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
 
236
        dialog.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
 
237
        dialog.set_select_multiple(True)
 
238
 
 
239
        result = dialog.run()
 
240
        dialog.hide()
 
241
        if result == gtk.RESPONSE_OK:
 
242
            files = dialog.get_filenames()
263
243
            for file in files:
264
244
                self.open_uri(file, play=False)
265
245
 
274
254
            Determines the type of a uri, imports it into a playlist, and
275
255
            starts playing it
276
256
        """
277
 
        from xl import playlist, track
 
257
        from xl import playlist, trax
278
258
        if playlist.is_valid_playlist(uri):
279
259
            pl = playlist.import_playlist(uri)
280
260
            self.main.add_playlist(pl)
283
263
        else:
284
264
            pl = self.main.get_selected_playlist()
285
265
            column, descending = pl.get_sort_by()
286
 
            tracks = track.get_tracks_from_uri(uri)
287
 
            tracks.sort(key=lambda track: track.sort_param(column), reverse=descending)
 
266
 
 
267
            tracks = trax.get_tracks_from_uri(uri)
 
268
            tracks = trax.sort_tracks(pl.return_order_tags(column), tracks)
 
269
 
288
270
            try:
289
271
                pl.playlist.add_tracks(tracks)
290
272
                pl.playlist.set_current_pos(len(pl.playlist) - len(tracks))
345
327
            coll.thaw_libraries()
346
328
            self.on_rescan_collection()
347
329
 
 
330
    def on_gui_loaded(self, event, object, nothing):
 
331
        """
 
332
            Allows plugins to be the last selected panel
 
333
        """
 
334
        try:
 
335
            last_selected_panel = settings.get_option(
 
336
                'gui/last_selected_panel', 'collection')
 
337
            panel = self.panels[last_selected_panel]._child
 
338
            panel_num = self.panel_notebook.page_num(panel)
 
339
            self.panel_notebook.set_current_page(panel_num)
 
340
            # Fix track info not displaying properly when resuming after a restart.
 
341
            self.main._update_track_information()
 
342
        except KeyError:
 
343
            pass
 
344
 
348
345
    def on_goto_playing_track(self, *e):
349
346
        track = self.exaile.queue.get_current()
350
347
        pl = self.main.get_current_playlist()
353
350
            pl.list.scroll_to_cell(index)
354
351
            pl.list.set_cursor(index)
355
352
        #TODO implement a way to browse through all playlists and search for the track
356
 
            
 
353
 
357
354
    def on_rescan_collection(self, *e):
358
355
        """
359
356
            Called when the user wishes to rescan the collection
360
357
        """
361
 
        from xlgui import collection as guicol
362
 
        try:
363
 
            thread = guicol.CollectionScanThread(self, self.exaile.collection, 
 
358
        if not self.exaile.collection._scanning:
 
359
            from xlgui.collection import CollectionScanThread
 
360
            thread = CollectionScanThread(self, self.exaile.collection,
364
361
                    self.panels['collection'])
365
362
            self.progress_manager.add_monitor(thread,
366
363
                _("Scanning collection..."), 'gtk-refresh')
367
 
        except:
368
 
            pass
369
364
 
370
365
    def on_randomize_playlist(self, *e):
371
366
        pl = self.main.get_selected_playlist()
372
367
        pl.playlist.randomize()
373
368
        pl._set_tracks(pl.playlist.get_tracks())
374
369
        pl.reorder_songs()
375
 
    
 
370
 
376
371
    def on_track_properties(self, *e):
377
372
        pl = self.main.get_selected_playlist()
378
373
        if not pl.properties_dialog():
379
374
            if self.exaile.player.current:
 
375
                from xlgui import properties
380
376
                dialog = properties.TrackPropertiesDialog(self.main.window,
381
 
                        self.exaile.player.current)
382
 
                result = dialog.run()
 
377
                        [self.exaile.player.current])
383
378
                dialog.hide()
384
379
 
385
380
 
396
391
 
397
392
            # the first tab in the panel is a stub that just stops libglade from
398
393
            # complaining
 
394
            # TODO: Check if this is valid for GtkBuilder
399
395
            self.panel_notebook.remove_page(0)
400
396
 
401
397
    def remove_panel(self, child):
406
402
        raise ValueError("No such panel")
407
403
 
408
404
    def on_panel_switch(self, notebook, page, pagenum):
 
405
        """
 
406
            Saves the currently selected panel
 
407
        """
 
408
        if self.exaile.loading:
 
409
            return
 
410
 
409
411
        page = notebook.get_nth_page(pagenum)
410
 
        for id, panel in self.panels.items():
 
412
        for i, panel in self.panels.items():
411
413
            if panel._child == page:
412
 
                self.last_selected_panel = id
 
414
                settings.set_option('gui/last_selected_panel', i)
 
415
                return
413
416
 
414
417
    def show_about_dialog(self, *e):
415
418
        """
416
419
            Displays the about dialog
417
420
        """
418
421
        import xl.main as xlmain
419
 
        xml = gtk.glade.XML(xdg.get_data_path('glade/about_dialog.glade'),
420
 
            'AboutDialog', 'exaile')
421
 
        dialog = xml.get_widget('AboutDialog')
 
422
        builder = gtk.Builder()
 
423
        builder.add_from_file(xdg.get_data_path('ui/about_dialog.ui'))
 
424
        dialog = builder.get_object('AboutDialog')
422
425
        logo = gtk.gdk.pixbuf_new_from_file(
423
426
            xdg.get_data_path('images/exailelogo.png'))
424
427
        dialog.set_logo(logo)
427
430
        dialog.set_transient_for(self.main.window)
428
431
        dialog.connect('response', lambda d, r: d.destroy())
429
432
        dialog.show()
430
 
        
 
433
 
431
434
    def quit(self):
432
435
        """
433
436
            Quits the gui, saving anything that needs to be saved
448
451
            elif issubclass(device.panel_type, xlgui.panel.Panel):
449
452
                paneltype = device.panel_type
450
453
 
451
 
        panel = paneltype(self.main.window, self.main, 
 
454
        panel = paneltype(self.main.window, self.main,
452
455
            device, device.get_name())
453
456
 
454
457
        sort = True
459
462
 
460
463
        self.device_panels[device.get_name()] = panel
461
464
        gobject.idle_add(self.add_panel, *panel.get_panel())
462
 
        thread = collection.CollectionScanThread(self.main, 
 
465
        thread = collection.CollectionScanThread(self.main,
463
466
                device.get_collection(), panel)
464
467
        self.progress_manager.add_monitor(thread,
465
468
                _("Scanning %s..."%device.name), 'gtk-refresh')
482
485
    if not show: return
483
486
    image = gtk.Image()
484
487
    image.set_from_file(xdg.get_data_path("images/splash.png"))
485
 
    xml = gtk.glade.XML(xdg.get_data_path("glade/splash.glade"), 'SplashScreen',
486
 
        'exaile')
487
 
    splash_screen = xml.get_widget('SplashScreen')
488
 
    box = xml.get_widget('splash_box')
 
488
    builder = gtk.Builder()
 
489
    builder.add_from_file(xdg.get_data_path("ui/splash.ui"))
 
490
    splash_screen = builder.get_object('SplashScreen')
 
491
    box = builder.get_object('splash_box')
489
492
    box.pack_start(image, True, True)
490
493
    splash_screen.set_transient_for(None)
491
494
    splash_screen.show_all()