def __init__(self, all_button=True): Gtk.Box.__init__(self) self.init_template() self.parent_row = None self.all_func = None self.update_func = None # Prevents the update function from being called, make # sure you do that manually after the batch update self.batch_update = False self.pixbuf = None self.info = CoverImage(None, None, None, None) self.default_type = 3 self.mime_info = { 'image/jpeg': { # Title for display 'title': _('JPEG image'), # Type and options for GDK Pixbuf saving 'type': 'jpeg', 'options': { 'quality': '90' } }, 'image/png': { 'title': _('PNG image'), 'type': 'png', 'options': {} }, 'image/': { 'title': _('Image'), # Store unknown images as JPEG 'type': 'jpeg', 'options': { 'quality': '90' } }, # TODO: Handle linked images '-->': { 'title': _('Linked image') } } self.button.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) self.button.drag_dest_add_uri_targets() self.type_selection.connect('scroll-event', dummy_scroll_handler) self.all_button = None if all_button: self.all_button = AllButton(self) self.pack_start(self.all_button, False, False, 0)
def _get_tag(self, raw, t): if not raw.tags: return [] if t not in self.tag_mapping.itervalues(): t = "TXXX:" + t field = raw.tags.getall(t) if len(field) <= 0: return [] ret = [] if t in ('TDRC', 'TDOR'): # values are ID3TimeStamps for value in field: ret.extend([unicode(x) for x in value.text]) elif t == 'USLT': # Lyrics are stored in plain old strings for value in field: ret.append(unicode(value.text)) elif t == 'WOAR': # URLS are stored in url not text for value in field: ret.extend([unicode(value.url.replace('\n','').replace('\r',''))]) elif t == 'APIC': ret = [CoverImage(type=f.type, desc=f.desc, mime=f.mime, data=f.data) for f in field] elif t == 'COMM': # Newlines within comments are allowed, keep them for item in field: ret.extend([value for value in item.text]) else: for value in field: try: ret.extend([unicode(x.replace('\n','').replace('\r','')) \ for x in value.text]) except: pass return ret
def __init__(self, all_button=True): gtk.HBox.__init__(self, homogeneous=False, spacing=5) self.parent_row = None self.all_func = None self.update_func = None # Prevents the update function from being called, make # sure you do that manually after the batch update self.batch_update = False self.pixbuf = None self.info = CoverImage(None, None, None, None) self.default_type = 3 self.mime_info = { 'image/jpeg': { # Title for display 'title': _('JPEG image'), # Type and options for GDK Pixbuf saving 'type': 'jpeg', 'options': {'quality': '90'} }, 'image/png': { 'title': _('PNG image'), 'type': 'png', 'options': {} }, 'image/': { 'title': _('Image'), # Store unknown images as JPEG 'type': 'jpeg', 'options': {'quality': '90'} }, # TODO: Handle linked images '-->': { 'title': _('Linked image') } } builder = gtk.Builder() builder.add_from_file(xdg.get_data_path('ui', 'trackproperties_dialog_cover_row.ui')) builder.connect_signals(self) cover_row = builder.get_object('cover_row') cover_row.reparent(self) button = builder.get_object('button') button.drag_dest_set(gtk.DEST_DEFAULT_ALL, [], gtk.gdk.ACTION_COPY) button.drag_dest_add_uri_targets() self.image = builder.get_object('image') self.info_label = builder.get_object('info_label') self.type_model = builder.get_object('type_model') self.type_selection = builder.get_object('type_selection') self.type_selection.set_sensitive(False) self.description_entry = builder.get_object('description_entry') self.description_entry.set_sensitive(False) self.all_button = None if all_button: self.all_button = AllButton(self) self.pack_start(self.all_button, expand=False, fill=False)
def _get_tag(self, raw, tag): if tag == '__cover': return [ CoverImage(type=p.type, desc=p.desc, mime=p.mime, data=p.data) for p in raw.pictures ] return CaseInsensitveBaseFormat._get_tag(self, raw, tag)
def _get_tag(self, raw, tag): value = CaseInsensitveBaseFormat._get_tag(self, raw, tag) if value and tag == 'metadata_block_picture': new_value = [] for v in value: picture = Picture(base64.b64decode(v)) new_value.append(CoverImage(type=picture.type, desc=picture.desc, mime=picture.mime, data=picture.data)) value = new_value return value
def read_tags(self, tags): if "cover" in tags: tags[tags.index("cover")] = "metadata_block_picture" td = super(OggFormat, self).read_tags(tags) if 'metadata_block_picture' in td: td['cover'] = [] for d in td["metadata_block_picture"]: picture = mutagen.flac.Picture(base64.standard_b64decode(d)) td['cover'] += [ CoverImage(type=picture.type, desc=picture.desc, mime=picture.mime, data=picture.data) ] return td
def __init__(self, all_button=True): Gtk.Box.__init__(self) self.init_template() self.parent_row = None self.all_func = None self.update_func = None # Prevents the update function from being called, make # sure you do that manually after the batch update self.batch_update = False self.pixbuf = None self.info = CoverImage(None, None, None, None) self.default_type = 3 self.mime_info = { 'image/jpeg': { # Title for display 'title': _('JPEG image'), # Type and options for GDK Pixbuf saving 'type': 'jpeg', 'options': {'quality': '90'} }, 'image/png': { 'title': _('PNG image'), 'type': 'png', 'options': {} }, 'image/': { 'title': _('Image'), # Store unknown images as JPEG 'type': 'jpeg', 'options': {'quality': '90'} }, # TODO: Handle linked images '-->': { 'title': _('Linked image') } } self.button.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) self.button.drag_dest_add_uri_targets() self.type_selection.connect('scroll-event', dummy_scroll_handler) self.all_button = None if all_button: self.all_button = AllButton(self) self.pack_start(self.all_button, False, False, 0)
def _get_tag(self, f, name): if not f.has_key(name): return [] elif name == 'covr': ret = [] for value in f[name]: if value.imageformat == mp4.MP4Cover.FORMAT_PNG: mime = 'image/png' else: mime = 'image/jpeg' ret.append( CoverImage(type=None, desc=None, mime=mime, data=value)) return ret elif name in ['trkn', 'disk']: ret = [] for value in f[name]: ret.append("%d/%d" % (value[0], value[1])) return ret else: return [t for t in f[name]]
def _get_tag(self, raw, t): if not raw.tags: return [] if t not in self.tag_mapping.values(): t = "TXXX:" + t field = raw.tags.getall(t) if len(field) <= 0: return [] ret = [] if t in ('TDRC', 'TDOR'): # values are ID3TimeStamps, need str conversion for value in field: ret.extend([str(x) for x in value.text]) elif t == 'USLT': # Lyrics are stored in a single str object for value in field: ret.append(value.text) elif ( t == 'WOAR' ): # URLs are stored in url field instead of text field (as a single str object) for value in field: ret.append(value.url.replace('\n', '').replace('\r', '')) elif t == 'APIC': ret = [ CoverImage(type=f.type, desc=f.desc, mime=f.mime, data=f.data) for f in field ] elif t == 'COMM': # Newlines within comments are allowed, keep them for item in field: ret.extend([value for value in item.text]) else: for value in field: try: ret.extend( [x.replace('\n', '').replace('\r', '') for x in value.text] ) except Exception: pass return ret
class TagImageField(Gtk.HBox): def __init__(self, all_button=True): Gtk.HBox.__init__(self, homogeneous=False, spacing=5) self.parent_row = None self.all_func = None self.update_func = None # Prevents the update function from being called, make # sure you do that manually after the batch update self.batch_update = False self.pixbuf = None self.info = CoverImage(None, None, None, None) self.default_type = 3 self.mime_info = { 'image/jpeg': { # Title for display 'title': _('JPEG image'), # Type and options for GDK Pixbuf saving 'type': 'jpeg', 'options': {'quality': '90'} }, 'image/png': { 'title': _('PNG image'), 'type': 'png', 'options': {} }, 'image/': { 'title': _('Image'), # Store unknown images as JPEG 'type': 'jpeg', 'options': {'quality': '90'} }, # TODO: Handle linked images '-->': { 'title': _('Linked image') } } builder = Gtk.Builder() builder.add_from_file(xdg.get_data_path('ui', 'trackproperties_dialog_cover_row.ui')) builder.connect_signals(self) cover_row = builder.get_object('cover_row') cover_row.reparent(self) button = builder.get_object('button') button.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) button.drag_dest_add_uri_targets() self.image = builder.get_object('image') self.info_label = builder.get_object('info_label') self.type_model = builder.get_object('type_model') self.type_selection = builder.get_object('type_selection') self.type_selection.set_sensitive(False) self.type_selection.connect('scroll-event', dummy_scroll_handler) self.description_entry = builder.get_object('description_entry') self.description_entry.set_sensitive(False) self.all_button = None if all_button: self.all_button = AllButton(self) self.pack_start(self.all_button, False, False, 0) def grab_focus(self): """ Gives focus to the internal widget """ self.image.grab_focus() def register_parent_row(self, parent_row): self.parent_row = parent_row def register_update_func(self, func): self.update_func = func def register_all_func(self, function): self.all_func = function def set_value(self, val, all_vals=None, doupdate=True): if doupdate: if val: loader = GdkPixbuf.PixbufLoader() try: loader.write(val.data) loader.close() except GLib.GError: pass else: self.batch_update = True self.set_pixbuf(loader.get_pixbuf(), val.mime) # some file types do not support multiple cover types if val.type is not None: self.type_selection.set_active(val.type) self.type_selection.set_sensitive(True) else: self.type_selection.set_active(-1) self.type_selection.set_sensitive(False) if val.desc is not None: self.description_entry.set_text(val.desc) self.description_entry.set_sensitive(True) else: self.description_entry.set_text('') self.description_entry.set_sensitive(False) self.batch_update = False else: self.batch_update = True self.set_pixbuf(None) self.type_selection.set_active(-1) self.type_selection.set_sensitive(False) self.description_entry.set_text('') self.description_entry.set_sensitive(False) self.batch_update = False self.call_update_func() if not None in (all_vals, self.all_button): self.all_button.set_active(all(val == v for v in all_vals)) def get_value(self): if not self.pixbuf: return None mime = self.mime_info[self.info.mime] # Retrieve proper image data writer = io.BytesIO() self.pixbuf.save_to_callback(writer.write, mime['type'], mime['options']) # Move to the beginning of the buffer to allow read operations writer.seek(0) return self.info._replace(data=writer.read()) def call_update_func(self): """ Wrapper around the update function """ if not self.update_func or self.batch_update: return self.update_func(self, self.parent_row.tag, self.parent_row.multi_id, self.get_value) def set_pixbuf(self, pixbuf, mime=None): """ Updates the displayed cover image and info values """ self.pixbuf = pixbuf if pixbuf is None: self.image.set_from_icon_name('list-add', Gtk.IconSize.DIALOG) self.info_label.set_markup('') else: self.image.set_from_pixbuf(pixbuf.scale_simple( 100, 100, GdkPixbuf.InterpType.BILINEAR)) width, height = pixbuf.get_width(), pixbuf.get_height() if mime is None: # TRANSLATORS: do not translate 'width' and 'height' markup = _('{width}x{height} pixels').format(width=width, height=height) else: # TRANSLATORS: do not translate 'format', 'width', and 'height' markup = _('{format} ({width}x{height} pixels)').format( format=self.mime_info.get(mime, self.mime_info['image/'])['title'], width=width, height=height ) self.info_label.set_markup(markup) self.info = self.info._replace(mime=mime) def on_button_clicked(self, button): """ Allows setting the cover image using a file selection dialog """ dialog = dialogs.FileOperationDialog( title=_('Select image to set as cover'), parent=self.get_toplevel(), buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK) ) dialog.set_select_multiple(False) filefilter = Gtk.FileFilter() # Not using Gtk.FileFilter.add_pixbuf_formats since # not all image formats are supported in tags filefilter.set_name(_('Supported image formats')) filefilter.add_pattern('*.[jJ][pP][gG]') filefilter.add_pattern('*.[jJ][pP][eE][gG]') filefilter.add_pattern('*.[pP][nN][gG]') dialog.add_filter(filefilter) if dialog.run() == Gtk.ResponseType.OK: filename = dialog.get_filename() try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) info = GdkPixbuf.Pixbuf.get_file_info(filename)[0] except TypeError: pass else: self.batch_update = True self.set_pixbuf(pixbuf, info['mime_types'][0]) self.type_selection.set_active(self.default_type) self.type_selection.set_sensitive(True) self.description_entry.set_text(os.path.basename(filename).rsplit('.', 1)[0]) self.description_entry.set_sensitive(True) self.batch_update = False self.call_update_func() dialog.destroy() def on_button_drag_data_received(self, widget, context, x, y, selection, info, time): """ Allows setting the cover image via drag and drop """ if selection.target.name() == 'text/uri-list': filename = Gio.File.new_for_uri(selection.get_uris()[0]).get_path() try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) info = GdkPixbuf.Pixbuf.get_file_info(filename)[0] except TypeError: pass else: self.batch_update = True self.set_pixbuf(pixbuf, info['mime_types'][0]) self.type_selection.set_active(self.default_type) self.description_entry.set_sensitive(True) self.description_entry.set_text(os.path.basename(filename).rsplit('.', 1)[0]) self.description_entry.set_sensitive(True) self.batch_update = False self.call_update_func() def on_type_selection_changed(self, combobox): """ Notifies about changes in the cover type """ self.info = self.info._replace(type=self.type_model[combobox.get_active()][0]) self.call_update_func() def on_description_entry_changed(self, entry): """ Notifies about changes in the cover description """ self.info = self.info._replace(desc=entry.get_text()) self.call_update_func()
def read_tags(self, tags): td = super(FlacFormat, self).read_tags(tags) if 'cover' in tags: td['cover'] = [CoverImage(type=p.type, desc=p.desc, mime=p.mime, data=p.data) \ for p in self.mutagen.pictures] return td
def __init__(self, all_button=True): gtk.HBox.__init__(self, homogeneous=False, spacing=5) self.parent_row = None self.all_func = None self.update_func = None # Prevents the update function from being called, make # sure you do that manually after the batch update self.batch_update = False self.pixbuf = None self.info = CoverImage(None, None, None, None) self.default_type = 3 self.mime_info = { 'image/jpeg': { # Title for display 'title': _('JPEG image'), # Type and options for GDK Pixbuf saving 'type': 'jpeg', 'options': { 'quality': '90' } }, 'image/png': { 'title': _('PNG image'), 'type': 'png', 'options': {} }, 'image/': { 'title': _('Image'), # Store unknown images as JPEG 'type': 'jpeg', 'options': { 'quality': '90' } }, # TODO: Handle linked images '-->': { 'title': _('Linked image') } } builder = gtk.Builder() builder.add_from_file( xdg.get_data_path('ui', 'trackproperties_dialog_cover_row.ui')) builder.connect_signals(self) cover_row = builder.get_object('cover_row') cover_row.reparent(self) button = builder.get_object('button') button.drag_dest_set(gtk.DEST_DEFAULT_ALL, [], gtk.gdk.ACTION_COPY) button.drag_dest_add_uri_targets() self.image = builder.get_object('image') self.info_label = builder.get_object('info_label') self.type_model = builder.get_object('type_model') self.type_selection = builder.get_object('type_selection') self.type_selection.set_sensitive(False) self.description_entry = builder.get_object('description_entry') self.description_entry.set_sensitive(False) self.all_button = None if all_button: self.all_button = AllButton(self) self.pack_start(self.all_button, expand=False, fill=False)
class TagImageField(gtk.HBox): def __init__(self, all_button=True): gtk.HBox.__init__(self, homogeneous=False, spacing=5) self.parent_row = None self.all_func = None self.update_func = None # Prevents the update function from being called, make # sure you do that manually after the batch update self.batch_update = False self.pixbuf = None self.info = CoverImage(None, None, None, None) self.default_type = 3 self.mime_info = { 'image/jpeg': { # Title for display 'title': _('JPEG image'), # Type and options for GDK Pixbuf saving 'type': 'jpeg', 'options': { 'quality': '90' } }, 'image/png': { 'title': _('PNG image'), 'type': 'png', 'options': {} }, 'image/': { 'title': _('Image'), # Store unknown images as JPEG 'type': 'jpeg', 'options': { 'quality': '90' } }, # TODO: Handle linked images '-->': { 'title': _('Linked image') } } builder = gtk.Builder() builder.add_from_file( xdg.get_data_path('ui', 'trackproperties_dialog_cover_row.ui')) builder.connect_signals(self) cover_row = builder.get_object('cover_row') cover_row.reparent(self) button = builder.get_object('button') button.drag_dest_set(gtk.DEST_DEFAULT_ALL, [], gtk.gdk.ACTION_COPY) button.drag_dest_add_uri_targets() self.image = builder.get_object('image') self.info_label = builder.get_object('info_label') self.type_model = builder.get_object('type_model') self.type_selection = builder.get_object('type_selection') self.type_selection.set_sensitive(False) self.description_entry = builder.get_object('description_entry') self.description_entry.set_sensitive(False) self.all_button = None if all_button: self.all_button = AllButton(self) self.pack_start(self.all_button, expand=False, fill=False) def grab_focus(self): """ Gives focus to the internal widget """ self.image.grab_focus() def register_parent_row(self, parent_row): self.parent_row = parent_row def register_update_func(self, func): self.update_func = func def register_all_func(self, function): self.all_func = function def set_value(self, val, all_vals=None, doupdate=True): if doupdate: if val: loader = gtk.gdk.PixbufLoader() try: loader.write(val.data) loader.close() except glib.GError: pass else: self.batch_update = True self.set_pixbuf(loader.get_pixbuf(), val.mime) # some file types do not support multiple cover types if val.type is not None: self.type_selection.set_active(val.type) self.type_selection.set_sensitive(True) else: self.type_selection.set_active(-1) self.type_selection.set_sensitive(False) if val.desc is not None: self.description_entry.set_text(val.desc) self.description_entry.set_sensitive(True) else: self.description_entry.set_text('') self.description_entry.set_sensitive(False) self.batch_update = False else: self.batch_update = True self.set_pixbuf(None) self.type_selection.set_active(-1) self.type_selection.set_sensitive(False) self.description_entry.set_text('') self.description_entry.set_sensitive(False) self.batch_update = False self.call_update_func() if not None in (all_vals, self.all_button): self.all_button.set_active(all(val == v for v in all_vals)) def get_value(self): if not self.pixbuf: return None mime = self.mime_info[self.info.mime] # Retrieve proper image data writer = io.BytesIO() self.pixbuf.save_to_callback(writer.write, mime['type'], mime['options']) # Move to the beginning of the buffer to allow read operations writer.seek(0) return self.info._replace(data=writer.read()) def call_update_func(self): """ Wrapper around the update function """ if not self.update_func or self.batch_update: return self.update_func(self, self.parent_row.tag, self.parent_row.multi_id, self.get_value) def set_pixbuf(self, pixbuf, mime=None): """ Updates the displayed cover image and info values """ self.pixbuf = pixbuf if pixbuf is None: self.image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_DIALOG) self.info_label.set_markup('') else: self.image.set_from_pixbuf( pixbuf.scale_simple(100, 100, gtk.gdk.INTERP_BILINEAR)) width, height = pixbuf.get_width(), pixbuf.get_height() if mime is None: # TRANSLATORS: do not translate 'width' and 'height' markup = _('{width}x{height} pixels').format(width=width, height=height) else: # TRANSLATORS: do not translate 'format', 'width', and 'height' markup = _('{format} ({width}x{height} pixels)').format( format=self.mime_info.get( mime, self.mime_info['image/'])['title'], width=width, height=height) self.info_label.set_markup(markup) self.info = self.info._replace(mime=mime) def on_button_clicked(self, button): """ Allows setting the cover image using a file selection dialog """ dialog = dialogs.FileOperationDialog( title=_('Select image to set as cover'), parent=self.get_toplevel(), buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dialog.set_select_multiple(False) filefilter = gtk.FileFilter() # Not using gtk.FileFilter.add_pixbuf_formats since # not all image formats are supported in tags filefilter.set_name(_('Supported image formats')) filefilter.add_pattern('*.[jJ][pP][gG]') filefilter.add_pattern('*.[jJ][pP][eE][gG]') filefilter.add_pattern('*.[pP][nN][gG]') dialog.add_filter(filefilter) if dialog.run() == gtk.RESPONSE_OK: filename = dialog.get_filename() try: pixbuf = gtk.gdk.pixbuf_new_from_file(filename) info = gtk.gdk.pixbuf_get_file_info(filename)[0] except TypeError: pass else: self.batch_update = True self.set_pixbuf(pixbuf, info['mime_types'][0]) self.type_selection.set_active(self.default_type) self.type_selection.set_sensitive(True) self.description_entry.set_text( os.path.basename(filename).rsplit('.', 1)[0]) self.description_entry.set_sensitive(True) self.batch_update = False self.call_update_func() dialog.destroy() def on_button_drag_data_received(self, widget, context, x, y, selection, info, time): """ Allows setting the cover image via drag and drop """ if selection.target == 'text/uri-list': filename = gio.File(selection.get_uris()[0]).get_path() try: pixbuf = gtk.gdk.pixbuf_new_from_file(filename) info = gtk.gdk.pixbuf_get_file_info(filename)[0] except TypeError: pass else: self.batch_update = True self.set_pixbuf(pixbuf, info['mime_types'][0]) self.type_selection.set_active(self.default_type) self.description_entry.set_sensitive(True) self.description_entry.set_text( os.path.basename(filename).rsplit('.', 1)[0]) self.description_entry.set_sensitive(True) self.batch_update = False self.call_update_func() def on_type_selection_changed(self, combobox): """ Notifies about changes in the cover type """ self.info = self.info._replace( type=self.type_model[combobox.get_active()][0]) self.call_update_func() def on_description_entry_changed(self, entry): """ Notifies about changes in the cover description """ self.info = self.info._replace(desc=entry.get_text()) self.call_update_func()
class TagImageField(Gtk.Box): __gtype_name__ = 'TagImageField' button, image, type_model, description_entry, type_selection, \ info_label = GtkTemplate.Child.widgets(6) def __init__(self, all_button=True): Gtk.Box.__init__(self) self.init_template() self.parent_row = None self.all_func = None self.update_func = None # Prevents the update function from being called, make # sure you do that manually after the batch update self.batch_update = False self.pixbuf = None self.info = CoverImage(None, None, None, None) self.default_type = 3 self.mime_info = { 'image/jpeg': { # Title for display 'title': _('JPEG image'), # Type and options for GDK Pixbuf saving 'type': 'jpeg', 'options': { 'quality': '90' } }, 'image/png': { 'title': _('PNG image'), 'type': 'png', 'options': {} }, 'image/': { 'title': _('Image'), # Store unknown images as JPEG 'type': 'jpeg', 'options': { 'quality': '90' } }, # TODO: Handle linked images '-->': { 'title': _('Linked image') } } self.button.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) self.button.drag_dest_add_uri_targets() self.type_selection.connect('scroll-event', dummy_scroll_handler) self.all_button = None if all_button: self.all_button = AllButton(self) self.pack_start(self.all_button, False, False, 0) def grab_focus(self): """ Gives focus to the internal widget """ self.image.grab_focus() def register_parent_row(self, parent_row): self.parent_row = parent_row def register_update_func(self, func): self.update_func = func def register_all_func(self, function): self.all_func = function def set_value(self, val, all_vals=None, doupdate=True): if doupdate: if val: loader = GdkPixbuf.PixbufLoader() try: loader.write(val.data) loader.close() except GLib.GError: pass else: self.batch_update = True self.set_pixbuf(loader.get_pixbuf(), val.mime) # some file types do not support multiple cover types if val.type is not None: self.type_selection.set_active(val.type) self.type_selection.set_sensitive(True) else: self.type_selection.set_active(-1) self.type_selection.set_sensitive(False) if val.desc is not None: self.description_entry.set_text(val.desc) self.description_entry.set_sensitive(True) else: self.description_entry.set_text('') self.description_entry.set_sensitive(False) self.batch_update = False else: self.batch_update = True self.set_pixbuf(None) self.type_selection.set_active(-1) self.type_selection.set_sensitive(False) self.description_entry.set_text('') self.description_entry.set_sensitive(False) self.batch_update = False self.call_update_func() if not None in (all_vals, self.all_button): self.all_button.set_active(all(val == v for v in all_vals)) def get_value(self): if not self.pixbuf: return None mime = self.mime_info[self.info.mime] # Retrieve proper image data writer = io.BytesIO() def gdk_pixbuf_save_func(buf, count, user_data): if writer.write(buf) == count: return True return False # workaround for some undocumented changes in GdkPixbuf API # see https://bugzilla.gnome.org/show_bug.cgi?id=670372 # see https://github.com/mypaint/mypaint/issues/236 try: save_to_callback_function = self.pixbuf.save_to_callbackv except AttributeError: save_to_callback_function = self.pixbuf.save_to_callback save_to_callback_function(gdk_pixbuf_save_func, None, mime['type'], \ mime['options'].keys(), mime['options'].values()) # Move to the beginning of the buffer to allow read operations writer.seek(0) return self.info._replace(data=writer.read()) def call_update_func(self): """ Wrapper around the update function """ if not self.update_func or self.batch_update: return self.update_func(self, self.parent_row.tag, self.parent_row.multi_id, self.get_value) def set_pixbuf(self, pixbuf, mime=None): """ Updates the displayed cover image and info values """ self.pixbuf = pixbuf if pixbuf is None: self.image.set_from_icon_name('list-add', Gtk.IconSize.DIALOG) self.info_label.set_markup('') else: self.image.set_from_pixbuf( pixbuf.scale_simple(100, 100, GdkPixbuf.InterpType.BILINEAR)) width, height = pixbuf.get_width(), pixbuf.get_height() if mime is None: # TRANSLATORS: do not translate 'width' and 'height' markup = _('{width}x{height} pixels').format(width=width, height=height) else: # TRANSLATORS: do not translate 'format', 'width', and 'height' markup = _('{format} ({width}x{height} pixels)').format( format=self.mime_info.get( mime, self.mime_info['image/'])['title'], width=width, height=height) self.info_label.set_markup(markup) self.info = self.info._replace(mime=mime) def _on_button_clicked(self, button): """ Allows setting the cover image using a file selection dialog """ dialog = dialogs.FileOperationDialog( title=_('Select image to set as cover'), parent=self.get_toplevel(), buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)) dialog.set_select_multiple(False) filefilter = Gtk.FileFilter() # Not using Gtk.FileFilter.add_pixbuf_formats since # not all image formats are supported in tags filefilter.set_name(_('Supported image formats')) filefilter.add_pattern('*.[jJ][pP][gG]') filefilter.add_pattern('*.[jJ][pP][eE][gG]') filefilter.add_pattern('*.[pP][nN][gG]') dialog.add_filter(filefilter) if dialog.run() == Gtk.ResponseType.OK: filename = dialog.get_filename() try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) info = GdkPixbuf.Pixbuf.get_file_info(filename)[0] except TypeError: pass else: self.batch_update = True self.set_pixbuf(pixbuf, info.get_mime_types()[0]) self.type_selection.set_active(self.default_type) self.type_selection.set_sensitive(True) self.description_entry.set_text( os.path.basename(filename).rsplit('.', 1)[0]) self.description_entry.set_sensitive(True) self.batch_update = False self.call_update_func() dialog.destroy() def _on_button_drag_data_received(self, widget, context, x, y, selection, info, time): """ Allows setting the cover image via drag and drop """ if selection.target.name() == 'text/uri-list': filename = Gio.File.new_for_uri(selection.get_uris()[0]).get_path() try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) info = GdkPixbuf.Pixbuf.get_file_info(filename)[0] except TypeError: pass else: self.batch_update = True self.set_pixbuf(pixbuf, info['mime_types'][0]) self.type_selection.set_active(self.default_type) self.description_entry.set_sensitive(True) self.description_entry.set_text( os.path.basename(filename).rsplit('.', 1)[0]) self.description_entry.set_sensitive(True) self.batch_update = False self.call_update_func() def _on_type_selection_changed(self, combobox): """ Notifies about changes in the cover type """ self.info = self.info._replace( type=self.type_model[combobox.get_active()][0]) self.call_update_func() def _on_description_entry_changed(self, entry): """ Notifies about changes in the cover description """ self.info = self.info._replace(desc=entry.get_text()) self.call_update_func()