コード例 #1
0
ファイル: media.py プロジェクト: silkecho/glowing-silk
    def __init__(self, library, main):
        super(MediaDevices, self).__init__(spacing=6)
        self._register_instance()

        self.__cache = {}

        # Device list on the left pane
        swin = ScrolledWindow()
        swin.set_shadow_type(gtk.SHADOW_IN)
        swin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.pack_start(swin)

        self.__view = view = AllTreeView()
        view.set_model(self.__devices)
        view.set_rules_hint(True)
        view.set_headers_visible(False)
        view.get_selection().set_mode(gtk.SELECTION_BROWSE)
        view.get_selection().connect_object('changed', self.__refresh, False)
        view.connect('popup-menu', self.__popup_menu, library)
        if main:
            view.connect('row-activated', lambda *a: self.emit("activated"))
        swin.add(view)

        col = gtk.TreeViewColumn("Devices")
        view.append_column(col)

        render = gtk.CellRendererPixbuf()
        col.pack_start(render, expand=False)
        col.add_attribute(render, 'icon-name', 1)

        self.__render = render = gtk.CellRendererText()
        render.set_property('ellipsize', pango.ELLIPSIZE_END)
        render.connect('edited', self.__edited)
        col.pack_start(render)
        col.set_cell_data_func(render, MediaDevices.cell_data)

        hbox = gtk.HBox(spacing=6)
        hbox.set_homogeneous(True)
        if main:
            self.pack_start(Alignment(hbox, left=3, bottom=3), expand=False)
        else:
            self.pack_start(hbox, expand=False)

        self.__refresh_button = refresh = gtk.Button(stock=gtk.STOCK_REFRESH)
        refresh.connect_object('clicked', self.__refresh, True)
        refresh.set_sensitive(False)
        hbox.pack_start(refresh)

        self.__eject_button = eject = gtk.Button(_("_Eject"))
        eject.set_image(
            gtk.image_new_from_icon_name("media-eject", gtk.ICON_SIZE_BUTTON))
        eject.connect('clicked', self.__eject)
        eject.set_sensitive(False)
        hbox.pack_start(eject)

        # Device info on the right pane
        self.__header = table = gtk.Table()
        table.set_col_spacings(8)

        self.__device_icon = icon = gtk.Image()
        icon.set_size_request(48, 48)
        table.attach(icon, 0, 1, 0, 2, 0)

        self.__device_name = label = gtk.Label()
        label.set_alignment(0, 0)
        table.attach(label, 1, 3, 0, 1)

        self.__device_space = label = gtk.Label()
        label.set_alignment(0, 0.5)
        table.attach(label, 1, 2, 1, 2)

        self.__progress = progress = gtk.ProgressBar()
        progress.set_size_request(150, -1)
        table.attach(progress, 2, 3, 1, 2, xoptions=0, yoptions=0)

        self.accelerators = gtk.AccelGroup()
        key, mod = gtk.accelerator_parse('F2')
        self.accelerators.connect_group(key, mod, 0, self.__rename)

        self.__statusbar = WaitLoadBar()

        self.show_all()
