17
15
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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
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.
39
27
__all__ = ['main', 'panel', 'playlist']
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
41
gtk.glade.textdomain('exaile')
43
gtk.glade.bindtextdomain('exaile', os.path.join(xdg.exaile_dir, 'po'))
46
"Failed to import gtk.glade, interface "
47
"will not be fully translated.")
45
from xlgui import commondialogs, cover
49
from xlgui import commondialogs, cover
46
50
from xlgui import devices, guiutil, icons, prefs, queue
48
logger = logging.getLogger(__name__)
51
54
# Set up xl/event to work with the gtk event loop
98
100
xdg.get_data_path('images'))
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')
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)
117
117
for panel in ('collection', 'radio', 'playlists', 'files'):
118
118
self.add_panel(*self.panels[panel].get_panel())
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)
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)
132
125
logger.info("Connecting panel events...")
133
126
self.main._connect_panel_events()
135
logger.info("Connecting main window events...")
136
self._connect_events()
138
128
if settings.get_option('gui/use_tray', False):
139
129
self.tray_icon = tray.TrayIcon(self.main)
141
event.add_callback(self._on_quit_application, 'quit_application')
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')
147
136
logger.info("Done loading main window...")
148
137
Main._main = self
150
def _connect_events(self):
152
Connects the various events to their handlers
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,
172
def _on_quit_application(self, event, sender, data):
174
Updates settings affected by GUI interaction
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)
180
139
def export_current_playlist(self, *e):
181
140
pl = self.main.get_current_playlist ().playlist
182
141
name = pl.get_name() + ".m3u"
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,
261
222
if result == gtk.RESPONSE_OK:
262
223
files = dialog.get_filenames()
225
self.open_uri(f, play=False)
227
def open_dir(self, *e):
229
Shows a dialog for opening (multiple) directories
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)
239
result = dialog.run()
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)
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
357
354
def on_rescan_collection(self, *e):
359
356
Called when the user wishes to rescan the collection
361
from xlgui import collection as guicol
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')
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()
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])
406
402
raise ValueError("No such panel")
408
404
def on_panel_switch(self, notebook, page, pagenum):
406
Saves the currently selected panel
408
if self.exaile.loading:
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)
414
417
def show_about_dialog(self, *e):
416
419
Displays the about dialog
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)