Пример #1
0
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()
Пример #2
0
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()
Пример #3
0
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()