コード例 #2
0
ファイル: media.py プロジェクト: silkecho/glowing-silk
class MediaDevices(gtk.VBox, Browser, util.InstanceTracker):
    __gsignals__ = Browser.__gsignals__

    name = _("Media Devices")
    accelerated_name = _("_Media Devices")
    priority = 25
    replaygain_profiles = ['track']

    __devices = gtk.ListStore(object, str)
    __busy = False
    __last = None

    @staticmethod
    def cell_data(col, render, model, iter):
        device = model[iter][0]
        if device.is_connected():
            render.markup = "<b>%s</b>" % util.escape(device['name'])
        else:
            render.markup = util.escape(device['name'])
        render.set_property('markup', render.markup)

    @classmethod
    def init(klass, library):
        devices.device_manager.connect('added', klass.__device_added)
        devices.device_manager.connect('removed', klass.__device_removed)
        devices.device_manager.discover()

    @classmethod
    def devices(klass):
        return [row[0] for row in klass.__devices]

    @classmethod
    def __device_added(klass, manager, device):
        klass.__devices.append(row=[device, device.icon])

    @classmethod
    def __device_removed(klass, manager, bid):
        for row in klass.__devices:
            if row[0].bid == bid:
                klass.__devices.remove(row.iter)
                break

    def __init__(self, library, main):
        super(MediaDevices, self).__init__(spacing=6)
        self._register_instance()

        self.__cache = {}

        # Device list on the left pane
        swin = ScrolledWindow()
        swin.set_shadow_type(gtk.SHADOW_IN)
        swin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.pack_start(swin)

        self.__view = view = AllTreeView()
        view.set_model(self.__devices)
        view.set_rules_hint(True)
        view.set_headers_visible(False)
        view.get_selection().set_mode(gtk.SELECTION_BROWSE)
        view.get_selection().connect_object('changed', self.__refresh, False)
        view.connect('popup-menu', self.__popup_menu, library)
        if main:
            view.connect('row-activated', lambda *a: self.emit("activated"))
        swin.add(view)

        col = gtk.TreeViewColumn("Devices")
        view.append_column(col)

        render = gtk.CellRendererPixbuf()
        col.pack_start(render, expand=False)
        col.add_attribute(render, 'icon-name', 1)

        self.__render = render = gtk.CellRendererText()
        render.set_property('ellipsize', pango.ELLIPSIZE_END)
        render.connect('edited', self.__edited)
        col.pack_start(render)
        col.set_cell_data_func(render, MediaDevices.cell_data)

        hbox = gtk.HBox(spacing=6)
        hbox.set_homogeneous(True)
        if main:
            self.pack_start(Alignment(hbox, left=3, bottom=3), expand=False)
        else:
            self.pack_start(hbox, expand=False)

        self.__refresh_button = refresh = gtk.Button(stock=gtk.STOCK_REFRESH)
        refresh.connect_object('clicked', self.__refresh, True)
        refresh.set_sensitive(False)
        hbox.pack_start(refresh)

        self.__eject_button = eject = gtk.Button(_("_Eject"))
        eject.set_image(
            gtk.image_new_from_icon_name("media-eject", gtk.ICON_SIZE_BUTTON))
        eject.connect('clicked', self.__eject)
        eject.set_sensitive(False)
        hbox.pack_start(eject)

        # Device info on the right pane
        self.__header = table = gtk.Table()
        table.set_col_spacings(8)

        self.__device_icon = icon = gtk.Image()
        icon.set_size_request(48, 48)
        table.attach(icon, 0, 1, 0, 2, 0)

        self.__device_name = label = gtk.Label()
        label.set_alignment(0, 0)
        table.attach(label, 1, 3, 0, 1)

        self.__device_space = label = gtk.Label()
        label.set_alignment(0, 0.5)
        table.attach(label, 1, 2, 1, 2)

        self.__progress = progress = gtk.ProgressBar()
        progress.set_size_request(150, -1)
        table.attach(progress, 2, 3, 1, 2, xoptions=0, yoptions=0)

        self.accelerators = gtk.AccelGroup()
        key, mod = gtk.accelerator_parse('F2')
        self.accelerators.connect_group(key, mod, 0, self.__rename)

        self.__statusbar = WaitLoadBar()

        self.show_all()

    def pack(self, songpane):
        self.__vbox = vbox = gtk.VBox(spacing=6)
        vbox.pack_start(self.__header, expand=False)
        vbox.pack_start(songpane)
        vbox.pack_start(self.__statusbar, expand=False)

        vbox.show()
        self.__header.show_all()
        self.__header.hide()
        self.__statusbar.show_all()
        self.__statusbar.hide()

        self.__paned = paned = qltk.RHPaned()
        paned.pack1(self)
        paned.pack2(vbox)
        return paned

    def unpack(self, container, songpane):
        self.__vbox.remove(songpane)
        self.__paned.remove(self)

    def Menu(self, songs, songlist, library):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            device = model[iter][0]
            delete = device.delete and self.__delete_songs
        else:
            delete = False

        menu = SongsMenu(library, songs, delete=delete, remove=False,
            accels=songlist.accelerators, parent=self)
        return menu

    def activate(self):
        self.__refresh()

    def save(self):
        selection = self.__view.get_selection()
        model, iter = selection.get_selected()
        if iter:
            config.set('browsers', 'media', model[iter][0]['name'])

    def restore(self):
        try: name = config.get('browsers', 'media')
        except config.error: pass
        else:
            for row in self.__devices:
                if row[0]['name'] == name: break
            else: return
            selection = self.__view.get_selection()
            selection.unselect_all()
            selection.select_iter(row.iter)

    def select(self, device):
        for row in self.__devices:
            if row[0] == device: break
        else: return

        # Force a full refresh
        try: del self.__cache[device.bid]
        except KeyError: pass

        selection = self.__view.get_selection()
        selection.unselect_all()
        selection.select_iter(row.iter)

    def dropped(self, songlist, songs):
        return self.__copy_songs(songlist, songs)

    def __popup_menu(self, view, library):
        model, iter = view.get_selection().get_selected()
        device = model[iter][0]

        if device.is_connected() and not self.__busy:
            songs = self.__list_songs(device)
        else:
            songs = []
        menu = SongsMenu(library, songs, playlists=False,
                         devices=False, remove=False, parent=self)

        menu.preseparate()

        props = gtk.ImageMenuItem(gtk.STOCK_PROPERTIES)
        props.connect_object('activate', self.__properties, model[iter][0])
        props.set_sensitive(not self.__busy)
        menu.prepend(props)

        ren = qltk.MenuItem(_("_Rename"), gtk.STOCK_EDIT)
        keyval, mod = gtk.accelerator_parse("F2")
        ren.add_accelerator(
            'activate', self.accelerators, keyval, mod, gtk.ACCEL_VISIBLE)
        def rename(path):
            self.__render.set_property('editable', True)
            view.set_cursor(path, view.get_columns()[0], start_editing=True)
        ren.connect_object('activate', rename, model.get_path(iter))
        menu.prepend(ren)

        menu.preseparate()

        eject = gtk.ImageMenuItem(_("_Eject"))
        eject.set_image(
            gtk.image_new_from_icon_name("media-eject", gtk.ICON_SIZE_MENU))
        eject.set_sensitive(
            not self.__busy and device.eject and device.is_connected())
        eject.connect_object('activate', self.__eject, None)
        menu.prepend(eject)

        refresh = gtk.ImageMenuItem(gtk.STOCK_REFRESH)
        refresh.set_sensitive(device.is_connected())
        refresh.connect_object('activate', self.__refresh, True)
        menu.prepend(refresh)

        menu.show_all()
        menu.popup(None, None, None, 0, gtk.get_current_event_time())
        return True

    def __properties(self, device):
        DeviceProperties(self, device).run()
        self.__set_name(device)

    def __rename(self, group, acceleratable, keyval, modifier):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            self.__render.set_property('editable', True)
            self.__view.set_cursor(model.get_path(iter),
                                   self.__view.get_columns()[0],
                                   start_editing=True)

    def __edited(self, render, path, newname):
        self.__devices[path][0]['name'] = newname
        self.__set_name(self.__devices[path][0])
        render.set_property('editable', False)
        devices.write()

    def __set_name(self, device):
        self.__device_name.set_markup(
            '<span size="x-large"><b>%s</b></span>' %
                util.escape(device['name']))

    def __refresh(self, rescan=False):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            path = model[iter].path
            if not rescan and self.__last == path: return
            self.__last = path

            device = model[iter][0]
            self.__device_icon.set_from_icon_name(device.icon, gtk.ICON_SIZE_DIALOG)
            self.__set_name(device)

            songs = []
            if device.is_connected():
                self.__header.show_all()
                self.__eject_button.set_sensitive(bool(device.eject))
                self.__refresh_button.set_sensitive(True)
                self.__refresh_space(device)

                try: songs = self.__list_songs(device, rescan)
                except NotImplementedError: pass
            else:
                self.__eject_button.set_sensitive(False)
                self.__refresh_button.set_sensitive(False)
                self.__header.hide()
            self.emit('songs-selected', songs, device.ordered)
        else:
            self.__last = None
            self.emit('songs-selected', [], False)

    def __refresh_space(self, device):
        try: space, free = device.get_space()
        except NotImplementedError:
            self.__device_space.set_text("")
            self.__progress.hide()
        else:
            used = space - free
            fraction = float(used) / space

            self.__device_space.set_markup(
                _("<b>%s</b> used, <b>%s</b> available") %
                    (util.format_size(used), util.format_size(free)))
            self.__progress.set_fraction(fraction)
            self.__progress.set_text("%.f%%" % round(fraction * 100))
            self.__progress.show()

    def __list_songs(self, device, rescan=False):
        if rescan or not device.bid in self.__cache:
            self.__busy = True
            self.__cache[device.bid] = device.list(self.__statusbar)
            self.__busy = False
        return self.__cache[device.bid]

    def __check_device(self, device, message):
        if not device.is_connected():
            qltk.WarningMessage(
                self, message,
                _("<b>%s</b> is not connected.") % util.escape(device['name'])
            ).run()
            return False
        return True

    def __copy_songs(self, songlist, songs):
        model, iter = self.__view.get_selection().get_selected()
        if not iter: return False

        device = model[iter][0]
        if not self.__check_device(device, _("Unable to copy songs")):
            return False

        self.__busy = True

        wlb = self.__statusbar
        wlb.setup(len(songs), _("Copying <b>%(song)s</b>"), { 'song': '' })
        wlb.show()

        model = songlist.get_model()
        for song in songs:
            label = util.escape(song('~artist~title'))
            if wlb.step(song=label):
                wlb.hide()
                break

            if len(model) > 0:
                songlist.scroll_to_cell(model[-1].path)
            while gtk.events_pending(): gtk.main_iteration()

            space, free = device.get_space()
            if free < os.path.getsize(song['~filename']):
                wlb.hide()
                qltk.WarningMessage(
                    self, _("Unable to copy song"),
                    _("There is not enough free space for this song.")
                ).run()
                break

            status = device.copy(songlist, song)
            if isinstance(status, AudioFile):
                model.append([status])
                try: self.__cache[device.bid].append(song)
                except KeyError: pass
                self.__refresh_space(device)
            else:
                msg = _("<b>%s</b> could not be copied.") % label
                if type(status) == unicode:
                    msg += "\n\n" + util.escape(status)
                qltk.WarningMessage(self, _("Unable to copy song"), msg).run()

        if device.cleanup and not device.cleanup(wlb, 'copy'):
            pass
        else:
            wlb.hide()

        self.__busy = False
        return True

    def __delete_songs(self, songs):
        model, iter = self.__view.get_selection().get_selected()
        if not iter:
            return False
        songlist = qltk.get_top_parent(self).songlist

        device = model[iter][0]
        if not self.__check_device(device, _("Unable to delete songs")):
            return False

        song_titles = [s('~artist~title') for s in songs]
        if DeleteDialog(self, song_titles, False, True).run() != 2:
            return False

        self.__busy = True

        wlb = self.__statusbar
        wlb.setup(len(songs), _("Deleting <b>%(song)s</b>"), { 'song': '' })
        wlb.show()

        model = songlist.get_model()
        for song in songs:
            label = util.escape(song('~artist~title'))
            if wlb.step(song=label):
                wlb.hide()
                break

            status = device.delete(songlist, song)
            if status == True:
                model.remove(model.find(song))
                try: self.__cache[device.bid].remove(song)
                except (KeyError, ValueError): pass
                self.__refresh_space(device)
            else:
                msg = _("<b>%s</b> could not be deleted.") % label
                if type(status) == unicode:
                    msg += "\n\n%s" % status
                qltk.WarningMessage(
                    self, _("Unable to delete song"), msg).run()

        if device.cleanup and not device.cleanup(wlb, 'delete'):
            pass
        else:
            wlb.hide()

        self.__busy = False

    def __eject(self, button):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            device = model[iter][0]
            status = device.eject()
            if status != True:
                msg = _("Ejecting <b>%s</b> failed.") % device['name']
                if status:
                    msg += "\n\n%s" % status
                qltk.ErrorMessage(self, _("Unable to eject device"), msg).run()
