home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.4) import gtk import gobject import sys import weakref from gtk import glade from os import path from types import IntType, TupleType from gettext import gettext as _ from gettext import ngettext as N_ import operations import audio import xspf import gtkutil import urlutil from gtkutil import DictStore from operations import OperationsQueue from gdkpiechart import SerpentineUsage class ErrorTrapper(operations.Operation, operations.OperationListener): def __init__(self, parent = None): operations.Operation.__init__(self) self._ErrorTrapper__errors = [] self._parent = parent errors = property((lambda self: self._ErrorTrapper__errors)) parent = property((lambda self: self._parent)) def on_finished(self, event): if event.id == operations.ERROR: self.errors.append(event.source) def start(self): if len(self.errors) == 0: e = operations.FinishedEvent(self, operations.SUCCESSFUL) for l in self.listeners: l.on_finished(e) return None filenames = [] for e in self.errors: filenames.append(urlutil.basename(e.hints['location'])) del self._ErrorTrapper__errors title = N_('Unsupported file type', 'Unsupported file types', len(filenames)) msg = N_('The following file was not added:', 'The following files were not added:', len(filenames)) gtkutil.list_dialog(title, _("If you're having problems opening certain files make sure you have the GStreamer plugins needed to decode them."), list_title = msg, parent = self.parent, items = filenames, stock = gtk.STOCK_DIALOG_ERROR, buttons = (gtk.STOCK_CLOSE, gtk.RESPONSE_OK)) e = operations.FinishedEvent(self, operations.SUCCESSFUL) for l in self.listeners: l.on_finished(e) class AddFile(audio.AudioMetadataListener, operations.Operation): running = property((lambda self: False)) def __init__(self, music_list, hints, insert = None, app = None): operations.Operation.__init__(self) self.hints = hints self.music_list = music_list self.insert = insert hints['location'] = urlutil.normalize(hints['location']) self.app = app def start(self): if self.app.preferences.useGnomeVfs: oper = audio.get_metadata(audio.GVFS_SRC, self.hints['location']) else: url = urlutil.UrlParse(self.hints['location']) if not url.is_local: self._send_finished_error(operations.ERROR, error = StandardError(self.hints['location'])) return None filename = url.path oper = audio.get_metadata('filesrc', filename) oper.listeners.append(self) try: oper.start() except audio.GstPlayingFailledError: self._send_finished_event(operations.ERROR, error = StandardError(self.hints['location'])) def on_metadata(self, event, metadata): title = urlutil.basename(self.hints['location']) title = path.splitext(title)[0] if not title: pass row = { 'location': self.hints['location'], 'cache_location': '', 'title': _('Unknown'), 'artist': _('Unknown Artist'), 'duration': int(metadata['duration']) } if metadata.has_key('title'): row['title'] = metadata['title'] if metadata.has_key('artist'): row['artist'] = metadata['artist'] if self.hints.has_key('title'): row['title'] = self.hints['title'] if self.hints.has_key('artist'): row['artist'] = self.hints['artist'] if self.insert is not None: self.music_list.insert(self.insert, row) else: self.music_list.append(row) def on_finished(self, evt): e = operations.FinishedEvent(self, evt.id) for l in self.listeners: l.on_finished(e) class UpdateDiscUsage(operations.Operation): def __init__(self, masterer, update): operations.Operation.__init__(self) self._UpdateDiscUsage__update = update self._UpdateDiscUsage__masterer = masterer running = property((lambda self: False)) can_run = property((lambda self: True)) def start(self): self._UpdateDiscUsage__masterer.update = self._UpdateDiscUsage__update if self._UpdateDiscUsage__update: self._UpdateDiscUsage__masterer.update_disc_usage() e = operations.FinishedEvent(self, operations.SUCCESSFUL) for l in self.listeners: l.on_finished(e) class MusicListListener: def on_musics_added(self, event, rows): pass def on_musics_removed(self, event, rows): pass class MusicList(operations.Listenable): def __getitem__(self): pass def append_many(self, rows): pass def append(self, row): pass def insert(self, index, row): pass def insert_many(self, index, rows): pass def __len__(self): pass def __delitem__(self, index): pass def delete_many(self, indexes): pass def clear(self): pass def has_key(self, key): pass def from_playlist(self, playlist): rows = [] for t in playlist.tracks: rows.append({ 'location': t.location, 'duration': t.duration, 'title': t.title, 'artist': t.creator }) self.append_many(rows) def to_playlist(self, playlist): for r in self: t = xspf.Track() t.location = r['location'] t.duration = r['duration'] t.title = r['title'] t.creator = r['artist'] playlist.tracks.append(t) class GtkMusicList(MusicList): '''The GtkMusicList uses a ListStore as a backend, it is not visual and depends only on glib. Takes care of the data source. Supports events and listeners. ''' SPEC = ({ 'name': 'location', 'type': gobject.TYPE_STRING }, { 'name': 'cache_location', 'type': gobject.TYPE_STRING }, { 'name': 'duration', 'type': gobject.TYPE_INT }, { 'name': 'title', 'type': gobject.TYPE_STRING }, { 'name': 'artist', 'type': gobject.TYPE_STRING }, { 'name': 'time', 'type': gobject.TYPE_STRING }) def __init__(self): operations.Listenable.__init__(self) self._GtkMusicList__model = DictStore(*self.SPEC) self._GtkMusicList__total_duration = 0 self._GtkMusicList__freezed = False model = property(fget = (lambda self: self._GtkMusicList__model), doc = 'Associated ListStore.') total_duration = property(fget = (lambda self: self._GtkMusicList__total_duration), doc = 'Total disc duration, in seconds.') def __getitem__(self, index): return self.model.get(index) def append_many(self, rows): self._GtkMusicList__freezed = True for row in rows: self.append(row) self._GtkMusicList__freezed = False rows = tuple(rows) e = operations.Event(self) for l in self.listeners: l.on_musics_added(e, rows) def __correct_row(self, row): if not row.has_key('time'): row['time'] = '%.2d:%.2d' % (row['duration'] / 60, row['duration'] % 60) if not row.has_key('cache_location'): row['cache_location'] = '' return row def append(self, row): row = self._GtkMusicList__correct_row(row) self.model.append(row) self._GtkMusicList__total_duration += int(row['duration']) if not self._GtkMusicList__freezed: e = operations.Event(self) rows = (row,) for l in self.listeners: l.on_musics_added(e, rows) def insert(self, index, row): row = self._GtkMusicList__correct_row(row) self.model.insert_before(self.model.get_iter(index), row) self._GtkMusicList__total_duration += int(row['duration']) if not self._GtkMusicList__freezed: e = operations.Event(self) rows = (row,) for l in self.listeners: l.on_musics_added(e, rows) def __len__(self): return len(self.model) def __delitem__(self, index): row = dict(self[index]) del self.model[index] self._GtkMusicList__total_duration -= row['duration'] rows = (row,) if not self._GtkMusicList__freezed: e = operations.Event(self) for l in self.listeners: l.on_musics_removed(e, rows) def delete_many(self, indexes): if not isinstance(indexes, list): raise AssertionError rows = [] indexes.sort() low = indexes[0] - 1 for i in indexes: if low == i: indexes.remove(i) low = i for i in range(len(indexes)): indexes[i] -= i for i in indexes: r = dict(self.model.get(i)) rows.append(r) self._GtkMusicList__total_duration -= r['duration'] del self.model[i] rows = tuple(rows) e = operations.Event(self) for l in self.listeners: l.on_musics_removed(e, rows) def clear(self): rows = [] for row in iter(self.model): rows.append(dict(row)) self.model.clear() self._GtkMusicList__total_duration = 0 rows = tuple(rows) e = operations.Event(self) for l in self.listeners: l.on_musics_removed(e, rows) class AudioMasteringMusicListener(MusicListListener): def __init__(self, audio_mastering): self._AudioMasteringMusicListener__master = audio_mastering def on_musics_added(self, e, rows): self._AudioMasteringMusicListener__master.update_disc_usage() def on_musics_removed(self, e, rows): self._AudioMasteringMusicListener__master.update_disc_usage() class HintsFilter(object): __priority = 0 def priority(self, value): if not isinstance(value, int): raise AssertionError self._HintsFilter__priority = value priority = property((lambda self: self._HintsFilter__priority), priority, doc = 'Represents the parser priority, a higher value will give it precedence over filters lower filters.') def filter_location(self, location): """Returns a list of dictionaries of hints of a given location. The 'location' field is obligatory. For example if your filter parses a directory it should return a list of hints of each encountered file. """ raise NotImplementedError def __cmp__(self, value): if not isinstance(value, HintsFilter): raise AssertionError return self.priority - value.priority def normalize_hints(hints): hints['location'] = urlutil.normalize(hints['location']) class MusicListGateway: '''This class wraps the MusicList interface in a friendlier one with a method `add_files` easier to use then the `insert` method which expects a hints `dict`. It also serves as a hints filter which is a list of client objects which must provide the `filter_location` method. ''' class Handler: '''A handler is created each time a method is created, it must return objects with this class signature.''' def prepare_queue(self, gateway, queue): '''Method called before the AddFile operations are added to the queue''' pass def finish_queue(self, gateway, queue): '''Method called after the AddFile operations are added to the queue''' pass def prepare_add_file(self, gateway, add_file): '''Method called before add_file object is added to queue''' pass def __init__(self, app): self._MusicListGateway__filters = [] self._app = weakref.ref(app) music_list = None def __filter_location(self, location): for loc_filter in self._MusicListGateway__filters: hints = loc_filter.filter_location(location) if hints is not None: return hints continue def add_files(self, filenames): to_hint = lambda filename: { 'location': urlutil.normalize(filename) } return self.add_hints(map(to_hint, filenames)) def add_hints(self, hints_list, insert = None): if not insert is None and isinstance(insert, IntType): raise AssertionError queue = OperationsQueue() queue.abort_on_failure = False handler = self.Handler() handler.prepare_queue(self, queue) i = 0 for h in hints_list: pls = self._MusicListGateway__filter_location(h['location']) if pls is not None and len(pls) > 0: map(normalize_hints, pls) queue.append(self.add_hints(pls, insert)) continue ins = insert if insert != None: ins += i a = AddFile(self.music_list, h, ins, self._app()) handler.prepare_add_file(self, a) queue.append(a) i += 1 handler.finish_queue(self, queue) return queue def add_hints_filter(self, location_filter): self._MusicListGateway__filters.append(location_filter) self._MusicListGateway__filters.sort() def remove_hints_filter(self, location_filter): self._MusicListGateway__filters.remove(location_filter) class AudioMastering(gtk.VBox, operations.Listenable): SIZE_21 = 0 SIZE_74 = 1 SIZE_80 = 2 SIZE_90 = 3 class MusicListGateway(MusicListGateway): def __init__(self, parent): MusicListGateway.__init__(self, parent._application()) self.parent = parent def music_list(self): return self.parent.music_list music_list = property(music_list) def window(self): return gtkutil.get_root_parent(self.parent) window = property(window) class Handler: def prepare_queue(self, gateway, queue): queue.append(UpdateDiscUsage(gateway.parent, False)) self.trapper = ErrorTrapper(gateway.window) def finish_queue(self, gateway, queue): queue.append(UpdateDiscUsage(gateway.parent, True)) queue.append(self.trapper) del self.trapper def prepare_add_file(self, gateway, add_file): add_file.listeners.append(self.trapper) disc_sizes = [ 21 * 60, 74 * 60, 80 * 60, 90 * 60] DND_TARGETS = [ ('SERPENTINE_ROW', gtk.TARGET_SAME_WIDGET, 0), ('text/uri-list', 0, 1), ('text/plain', 0, 2), ('STRING', 0, 3)] def __init__(self, application): gtk.VBox.__init__(self) self._application = weakref.ref(application) operations.Listenable.__init__(self) self._AudioMastering__disc_size = 74 * 60 self.update = True self.source = GtkMusicList() self.source.listeners.append(AudioMasteringMusicListener(self)) self._AudioMastering__gateway = AudioMastering.MusicListGateway(self) gtk.VBox.__init__(self) filename = application.locations.get_data_file('serpentine.glade') g = glade.XML(filename, 'audio_container') self.add(g.get_widget('audio_container')) self._AudioMastering__setup_track_list(g) self._AudioMastering__setup_container_misc(g) def __set_disc_size(self, size): if not size in AudioMastering.disc_sizes: raise AssertionError self._AudioMastering__disc_size = size self._AudioMastering__size_list.set_active(AudioMastering.disc_sizes.index(size)) self.update_disc_usage() e = operations.Event(self) for l in self.listeners: if hasattr(l, 'on_disc_size_changed'): l.on_disc_size_changed(e) continue music_list_gateway = property((lambda self: self._AudioMastering__gateway)) music_list = property((lambda self: self.source)) disc_size = property((lambda self: self._AudioMastering__disc_size), __set_disc_size, doc = 'Represents the disc size, in seconds.') disc_size_widget = property((lambda self: self._AudioMastering__size_list)) def __setup_container_misc(self, g): self._AudioMastering__size_list = g.get_widget('size_list') self._AudioMastering__usage_label = g.get_widget('usage_label') self._AudioMastering__usage_gauge = SerpentineUsage(self) self._AudioMastering__usage_gauge.widget.show() self._AudioMastering__usage_gauge.widget.set_size_request(92, 92) hbox = g.get_widget('disc_details') hbox.pack_start(self._AudioMastering__usage_gauge.widget, expand = False, fill = False) self._AudioMastering__capacity_exceeded = g.get_widget('capacity_exceeded') self._AudioMastering__size_list.connect('changed', self._AudioMastering__on_size_changed) self._AudioMastering__size_list.set_active(AudioMastering.SIZE_74) def __setup_track_list(self, g): lst = g.get_widget('track_list') lst.set_model(self.source.model) r = gtk.CellRendererText() col = gtk.TreeViewColumn(_('Track'), r) col.set_cell_data_func(r, self._AudioMastering__generate_track) r = gtk.CellRendererText() r.set_property('editable', True) r.connect('edited', self._AudioMastering__on_title_edited) lst.append_column(col) col = gtk.TreeViewColumn('Title', r, text = self.source.model.index_of('title')) lst.append_column(col) r = gtk.CellRendererText() r.set_property('editable', True) r.connect('edited', self._AudioMastering__on_artist_edited) col = gtk.TreeViewColumn(_('Artist'), r, text = self.source.model.index_of('artist')) lst.append_column(col) r = gtk.CellRendererText() col = gtk.TreeViewColumn(_('Duration'), r, text = self.source.model.index_of('time')) lst.append_column(col) self._AudioMastering__selection = lst.get_selection() self._AudioMastering__selection.connect('changed', self._AudioMastering__selection_changed) self._AudioMastering__selection.set_mode(gtk.SELECTION_MULTIPLE) lst.set_reorderable(True) lst.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, AudioMastering.DND_TARGETS, gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) lst.enable_model_drag_dest(AudioMastering.DND_TARGETS, gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) lst.connect('drag_data_received', self._AudioMastering__on_dnd_drop) lst.connect('drag_data_get', self._AudioMastering__on_dnd_send) def __generate_track(self, col, renderer, tree_model, treeiter, user_data = None): index = tree_model.get_path(treeiter)[0] renderer.set_property('text', index + 1) def __on_size_changed(self, *args): self.disc_size = AudioMastering.disc_sizes[self._AudioMastering__size_list.get_active()] def __on_title_edited(self, cell, path, new_text, user_data = None): self.source[path]['title'] = new_text def __on_artist_edited(self, cell, path, new_text, user_data = None): self.source[path]['artist'] = new_text def __on_dnd_drop(self, treeview, context, x, y, selection, info, timestamp, user_data = None): data = selection.data hints_list = [] insert = None drop_info = treeview.get_dest_row_at_pos(x, y) if drop_info: (insert, insert_before) = drop_info if not isinstance(insert, TupleType): raise AssertionError, len(insert) == 1 (insert,) = insert if insert_before != gtk.TREE_VIEW_DROP_BEFORE and insert_before != gtk.TREE_VIEW_DROP_INTO_OR_BEFORE: insert += 1 if insert == len(self.source): insert = None del insert_before del drop_info if selection.type == 'application/x-rhythmbox-source': return None elif selection.type == 'SERPENTINE_ROW': (store, path_list) = self._AudioMastering__selection.get_selected_rows() if not path_list or len(path_list) != 1: return None (path,) = path_list row = dict(self.source[path]) del self.source[path] if len(self.source) == insert: insert = None if insert is not None: self.source.insert(insert, row) else: self.source.append(row) return None for line in data.split('\n'): line = line.strip() if len(line) < 1 or line == '\x00': continue if not '\x00' not in line: raise AssertionError, 'Malformed DnD string: %s' % line hint = { 'location': line } hints_list.append(hint) self.music_list_gateway.add_hints(hints_list, insert).start() def __on_dnd_send(self, widget, context, selection, target_type, timestamp): (store, path_list) = self._AudioMastering__selection.get_selected_rows() if not path_list or len(path_list) == 1: raise AssertionError (path,) = path_list selection.set(selection.target, 8, self.source[path]['location']) def __hig_duration(self, duration): hig_duration = '' minutes = duration / 60 if minutes: if not minutes == 1 or _('minute'): pass hig_duration = '%s %s' % (minutes, _('minutes')) seconds = duration % 60 if seconds: if not seconds == 1 or _('second'): pass hig_secs = '%s %s' % (seconds, _('seconds')) if len(hig_duration): hig_duration += _(' and ') hig_duration += hig_secs return hig_duration def get_preferences(self): return self._application().preferences def get_media_duration(self): total = self.source.total_duration num_tracks = len(self.source) if num_tracks > 0 and self.get_preferences().useGap: total += (num_tracks - 1) * 2 return total def update_disc_usage(self): if not self.update: return None if self.source.total_duration > self.disc_size: self._AudioMastering__capacity_exceeded.show() else: self._AudioMastering__capacity_exceeded.hide() while gtk.events_pending(): gtk.main_iteration(True) if self.source.total_duration > 0: duration = self._AudioMastering__disc_size - self.source.total_duration if duration > 0: dur = _('%s remaining') % self._AudioMastering__hig_duration(duration) else: dur = _('%s overlaping') % self._AudioMastering__hig_duration(abs(duration)) else: dur = _('Empty') self._AudioMastering__usage_label.set_text(dur) e = operations.Event(self) for l in self.listeners: l.on_contents_changed(e) def __selection_changed(self, treeselection): e = operations.Event(self) for l in self.listeners: l.on_selection_changed(e) def get_selected(self): '''Returns the selected indexes''' (store, path_list) = self._AudioMastering__selection.get_selected_rows() if path_list is None: return [] indexes = [] for p in path_list: if not len(p) == 1: raise AssertionError indexes.append(*p) return indexes def remove_selected(self): self.source.delete_many(self.get_selected()) def count_selected(self): return self._AudioMastering__selection.count_selected_rows() if __name__ == '__main__': win = gtk.Window() win.connect('delete-event', gtk.main_quit) w = AudioMastering() w.show() win.add(w) win.show() w.add_file(sys.argv[1]) w.source.clear() gtk.main()