コード例 #3
0
ファイル: media.py プロジェクト: Tjorriemorrie/quodlibet
    def __init__(self, library):
        super(MediaDevices, self).__init__(spacing=6)
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self._register_instance()

        self.__cache = {}

        # Device list on the left pane
        swin = ScrolledWindow()
        swin.set_shadow_type(Gtk.ShadowType.IN)
        swin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        self.pack_start(swin, True, True, 0)

        self.__view = view = AllTreeView()
        view.set_model(self.__devices)
        view.set_rules_hint(True)
        view.set_headers_visible(False)
        view.get_selection().set_mode(Gtk.SelectionMode.BROWSE)
        connect_obj(view.get_selection(), 'changed', self.__refresh, False)
        view.connect('popup-menu', self.__popup_menu, library)
        view.connect('row-activated', lambda *a: self.songs_activated())
        swin.add(view)

        col = Gtk.TreeViewColumn("Devices")
        view.append_column(col)

        render = Gtk.CellRendererPixbuf()
        col.pack_start(render, False)
        col.add_attribute(render, 'icon-name', 1)

        self.__render = render = Gtk.CellRendererText()
        render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.connect('edited', self.__edited)
        col.pack_start(render, True)
        col.set_cell_data_func(render, MediaDevices.cell_data)

        hbox = Gtk.HBox(spacing=6)
        hbox.set_homogeneous(True)
        self.pack_start(Align(hbox, left=3, bottom=3), False, True, 0)

        # refresh button
        refresh = Button(_("_Refresh"), Gtk.STOCK_REFRESH, Gtk.IconSize.MENU)
        self.__refresh_button = refresh
        connect_obj(refresh, 'clicked', self.__refresh, True)
        refresh.set_sensitive(False)
        hbox.pack_start(refresh, True, True, 0)

        # eject button
        eject = Button(_("_Eject"), "media-eject", Gtk.IconSize.MENU)
        self.__eject_button = eject
        eject.connect('clicked', self.__eject)
        eject.set_sensitive(False)
        hbox.pack_start(eject, True, True, 0)

        # Device info on the right pane
        self.__header = table = Gtk.Table()
        table.set_col_spacings(8)

        self.__device_icon = icon = Gtk.Image()
        icon.set_size_request(48, 48)
        table.attach(icon, 0, 1, 0, 2, 0)

        self.__device_name = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0)
        table.attach(label, 1, 3, 0, 1)

        self.__device_space = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0.5)
        table.attach(label, 1, 2, 1, 2)

        self.__progress = progress = Gtk.ProgressBar()
        progress.set_size_request(150, -1)
        table.attach(progress, 2, 3, 1, 2, xoptions=0, yoptions=0)

        self.accelerators = Gtk.AccelGroup()
        key, mod = Gtk.accelerator_parse('F2')
        self.accelerators.connect(key, mod, 0, self.__rename)

        self.__statusbar = WaitLoadBar()

        for child in self.get_children():
            child.show_all()
コード例 #4
0
ファイル: media.py プロジェクト: Tjorriemorrie/quodlibet
class MediaDevices(Browser, util.InstanceTracker):
    name = _("Media Devices")
    accelerated_name = _("_Media Devices")
    keys = ["MediaDevices"]
    priority = 25
    uses_main_library = False
    replaygain_profiles = ['track']

    __devices = Gtk.ListStore(object, str)
    __busy = False
    __last = None

    @staticmethod
    def cell_data(col, render, model, iter, data):
        device = model[iter][0]
        if device.is_connected():
            render.markup = "<b>%s</b>" % util.escape(device['name'])
        else:
            render.markup = util.escape(device['name'])
        render.set_property('markup', render.markup)

    @classmethod
    def init(klass, library):
        devices.device_manager.connect('added', klass.__device_added)
        devices.device_manager.connect('removed', klass.__device_removed)
        devices.device_manager.discover()

    @classmethod
    def devices(klass):
        return [row[0] for row in klass.__devices]

    @classmethod
    def __device_added(klass, manager, device):
        klass.__devices.append(row=[device, device.icon])

    @classmethod
    def __device_removed(klass, manager, bid):
        for row in klass.__devices:
            if row[0].bid == bid:
                klass.__devices.remove(row.iter)
                break

    def __init__(self, library):
        super(MediaDevices, self).__init__(spacing=6)
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self._register_instance()

        self.__cache = {}

        # Device list on the left pane
        swin = ScrolledWindow()
        swin.set_shadow_type(Gtk.ShadowType.IN)
        swin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        self.pack_start(swin, True, True, 0)

        self.__view = view = AllTreeView()
        view.set_model(self.__devices)
        view.set_rules_hint(True)
        view.set_headers_visible(False)
        view.get_selection().set_mode(Gtk.SelectionMode.BROWSE)
        connect_obj(view.get_selection(), 'changed', self.__refresh, False)
        view.connect('popup-menu', self.__popup_menu, library)
        view.connect('row-activated', lambda *a: self.songs_activated())
        swin.add(view)

        col = Gtk.TreeViewColumn("Devices")
        view.append_column(col)

        render = Gtk.CellRendererPixbuf()
        col.pack_start(render, False)
        col.add_attribute(render, 'icon-name', 1)

        self.__render = render = Gtk.CellRendererText()
        render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.connect('edited', self.__edited)
        col.pack_start(render, True)
        col.set_cell_data_func(render, MediaDevices.cell_data)

        hbox = Gtk.HBox(spacing=6)
        hbox.set_homogeneous(True)
        self.pack_start(Align(hbox, left=3, bottom=3), False, True, 0)

        # refresh button
        refresh = Button(_("_Refresh"), Gtk.STOCK_REFRESH, Gtk.IconSize.MENU)
        self.__refresh_button = refresh
        connect_obj(refresh, 'clicked', self.__refresh, True)
        refresh.set_sensitive(False)
        hbox.pack_start(refresh, True, True, 0)

        # eject button
        eject = Button(_("_Eject"), "media-eject", Gtk.IconSize.MENU)
        self.__eject_button = eject
        eject.connect('clicked', self.__eject)
        eject.set_sensitive(False)
        hbox.pack_start(eject, True, True, 0)

        # Device info on the right pane
        self.__header = table = Gtk.Table()
        table.set_col_spacings(8)

        self.__device_icon = icon = Gtk.Image()
        icon.set_size_request(48, 48)
        table.attach(icon, 0, 1, 0, 2, 0)

        self.__device_name = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0)
        table.attach(label, 1, 3, 0, 1)

        self.__device_space = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0.5)
        table.attach(label, 1, 2, 1, 2)

        self.__progress = progress = Gtk.ProgressBar()
        progress.set_size_request(150, -1)
        table.attach(progress, 2, 3, 1, 2, xoptions=0, yoptions=0)

        self.accelerators = Gtk.AccelGroup()
        key, mod = Gtk.accelerator_parse('F2')
        self.accelerators.connect(key, mod, 0, self.__rename)

        self.__statusbar = WaitLoadBar()

        for child in self.get_children():
            child.show_all()

    def pack(self, songpane):
        self.__vbox = vbox = Gtk.VBox(spacing=6)
        vbox.pack_start(self.__header, False, True, 0)
        vbox.pack_start(songpane, True, True, 0)
        vbox.pack_start(self.__statusbar, False, True, 0)

        vbox.show()
        self.__header.show_all()
        self.__header.hide()
        self.__statusbar.show_all()
        self.__statusbar.hide()
        self.show()

        self.__paned = paned = qltk.ConfigRHPaned(
            "browsers", "mediadevices_pos", 0.4)
        paned.pack1(self, True, False)
        paned.pack2(vbox, True, False)
        return paned

    def unpack(self, container, songpane):
        self.__vbox.remove(songpane)
        self.__paned.remove(self)

    def Menu(self, songs, library, items):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            device = model[iter][0]
            delete = device.delete and self.__delete_songs
        else:
            delete = False

        menu = SongsMenu(library, songs, delete=delete, remove=False,
                         items=items)
        return menu

    def activate(self):
        self.__refresh()

    def save(self):
        selection = self.__view.get_selection()
        model, iter = selection.get_selected()
        if iter:
            config.set('browsers', 'media', model[iter][0]['name'])

    def restore(self):
        try:
            name = config.get('browsers', 'media')
        except config.Error:
            pass
        else:
            for row in self.__devices:
                if row[0]['name'] == name:
                    break
            else:
                return
            selection = self.__view.get_selection()
            selection.unselect_all()
            selection.select_iter(row.iter)

    def select(self, device):
        for row in self.__devices:
            if row[0] == device:
                break
        else:
            return

        # Force a full refresh
        try:
            del self.__cache[device.bid]
        except KeyError:
            pass

        selection = self.__view.get_selection()
        selection.unselect_all()
        selection.select_iter(row.iter)

    def active_filter(self, song):
        model, iter = self.__view.get_selection().get_selected()
        device = model[iter][0]
        return device.contains(song)

    def dropped(self, songs):
        return self.__copy_songs(songs)

    def __popup_menu(self, view, library):
        model, iter = view.get_selection().get_selected()
        device = model[iter][0]

        if device.is_connected() and not self.__busy:
            songs = self.__list_songs(device)
        else:
            songs = []
        menu = SongsMenu(library, songs, playlists=False,
                         devices=False, remove=False)

        menu.preseparate()

        props = Gtk.ImageMenuItem(Gtk.STOCK_PROPERTIES, use_stock=True)
        connect_obj(props, 'activate', self.__properties, model[iter][0])
        props.set_sensitive(not self.__busy)
        menu.prepend(props)

        ren = qltk.MenuItem(_("_Rename"), Gtk.STOCK_EDIT)
        keyval, mod = Gtk.accelerator_parse("F2")
        ren.add_accelerator(
            'activate', self.accelerators, keyval, mod, Gtk.AccelFlags.VISIBLE)

        def rename(path):
            self.__render.set_property('editable', True)
            view.set_cursor(path, view.get_columns()[0], start_editing=True)
        connect_obj(ren, 'activate', rename, model.get_path(iter))
        menu.prepend(ren)

        menu.preseparate()

        eject = Gtk.ImageMenuItem(_("_Eject"), use_underline=True)
        eject.set_image(
            Gtk.Image.new_from_icon_name("media-eject", Gtk.IconSize.MENU))
        eject.set_sensitive(
            not self.__busy and device.eject and device.is_connected())
        connect_obj(eject, 'activate', self.__eject, None)
        menu.prepend(eject)

        refresh = Gtk.ImageMenuItem(Gtk.STOCK_REFRESH, use_stock=True)
        refresh.set_sensitive(device.is_connected())
        connect_obj(refresh, 'activate', self.__refresh, True)
        menu.prepend(refresh)

        menu.show_all()
        return view.popup_menu(menu, 0, Gtk.get_current_event_time())

    def __properties(self, device):
        DeviceProperties(self, device).run()
        self.__set_name(device)

    def __rename(self, group, acceleratable, keyval, modifier):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            self.__render.set_property('editable', True)
            self.__view.set_cursor(model.get_path(iter),
                                   self.__view.get_columns()[0],
                                   start_editing=True)

    def __edited(self, render, path, newname):
        self.__devices[path][0]['name'] = newname
        self.__set_name(self.__devices[path][0])
        render.set_property('editable', False)
        devices.write()

    def __set_name(self, device):
        self.__device_name.set_markup(
            '<span size="x-large"><b>%s</b></span>' %
            util.escape(device['name']))

    def __refresh(self, rescan=False):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            path = model[iter].path
            if not rescan and self.__last == path:
                return
            self.__last = path

            device = model[iter][0]
            self.__device_icon.set_from_icon_name(
                device.icon, Gtk.IconSize.DIALOG)
            self.__set_name(device)

            songs = []
            if device.is_connected():
                self.__header.show_all()
                self.__eject_button.set_sensitive(bool(device.eject))
                self.__refresh_button.set_sensitive(True)
                self.__refresh_space(device)

                try:
                    songs = self.__list_songs(device, rescan)
                except NotImplementedError:
                    pass
            else:
                self.__eject_button.set_sensitive(False)
                self.__refresh_button.set_sensitive(False)
                self.__header.hide()
            self.songs_selected(songs, device.ordered)
        else:
            self.__last = None
            self.songs_selected([], False)

    def __refresh_space(self, device):
        try:
            space, free = device.get_space()
        except NotImplementedError:
            self.__device_space.set_text("")
            self.__progress.hide()
        else:
            used = space - free
            fraction = float(used) / space

            self.__device_space.set_markup(
                _("%(used-size)s used, %(free-size)s available") %
                {"used-size": util.bold(util.format_size(used)),
                 "free-size": util.bold(util.format_size(free))})
            self.__progress.set_fraction(fraction)
            self.__progress.set_text("%.f%%" % round(fraction * 100))
            self.__progress.show()

    def __list_songs(self, device, rescan=False):
        if rescan or not device.bid in self.__cache:
            self.__busy = True
            self.__cache[device.bid] = device.list(self.__statusbar)
            self.__busy = False
        return self.__cache[device.bid]

    def __check_device(self, device, message):
        if not device.is_connected():
            qltk.WarningMessage(
                self, message,
                _("%s is not connected.") %
                util.bold(util.escape(device['name']))
            ).run()
            return False
        return True

    def __copy_songs(self, songs):
        model, iter = self.__view.get_selection().get_selected()
        if not iter:
            return False

        device = model[iter][0]
        if not self.__check_device(device, _("Unable to copy songs")):
            return False

        self.__busy = True

        wlb = self.__statusbar
        wlb.setup(
            len(songs),
            _("Copying %(song)s") % {'song': '<b>%(song)s</b>'},
            {'song': ''})
        wlb.show()

        for song in songs:
            label = util.escape(song('~artist~title'))
            if wlb.step(song=label):
                wlb.hide()
                break

            space, free = device.get_space()
            if free < os.path.getsize(song['~filename']):
                wlb.hide()
                qltk.WarningMessage(
                    self, _("Unable to copy song"),
                    _("There is not enough free space for this song.")
                ).run()
                break

            status = device.copy(self, song)
            if isinstance(status, AudioFile):
                try:
                    self.__cache[device.bid].append(song)
                except KeyError:
                    pass
                self.__refresh_space(device)
            else:
                msg = _("%s could not be copied.") % util.bold(label)
                if type(status) == unicode:
                    msg += "\n\n" + util.escape(status)
                qltk.WarningMessage(self, _("Unable to copy song"), msg).run()

        if device.cleanup and not device.cleanup(wlb, 'copy'):
            pass
        else:
            wlb.hide()

        self.__busy = False
        return True

    def __delete_songs(self, songs):
        model, iter = self.__view.get_selection().get_selected()
        if not iter:
            return False

        device = model[iter][0]
        if not self.__check_device(device, _("Unable to delete songs")):
            return False

        dialog = DeleteDialog.for_songs(self, songs)
        if dialog.run() != DeleteDialog.RESPONSE_DELETE:
            return False

        self.__busy = True

        wlb = self.__statusbar
        wlb.setup(
            len(songs),
            _("Deleting %(song)s") % {"song": "<b>%(song)s</b>"},
            {'song': ''})
        wlb.show()

        for song in songs:
            label = util.escape(song('~artist~title'))
            if wlb.step(song=label):
                wlb.hide()
                break

            status = device.delete(self, song)
            if status is True:
                try:
                    self.__cache[device.bid].remove(song)
                except (KeyError, ValueError):
                    pass
                self.__refresh_space(device)
            else:
                msg = _("%s could not be deleted.") % util.bold(label)
                if type(status) == unicode:
                    msg += "\n\n%s" % status
                qltk.WarningMessage(
                    self, _("Unable to delete song"), msg).run()

        if device.cleanup and not device.cleanup(wlb, 'delete'):
            pass
        else:
            wlb.hide()

        self.__busy = False

    def __eject(self, button):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            device = model[iter][0]
            status = device.eject()
            if status is not True:
                msg = _("Ejecting %s failed.") % util.bold(device['name'])
                if status:
                    msg += "\n\n%s" % status
                qltk.ErrorMessage(self, _("Unable to eject device"), msg).run()
コード例 #5
0
ファイル: media.py プロジェクト: tonydolan/quodlibet
    def __init__(self, library):
        super(MediaDevices, self).__init__(spacing=6)
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self._register_instance()

        self.__cache = {}

        # Device list on the left pane
        swin = ScrolledWindow()
        swin.set_shadow_type(Gtk.ShadowType.IN)
        swin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        self.pack_start(swin, True, True, 0)

        self.__view = view = AllTreeView()
        view.set_model(self.__devices)
        view.set_rules_hint(True)
        view.set_headers_visible(False)
        view.get_selection().set_mode(Gtk.SelectionMode.BROWSE)
        connect_obj(view.get_selection(), 'changed', self.__refresh, False)
        view.connect('popup-menu', self.__popup_menu, library)
        view.connect('row-activated', lambda *a: self.songs_activated())
        swin.add(view)

        col = Gtk.TreeViewColumn("Devices")
        view.append_column(col)

        render = Gtk.CellRendererPixbuf()
        col.pack_start(render, False)
        col.add_attribute(render, 'icon-name', 1)

        self.__render = render = Gtk.CellRendererText()
        render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.connect('edited', self.__edited)
        col.pack_start(render, True)
        col.set_cell_data_func(render, MediaDevices.cell_data)

        hbox = Gtk.HBox(spacing=6)
        hbox.set_homogeneous(True)
        self.pack_start(Align(hbox, left=3, bottom=3), False, True, 0)

        # refresh button
        refresh = Button(_("_Refresh"), Icons.VIEW_REFRESH, Gtk.IconSize.MENU)
        self.__refresh_button = refresh
        connect_obj(refresh, 'clicked', self.__refresh, True)
        refresh.set_sensitive(False)
        hbox.pack_start(refresh, True, True, 0)

        # eject button
        eject = Button(_("_Eject"), Icons.MEDIA_EJECT, Gtk.IconSize.MENU)
        self.__eject_button = eject
        eject.connect('clicked', self.__eject)
        eject.set_sensitive(False)
        hbox.pack_start(eject, True, True, 0)

        # Device info on the right pane
        self.__header = table = Gtk.Table()
        table.set_col_spacings(8)

        self.__device_icon = icon = Gtk.Image()
        icon.set_size_request(48, 48)
        table.attach(icon, 0, 1, 0, 2, 0)

        self.__device_name = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0)
        table.attach(label, 1, 3, 0, 1)

        self.__device_space = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0.5)
        table.attach(label, 1, 2, 1, 2)

        self.__progress = progress = Gtk.ProgressBar()
        progress.set_size_request(150, -1)
        table.attach(progress, 2, 3, 1, 2, xoptions=0, yoptions=0)

        self.accelerators = Gtk.AccelGroup()
        key, mod = Gtk.accelerator_parse('F2')
        self.accelerators.connect(key, mod, 0, self.__rename)

        self.__statusbar = WaitLoadBar()

        for child in self.get_children():
            child.show_all()
コード例 #6
0
ファイル: media.py プロジェクト: tonydolan/quodlibet
class MediaDevices(Browser, util.InstanceTracker):
    name = _("Media Devices")
    accelerated_name = _("_Media Devices")
    keys = ["MediaDevices"]
    priority = 25
    uses_main_library = False
    replaygain_profiles = ['track']

    __devices = Gtk.ListStore(object, str)
    __busy = False
    __last = None

    @staticmethod
    def cell_data(col, render, model, iter, data):
        device = model[iter][0]
        if device.is_connected():
            render.markup = "<b>%s</b>" % util.escape(device['name'])
        else:
            render.markup = util.escape(device['name'])
        render.set_property('markup', render.markup)

    @classmethod
    def init(klass, library):
        devices.device_manager.connect('added', klass.__device_added)
        devices.device_manager.connect('removed', klass.__device_removed)
        devices.device_manager.discover()

    @classmethod
    def devices(klass):
        return [row[0] for row in klass.__devices]

    @classmethod
    def __device_added(klass, manager, device):
        klass.__devices.append(row=[device, device.icon])

    @classmethod
    def __device_removed(klass, manager, bid):
        for row in klass.__devices:
            if row[0].bid == bid:
                klass.__devices.remove(row.iter)
                break

    def __init__(self, library):
        super(MediaDevices, self).__init__(spacing=6)
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self._register_instance()

        self.__cache = {}

        # Device list on the left pane
        swin = ScrolledWindow()
        swin.set_shadow_type(Gtk.ShadowType.IN)
        swin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        self.pack_start(swin, True, True, 0)

        self.__view = view = AllTreeView()
        view.set_model(self.__devices)
        view.set_rules_hint(True)
        view.set_headers_visible(False)
        view.get_selection().set_mode(Gtk.SelectionMode.BROWSE)
        connect_obj(view.get_selection(), 'changed', self.__refresh, False)
        view.connect('popup-menu', self.__popup_menu, library)
        view.connect('row-activated', lambda *a: self.songs_activated())
        swin.add(view)

        col = Gtk.TreeViewColumn("Devices")
        view.append_column(col)

        render = Gtk.CellRendererPixbuf()
        col.pack_start(render, False)
        col.add_attribute(render, 'icon-name', 1)

        self.__render = render = Gtk.CellRendererText()
        render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.connect('edited', self.__edited)
        col.pack_start(render, True)
        col.set_cell_data_func(render, MediaDevices.cell_data)

        hbox = Gtk.HBox(spacing=6)
        hbox.set_homogeneous(True)
        self.pack_start(Align(hbox, left=3, bottom=3), False, True, 0)

        # refresh button
        refresh = Button(_("_Refresh"), Icons.VIEW_REFRESH, Gtk.IconSize.MENU)
        self.__refresh_button = refresh
        connect_obj(refresh, 'clicked', self.__refresh, True)
        refresh.set_sensitive(False)
        hbox.pack_start(refresh, True, True, 0)

        # eject button
        eject = Button(_("_Eject"), Icons.MEDIA_EJECT, Gtk.IconSize.MENU)
        self.__eject_button = eject
        eject.connect('clicked', self.__eject)
        eject.set_sensitive(False)
        hbox.pack_start(eject, True, True, 0)

        # Device info on the right pane
        self.__header = table = Gtk.Table()
        table.set_col_spacings(8)

        self.__device_icon = icon = Gtk.Image()
        icon.set_size_request(48, 48)
        table.attach(icon, 0, 1, 0, 2, 0)

        self.__device_name = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0)
        table.attach(label, 1, 3, 0, 1)

        self.__device_space = label = Gtk.Label()
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_alignment(0, 0.5)
        table.attach(label, 1, 2, 1, 2)

        self.__progress = progress = Gtk.ProgressBar()
        progress.set_size_request(150, -1)
        table.attach(progress, 2, 3, 1, 2, xoptions=0, yoptions=0)

        self.accelerators = Gtk.AccelGroup()
        key, mod = Gtk.accelerator_parse('F2')
        self.accelerators.connect(key, mod, 0, self.__rename)

        self.__statusbar = WaitLoadBar()

        for child in self.get_children():
            child.show_all()

    def pack(self, songpane):
        self.__vbox = vbox = Gtk.VBox(spacing=6)
        vbox.pack_start(self.__header, False, True, 0)
        vbox.pack_start(songpane, True, True, 0)
        vbox.pack_start(self.__statusbar, False, True, 0)

        vbox.show()
        self.__header.show_all()
        self.__header.hide()
        self.__statusbar.show_all()
        self.__statusbar.hide()
        self.show()

        self.__paned = paned = qltk.ConfigRHPaned("browsers",
                                                  "mediadevices_pos", 0.4)
        paned.pack1(self, True, False)
        paned.pack2(vbox, True, False)
        return paned

    def unpack(self, container, songpane):
        self.__vbox.remove(songpane)
        self.__paned.remove(self)

    def Menu(self, songs, library, items):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            device = model[iter][0]
            delete = device.delete and self.__delete_songs
        else:
            delete = False

        menu = SongsMenu(library,
                         songs,
                         delete=delete,
                         remove=False,
                         items=items)
        return menu

    def activate(self):
        self.__refresh()

    def save(self):
        selection = self.__view.get_selection()
        model, iter = selection.get_selected()
        if iter:
            config.set('browsers', 'media', model[iter][0]['name'])

    def restore(self):
        try:
            name = config.get('browsers', 'media')
        except config.Error:
            pass
        else:
            for row in self.__devices:
                if row[0]['name'] == name:
                    break
            else:
                return
            selection = self.__view.get_selection()
            selection.unselect_all()
            selection.select_iter(row.iter)

    def select(self, device):
        for row in self.__devices:
            if row[0] == device:
                break
        else:
            return

        # Force a full refresh
        try:
            del self.__cache[device.bid]
        except KeyError:
            pass

        selection = self.__view.get_selection()
        selection.unselect_all()
        selection.select_iter(row.iter)

    def active_filter(self, song):
        model, iter_ = self.__view.get_selection().get_selected()
        if iter_ is None:
            return False
        device = model[iter_][0]
        return device.contains(song)

    def dropped(self, songs):
        return self.__copy_songs(songs)

    def __popup_menu(self, view, library):
        model, iter = view.get_selection().get_selected()
        device = model[iter][0]

        if device.is_connected() and not self.__busy:
            songs = self.__list_songs(device)
        else:
            songs = []
        menu = SongsMenu(library,
                         songs,
                         playlists=False,
                         devices=False,
                         remove=False)

        menu.preseparate()

        props = MenuItem(_("_Properties"), Icons.DOCUMENT_PROPERTIES)
        connect_obj(props, 'activate', self.__properties, model[iter][0])
        props.set_sensitive(not self.__busy)
        menu.prepend(props)

        ren = qltk.MenuItem(_("_Rename"), Icons.EDIT)
        keyval, mod = Gtk.accelerator_parse("F2")
        ren.add_accelerator('activate', self.accelerators, keyval, mod,
                            Gtk.AccelFlags.VISIBLE)

        def rename(path):
            self.__render.set_property('editable', True)
            view.set_cursor(path, view.get_columns()[0], start_editing=True)

        connect_obj(ren, 'activate', rename, model.get_path(iter))
        menu.prepend(ren)

        menu.preseparate()

        eject = Gtk.ImageMenuItem(_("_Eject"), use_underline=True)
        eject.set_image(
            Gtk.Image.new_from_icon_name(Icons.MEDIA_EJECT, Gtk.IconSize.MENU))
        eject.set_sensitive(not self.__busy and device.eject
                            and device.is_connected())
        connect_obj(eject, 'activate', self.__eject, None)
        menu.prepend(eject)

        refresh = MenuItem(_("_Refresh"), Icons.VIEW_REFRESH)
        refresh.set_sensitive(device.is_connected())
        connect_obj(refresh, 'activate', self.__refresh, True)
        menu.prepend(refresh)

        menu.show_all()
        return view.popup_menu(menu, 0, Gtk.get_current_event_time())

    def __properties(self, device):
        DeviceProperties(self, device).run()
        self.__set_name(device)

    def __rename(self, group, acceleratable, keyval, modifier):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            self.__render.set_property('editable', True)
            self.__view.set_cursor(model.get_path(iter),
                                   self.__view.get_columns()[0],
                                   start_editing=True)

    def __edited(self, render, path, newname):
        self.__devices[path][0]['name'] = newname
        self.__set_name(self.__devices[path][0])
        render.set_property('editable', False)
        devices.write()

    def __set_name(self, device):
        self.__device_name.set_markup('<span size="x-large"><b>%s</b></span>' %
                                      util.escape(device['name']))

    def __refresh(self, rescan=False):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            path = model[iter].path
            if not rescan and self.__last == path:
                return
            self.__last = path

            device = model[iter][0]
            self.__device_icon.set_from_icon_name(device.icon,
                                                  Gtk.IconSize.DIALOG)
            self.__set_name(device)

            songs = []
            if device.is_connected():
                self.__header.show_all()
                self.__eject_button.set_sensitive(bool(device.eject))
                self.__refresh_button.set_sensitive(True)
                self.__refresh_space(device)

                try:
                    songs = self.__list_songs(device, rescan)
                except NotImplementedError:
                    pass
            else:
                self.__eject_button.set_sensitive(False)
                self.__refresh_button.set_sensitive(False)
                self.__header.hide()
            self.songs_selected(songs, device.ordered)
        else:
            self.__last = None
            self.songs_selected([], False)

    def __refresh_space(self, device):
        try:
            space, free = device.get_space()
        except NotImplementedError:
            self.__device_space.set_text("")
            self.__progress.hide()
        else:
            used = space - free
            fraction = float(used) / space

            self.__device_space.set_markup(
                _("%(used-size)s used, %(free-size)s available") % {
                    "used-size": util.bold(util.format_size(used)),
                    "free-size": util.bold(util.format_size(free))
                })
            self.__progress.set_fraction(fraction)
            self.__progress.set_text("%.f%%" % round(fraction * 100))
            self.__progress.show()

    def __list_songs(self, device, rescan=False):
        if rescan or not device.bid in self.__cache:
            self.__busy = True
            self.__cache[device.bid] = device.list(self.__statusbar)
            self.__busy = False
        return self.__cache[device.bid]

    def __check_device(self, device, message):
        if not device.is_connected():
            qltk.WarningMessage(
                self, message,
                _("%s is not connected.") %
                util.bold(util.escape(device['name']))).run()
            return False
        return True

    def __copy_songs(self, songs):
        model, iter = self.__view.get_selection().get_selected()
        if not iter:
            return False

        device = model[iter][0]
        if not self.__check_device(device, _("Unable to copy songs")):
            return False

        self.__busy = True

        wlb = self.__statusbar
        wlb.setup(len(songs),
                  _("Copying %(song)s") % {'song': '<b>%(song)s</b>'},
                  {'song': ''})
        wlb.show()

        for song in songs:
            label = util.escape(song('~artist~title'))
            if wlb.step(song=label):
                wlb.hide()
                break

            space, free = device.get_space()
            if free < os.path.getsize(song['~filename']):
                wlb.hide()
                qltk.WarningMessage(
                    self, _("Unable to copy song"),
                    _("There is not enough free space for this song.")).run()
                break

            status = device.copy(self, song)
            if isinstance(status, AudioFile):
                try:
                    self.__cache[device.bid].append(song)
                except KeyError:
                    pass
                self.__refresh_space(device)
            else:
                msg = _("%s could not be copied.") % util.bold(label)
                if type(status) == text_type:
                    msg += "\n\n" + util.escape(status)
                qltk.WarningMessage(self, _("Unable to copy song"), msg).run()

        if device.cleanup and not device.cleanup(wlb, 'copy'):
            pass
        else:
            wlb.hide()

        self.__busy = False
        return True

    def __delete_songs(self, songs):
        model, iter = self.__view.get_selection().get_selected()
        if not iter:
            return False

        device = model[iter][0]
        if not self.__check_device(device, _("Unable to delete songs")):
            return False

        dialog = DeleteDialog.for_songs(self, songs)
        if dialog.run() != DeleteDialog.RESPONSE_DELETE:
            return False

        self.__busy = True

        wlb = self.__statusbar
        wlb.setup(len(songs),
                  _("Deleting %(song)s") % {"song": "<b>%(song)s</b>"},
                  {'song': ''})
        wlb.show()

        for song in songs:
            label = util.escape(song('~artist~title'))
            if wlb.step(song=label):
                wlb.hide()
                break

            status = device.delete(self, song)
            if status is True:
                try:
                    self.__cache[device.bid].remove(song)
                except (KeyError, ValueError):
                    pass
                self.__refresh_space(device)
            else:
                msg = _("%s could not be deleted.") % util.bold(label)
                if type(status) == text_type:
                    msg += "\n\n%s" % status
                qltk.WarningMessage(self, _("Unable to delete song"),
                                    msg).run()

        if device.cleanup and not device.cleanup(wlb, 'delete'):
            pass
        else:
            wlb.hide()

        self.__busy = False

    def __eject(self, button):
        model, iter = self.__view.get_selection().get_selected()
        if iter:
            device = model[iter][0]
            status = device.eject()
            if status is not True:
                msg = _("Ejecting %s failed.") % util.bold(device['name'])
                if status:
                    msg += "\n\n%s" % status
                qltk.ErrorMessage(self, _("Unable to eject device"), msg).run()