Example #1
0
    def __init__(self, main):
        self.main = main
        self.VOLUME_STEP = 0.05

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)
        self.tooltip.set_display_progress(True)

        self.menu = menu.ProviderMenu('tray-icon-context', self)
        self.update_icon()
        self.connect_events()
        event.log_event('tray_icon_toggled', self, True)
Example #2
0
    def __init__(self):
        PlaylistButtonControl.__init__(self)

        self.set_name('progressbutton')
        self.add_events(gtk.gdk.POINTER_MOTION_MASK)

        self.progressbar = SeekProgressBar(player.PLAYER)
        self.progressbar.set_size_request(-1, 1)
        self.progressbar.formatter = ProgressButtonFormatter()
        gtk_widget_replace(self.label, self.progressbar)
        self.label = self.progressbar

        if player.PLAYER.current is not None:
            self.progressbar.on_playback_track_start('playback_track_start',
                                                     player.PLAYER,
                                                     player.PLAYER.current)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)
Example #3
0
    def __init__(self, main):
        self.main = main
        self.VOLUME_STEP = 0.05

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)
        self.tooltip.set_display_progress(True)

        self.menu = menu.ProviderMenu('tray-icon-context', self)
        self.update_icon()
        self.connect_events()
        event.log_event('tray_icon_toggled', self, True)
Example #4
0
    def __init__(self):
        PlaylistButtonControl.__init__(self)

        self.set_name('progressbutton')
        self.add_events(Gdk.EventMask.POINTER_MOTION_MASK)

        self.progressbar = SeekProgressBar(player.PLAYER)
        self.progressbar.set_size_request(-1, 1)
        self.progressbar.formatter = ProgressButtonFormatter()
        self.progressbar.set_text = lambda *a: None  # Needed by PlaylistButtonControl
        gtk_widget_replace(self.label, self.progressbar)
        self.label = self.progressbar

        if player.PLAYER.current is not None:
            self.progressbar.on_playback_track_start(
                'playback_track_start', player.PLAYER, player.PLAYER.current
            )

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)
Example #5
0
class BaseTrayIcon(object):
    """
        Trayicon base, needs to be derived from
    """
    def __init__(self, main):
        self.main = main
        self.VOLUME_STEP = 0.05

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)
        self.tooltip.set_display_progress(True)

        self.menu = menu.ProviderMenu('tray-icon-context', self)
        self.update_icon()
        self.connect_events()
        event.log_event('tray_icon_toggled', self, True)

    def destroy(self):
        """
            Unhides the window and removes the tray icon
        """
        # FIXME: Allow other windows too
        if not self.main.window.get_property('visible'):
            self.main.window.deiconify()
            self.main.window.present()

        self.disconnect_events()
        self.set_visible(False)
        self.tooltip.destroy()
        event.log_event('tray_icon_toggled', self, False)

    def connect_events(self):
        """
            Connects various callbacks with events
        """
        self.connect('button-press-event', self.on_button_press_event)
        self.connect('scroll-event', self.on_scroll_event)

        event.add_ui_callback(self.on_playback_change_state,
                              'playback_player_end', player.PLAYER)
        event.add_ui_callback(self.on_playback_change_state,
                              'playback_track_start', player.PLAYER)
        event.add_ui_callback(self.on_playback_change_state,
                              'playback_toggle_pause', player.PLAYER)
        event.add_ui_callback(self.on_playback_change_state, 'playback_error',
                              player.PLAYER)

    def disconnect_events(self):
        """
            Disconnects various callbacks from events
        """
        event.remove_callback(self.on_playback_change_state,
                              'playback_player_end', player.PLAYER)
        event.remove_callback(self.on_playback_change_state,
                              'playback_track_start', player.PLAYER)
        event.remove_callback(self.on_playback_change_state,
                              'playback_toggle_pause', player.PLAYER)
        event.remove_callback(self.on_playback_change_state, 'playback_error',
                              player.PLAYER)

    def update_icon(self):
        """
            Updates icon appearance based
            on current playback state
        """
        if player.PLAYER.current is None:
            self.set_from_icon_name('exaile')
            self.set_tooltip(_('Exaile Music Player'))
        elif player.PLAYER.is_paused():
            self.set_from_icon_name('exaile-pause')
        else:
            self.set_from_icon_name('exaile-play')

    def set_from_icon_name(self, icon_name):
        """
            Updates the tray icon
        """
        pass

    def set_tooltip(self, tooltip_text):
        """
            Updates the tray icon tooltip
        """
        pass

    def set_visible(self, visible):
        """
            Shows or hides the tray icon
        """
        pass

    def get_menu_position(self, menu, icon):
        """
            Returns coordinates for
            the best menu position
        """
        return (0, 0, False)

    def on_button_press_event(self, widget, event):
        """
            Toggles main window visibility and
            pause as well as opens the context menu
        """
        if event.button == Gdk.BUTTON_PRIMARY:
            self.main.toggle_visible(bringtofront=True)
        if event.button == Gdk.BUTTON_MIDDLE:
            playback.playpause(player.PLAYER)
        if event.triggers_context_menu():
            self.menu.popup(None, None, self.get_menu_position, self,
                            event.button, event.time)

    def on_scroll_event(self, widget, event):
        """
            Changes volume and skips tracks on scroll
        """
        if event.get_state() & Gdk.ModifierType.SHIFT_MASK:
            if event.direction == Gdk.ScrollDirection.UP:
                player.QUEUE.prev()
            elif event.direction == Gdk.ScrollDirection.DOWN:
                player.QUEUE.next()
        else:
            if event.direction == Gdk.ScrollDirection.UP:
                volume = settings.get_option('player/volume', 1)
                settings.set_option('player/volume',
                                    min(volume + self.VOLUME_STEP, 1))
                return True
            elif event.direction == Gdk.ScrollDirection.DOWN:
                volume = settings.get_option('player/volume', 1)
                settings.set_option('player/volume',
                                    max(0, volume - self.VOLUME_STEP))
                return True
            elif event.direction == Gdk.ScrollDirection.LEFT:
                player.QUEUE.prev()
            elif event.direction == Gdk.ScrollDirection.RIGHT:
                player.QUEUE.next()

    def on_playback_change_state(self, event, player, current):
        """
            Updates tray icon appearance
            on playback state change
        """
        self.update_icon()
Example #6
0
    def __init__(self):
        Gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = Gtk.Box()
        self.arrow = Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.OUT)
        box.pack_start(self.arrow, False, True, 0)
        self.label = Gtk.Label(label='')
        self.label.props.ellipsize = Pango.EllipsizeMode.END
        box.pack_start(self.label, True, True, 0)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option('plugin/minimode/track_title_format',
                                '$tracknumber - $title'))

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_width', 350),
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_height', 400))
        scrollwindow = Gtk.ScrolledWindow()
        scrollwindow.set_policy(Gtk.PolicyType.AUTOMATIC,
                                Gtk.PolicyType.AUTOMATIC)
        scrollwindow.set_shadow_type(Gtk.ShadowType.IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('show', self.on_popup_show)
        self.popup.connect('hide', self.on_popup_hide)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = Gtk.AccelGroup()
        key, modifier = Gtk.accelerator_parse('<Control>J')
        accel_group.connect(key, modifier, Gtk.AccelFlags.VISIBLE,
                            self.on_accelerator_activate)
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None

        self.drag_dest_set(
            Gtk.DestDefaults.ALL, self.view.targets,
            Gdk.DragAction.COPY | Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_ui_callback(self.on_track_tags_changed, 'track_tags_changed')
        event.add_ui_callback(self.on_option_set, 'plugin_minimode_option_set')
        self.on_option_set('plugin_minimode_option_set', settings,
                           'plugin/minimode/track_title_format')
Example #7
0
class PlaylistButtonControl(Gtk.ToggleButton, BaseControl, QueueAdapter):
    name = 'playlist_button'
    title = _('Playlist button')
    description = _('Access the current playlist')
    __gsignals__ = {'scroll-event': 'override'}

    def __init__(self):
        Gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = Gtk.Box()
        self.arrow = Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.OUT)
        box.pack_start(self.arrow, False, True, 0)
        self.label = Gtk.Label(label='')
        self.label.props.ellipsize = Pango.EllipsizeMode.END
        box.pack_start(self.label, True, True, 0)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option('plugin/minimode/track_title_format',
                                '$tracknumber - $title'))

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_width', 350),
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_height', 400))
        scrollwindow = Gtk.ScrolledWindow()
        scrollwindow.set_policy(Gtk.PolicyType.AUTOMATIC,
                                Gtk.PolicyType.AUTOMATIC)
        scrollwindow.set_shadow_type(Gtk.ShadowType.IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('show', self.on_popup_show)
        self.popup.connect('hide', self.on_popup_hide)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = Gtk.AccelGroup()
        key, modifier = Gtk.accelerator_parse('<Control>J')
        accel_group.connect(key, modifier, Gtk.AccelFlags.VISIBLE,
                            self.on_accelerator_activate)
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None

        self.drag_dest_set(
            Gtk.DestDefaults.ALL, self.view.targets,
            Gdk.DragAction.COPY | Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_ui_callback(self.on_track_tags_changed, 'track_tags_changed')
        event.add_ui_callback(self.on_option_set, 'plugin_minimode_option_set')
        self.on_option_set('plugin_minimode_option_set', settings,
                           'plugin/minimode/track_title_format')

    def destroy(self):
        """
            Cleanups
        """
        self.tooltip.destroy()
        QueueAdapter.destroy(self)
        Gtk.ToggleButton.destroy(self)

    def update_playlist(self, playlist):
        """
            Updates the internally stored playlist
        """
        columns = self.view.get_model().columns
        model = PlaylistModel(playlist, columns, player.PLAYER)
        self.view.set_model(model)

    def do_scroll_event(self, event):
        """
            Changes the current track
        """
        if event.direction == Gdk.ScrollDirection.UP:
            self.view.playlist.prev()
        elif event.direction == Gdk.ScrollDirection.DOWN:
            self.view.playlist.next()
        else:
            return

        position = self.view.playlist.current_position

        try:
            track = self.view.playlist[position]
        except IndexError:
            pass
        else:
            player.QUEUE.play(track)

    def do_toggled(self):
        """
            Shows or hides the playlist
        """
        if self.get_active():
            self.arrow.props.arrow_type = Gtk.ArrowType.DOWN
            self.popup.show_all()
        else:
            self.popup.hide()
            self.arrow.props.arrow_type = Gtk.ArrowType.RIGHT

    def on_accelerator_activate(self, accel_group, acceleratable, keyval,
                                modifier):
        """
            Shows the current track
        """
        self.view.scroll_to_cell(self.view.playlist.current_position)
        self.view.set_cursor(self.view.playlist.current_position)

    def on_drag_motion(self, widget, context, x, y, time):
        """
            Prepares to show the playlist
        """
        # Defer display of the playlist
        if self._drag_motion_timeout_id is None:
            self._drag_motion_timeout_id = GLib.timeout_add(
                500, lambda: self.set_active(True))

        # Prevent hiding of the playlist
        if self._drag_leave_timeout_id is not None:
            GLib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

    def on_drag_leave(self, widget, context, time):
        """
            Prepares to hide the playlist
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            GLib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        if self._drag_leave_timeout_id is not None:
            GLib.source_remove(self._drag_leave_timeout_id)

        # Defer hiding of the playlist
        self._drag_leave_timeout_id = GLib.timeout_add(
            500, lambda: self.set_active(False))

    def on_drag_data_received(self, widget, context, x, y, selection, info,
                              time):
        """
            Handles dropped data
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            GLib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        # Enable hiding of the playlist on re-enter
        if self._drag_leave_timeout_id is not None:
            GLib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

        self.view.emit('drag-data-received', context, x, y, selection, info,
                       time)

    def on_popup_show(self, widget):
        if not self.get_active():
            self.set_active(True)

    def on_popup_hide(self, widget):
        if self.get_active():
            self.set_active(False)

    def on_popup_configure_event(self, widget, event):
        """
            Saves the window size after resizing
        """
        width = settings.get_option(
            'plugin/minimode/'
            'playlist_button_popup_width', 350)
        height = settings.get_option(
            'plugin/minimode/'
            'playlist_button_popup_height', 400)

        if event.width != width:
            settings.set_option(
                'plugin/minimode/'
                'playlist_button_popup_width', event.width)

        if event.height != height:
            settings.set_option(
                'plugin/minimode/'
                'playlist_button_popup_height', event.height)

    def on_queue_current_playlist_changed(self, event, queue, playlist):
        """
            Updates the list on queue changes
        """
        GLib.idle_add(self.update_playlist, playlist)

    def on_queue_current_position_changed(self, event, playlist, positions):
        """
            Updates the list on queue changes
        """
        try:
            track = playlist[positions[0]]
        except IndexError:
            text = ''
        else:
            text = self.formatter.format(track)

        GLib.idle_add(self.label.set_text, text)

    def on_track_tags_changed(self, event, track, tag):
        """
            Updates the button on tag changes
        """
        playlist = self.view.playlist
        track_position = playlist.index(track)

        if track in playlist and track_position == playlist.current_position:
            self.label.set_text(self.formatter.format(track))

    def on_option_set(self, event, settings, option):
        """
            Updates control upon setting change
        """
        if option == 'plugin/minimode/track_title_format':
            self.formatter.set_property(
                'format',
                settings.get_option(option, _('$tracknumber - $title')))
Example #8
0
class ProgressButtonControl(PlaylistButtonControl):
    name = 'progress_button'
    title = _('Progress button')
    description = _('Playback progress and access ' 'to the current playlist')
    # Required to make overrides work
    __gsignals__ = {}

    def __init__(self):
        PlaylistButtonControl.__init__(self)

        self.set_name('progressbutton')
        self.add_events(Gdk.EventMask.POINTER_MOTION_MASK)

        self.progressbar = SeekProgressBar(player.PLAYER)
        self.progressbar.set_size_request(-1, 1)
        self.progressbar.formatter = ProgressButtonFormatter()
        self.progressbar.set_text = lambda *a: None  # Needed by PlaylistButtonControl
        gtk_widget_replace(self.label, self.progressbar)
        self.label = self.progressbar

        if player.PLAYER.current is not None:
            self.progressbar.on_playback_track_start('playback_track_start',
                                                     player.PLAYER,
                                                     player.PLAYER.current)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

    def destroy(self):
        """
            Cleanups
        """
        self.tooltip.destroy()
        PlaylistButtonControl.destroy(self)

    def do_button_press_event(self, event):
        """
            Trigger normal toggle action or seek
        """
        if event.button == 1:
            PlaylistButtonControl.do_button_press_event(self, event)
        elif event.button == 2:
            event = event.copy()
            event.button = 1
            x, y = self.translate_coordinates(self.progressbar, int(event.x),
                                              int(event.y))
            event.x, event.y = float(x), float(y)
            self.progressbar.emit('button-press-event', event)

    def do_button_release_event(self, event):
        if event.button == 1:
            PlaylistButtonControl.do_button_release_event(self, event)
        elif event.button == 2:
            event = event.copy()
            event.button = 1
            x, y = self.translate_coordinates(self.progressbar, int(event.x),
                                              int(event.y))
            event.x, event.y = float(x), float(y)
            self.progressbar.emit('button-release-event', event)

    def do_motion_notify_event(self, event):
        event = event.copy()
        x, y = self.translate_coordinates(self.progressbar, int(event.x),
                                          int(event.y))
        event.x, event.y = float(x), float(y)
        self.progressbar.emit('motion-notify-event', event)

    def do_leave_notify_event(self, event):
        event = event.copy()
        x, y = self.translate_coordinates(self.progressbar, int(event.x),
                                          int(event.y))
        event.x, event.y = float(x), float(y)
        self.progressbar.emit('leave-notify-event', event)
Example #9
0
    def __init__(self):
        Gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = Gtk.Box()
        self.arrow = Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.OUT)
        box.pack_start(self.arrow, False, True, 0)
        self.label = Gtk.Label(label='')
        self.label.props.ellipsize = Pango.EllipsizeMode.END
        box.pack_start(self.label, True, True, 0)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option(
                'plugin/minimode/track_title_format', '$tracknumber - $title'
            )
        )

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option('plugin/minimode/' 'playlist_button_popup_width', 350),
            settings.get_option('plugin/minimode/' 'playlist_button_popup_height', 400),
        )
        scrollwindow = Gtk.ScrolledWindow()
        scrollwindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scrollwindow.set_shadow_type(Gtk.ShadowType.IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('show', self.on_popup_show)
        self.popup.connect('hide', self.on_popup_hide)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = Gtk.AccelGroup()
        key, modifier = Gtk.accelerator_parse('<Primary>J')
        accel_group.connect(
            key, modifier, Gtk.AccelFlags.VISIBLE, self.on_accelerator_activate
        )
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None

        self.drag_dest_set(
            Gtk.DestDefaults.ALL,
            self.view.targets,
            Gdk.DragAction.COPY | Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE,
        )

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_ui_callback(self.on_track_tags_changed, 'track_tags_changed')
        event.add_ui_callback(self.on_option_set, 'plugin_minimode_option_set')
        self.on_option_set(
            'plugin_minimode_option_set', settings, 'plugin/minimode/track_title_format'
        )
Example #10
0
class PlaylistButtonControl(Gtk.ToggleButton, BaseControl, QueueAdapter):
    name = 'playlist_button'
    title = _('Playlist button')
    description = _('Access the current playlist')
    __gsignals__ = {'scroll-event': 'override'}

    def __init__(self):
        Gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = Gtk.Box()
        self.arrow = Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.OUT)
        box.pack_start(self.arrow, False, True, 0)
        self.label = Gtk.Label(label='')
        self.label.props.ellipsize = Pango.EllipsizeMode.END
        box.pack_start(self.label, True, True, 0)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option(
                'plugin/minimode/track_title_format', '$tracknumber - $title'
            )
        )

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option('plugin/minimode/' 'playlist_button_popup_width', 350),
            settings.get_option('plugin/minimode/' 'playlist_button_popup_height', 400),
        )
        scrollwindow = Gtk.ScrolledWindow()
        scrollwindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scrollwindow.set_shadow_type(Gtk.ShadowType.IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('show', self.on_popup_show)
        self.popup.connect('hide', self.on_popup_hide)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = Gtk.AccelGroup()
        key, modifier = Gtk.accelerator_parse('<Primary>J')
        accel_group.connect(
            key, modifier, Gtk.AccelFlags.VISIBLE, self.on_accelerator_activate
        )
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None

        self.drag_dest_set(
            Gtk.DestDefaults.ALL,
            self.view.targets,
            Gdk.DragAction.COPY | Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE,
        )

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_ui_callback(self.on_track_tags_changed, 'track_tags_changed')
        event.add_ui_callback(self.on_option_set, 'plugin_minimode_option_set')
        self.on_option_set(
            'plugin_minimode_option_set', settings, 'plugin/minimode/track_title_format'
        )

    def destroy(self):
        """
            Cleanups
        """
        self.tooltip.destroy()
        QueueAdapter.destroy(self)
        Gtk.ToggleButton.destroy(self)

    def update_playlist(self, playlist):
        """
            Updates the internally stored playlist
        """
        columns = self.view.model.column_names
        model = PlaylistModel(playlist, columns, player.PLAYER, self.view)
        self.view.set_model(model)

    def do_scroll_event(self, event):
        """
            Changes the current track
        """
        if event.direction == Gdk.ScrollDirection.UP:
            self.view.playlist.prev()
        elif event.direction == Gdk.ScrollDirection.DOWN:
            self.view.playlist.next()
        else:
            return

        position = self.view.playlist.current_position

        try:
            track = self.view.playlist[position]
        except IndexError:
            pass
        else:
            player.QUEUE.play(track)

    def do_toggled(self):
        """
            Shows or hides the playlist
        """
        if self.get_active():
            self.arrow.props.arrow_type = Gtk.ArrowType.DOWN
            self.popup.show_all()
        else:
            self.popup.hide()
            self.arrow.props.arrow_type = Gtk.ArrowType.RIGHT

    def on_accelerator_activate(self, accel_group, acceleratable, keyval, modifier):
        """
            Shows the current track
        """
        self.view.scroll_to_cell(self.view.playlist.current_position)
        self.view.set_cursor(self.view.playlist.current_position)

    def on_drag_motion(self, widget, context, x, y, time):
        """
            Prepares to show the playlist
        """
        # Defer display of the playlist
        if self._drag_motion_timeout_id is None:
            self._drag_motion_timeout_id = GLib.timeout_add(
                500, lambda: self.set_active(True)
            )

        # Prevent hiding of the playlist
        if self._drag_leave_timeout_id is not None:
            GLib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

    def on_drag_leave(self, widget, context, time):
        """
            Prepares to hide the playlist
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            GLib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        if self._drag_leave_timeout_id is not None:
            GLib.source_remove(self._drag_leave_timeout_id)

        # Defer hiding of the playlist
        self._drag_leave_timeout_id = GLib.timeout_add(
            500, lambda: self.set_active(False)
        )

    def on_drag_data_received(self, widget, context, x, y, selection, info, time):
        """
            Handles dropped data
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            GLib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        # Enable hiding of the playlist on re-enter
        if self._drag_leave_timeout_id is not None:
            GLib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

        self.view.emit('drag-data-received', context, x, y, selection, info, time)

    def on_popup_show(self, widget):
        if not self.get_active():
            self.set_active(True)

    def on_popup_hide(self, widget):
        if self.get_active():
            self.set_active(False)

    def on_popup_configure_event(self, widget, event):
        """
            Saves the window size after resizing
        """
        width = settings.get_option(
            'plugin/minimode/' 'playlist_button_popup_width', 350
        )
        height = settings.get_option(
            'plugin/minimode/' 'playlist_button_popup_height', 400
        )

        if event.width != width:
            settings.set_option(
                'plugin/minimode/' 'playlist_button_popup_width', event.width
            )

        if event.height != height:
            settings.set_option(
                'plugin/minimode/' 'playlist_button_popup_height', event.height
            )

    def on_queue_current_playlist_changed(self, event, queue, playlist):
        """
            Updates the list on queue changes
        """
        GLib.idle_add(self.update_playlist, playlist)

    def on_queue_current_position_changed(self, event, playlist, positions):
        """
            Updates the list on queue changes
        """
        try:
            track = playlist[positions[0]]
        except IndexError:
            text = ''
        else:
            text = self.formatter.format(track)

        GLib.idle_add(self.label.set_text, text)

    def on_track_tags_changed(self, event, track, tags):
        """
            Updates the button on tag changes
        """
        playlist = self.view.playlist

        if track not in playlist:
            return

        track_position = playlist.index(track)
        if track_position == playlist.current_position:
            self.label.set_text(self.formatter.format(track))

    def on_option_set(self, event, settings, option):
        """
            Updates control upon setting change
        """
        if option == 'plugin/minimode/track_title_format':
            self.formatter.set_property(
                'format', settings.get_option(option, _('$tracknumber - $title'))
            )
Example #11
0
class ProgressButtonControl(PlaylistButtonControl):
    name = 'progress_button'
    title = _('Progress button')
    description = _('Playback progress and access ' 'to the current playlist')
    # Required to make overrides work
    __gsignals__ = {}

    def __init__(self):
        PlaylistButtonControl.__init__(self)

        self.set_name('progressbutton')
        self.add_events(Gdk.EventMask.POINTER_MOTION_MASK)

        self.progressbar = SeekProgressBar(player.PLAYER)
        self.progressbar.set_size_request(-1, 1)
        self.progressbar.formatter = ProgressButtonFormatter()
        self.progressbar.set_text = lambda *a: None  # Needed by PlaylistButtonControl
        gtk_widget_replace(self.label, self.progressbar)
        self.label = self.progressbar

        if player.PLAYER.current is not None:
            self.progressbar.on_playback_track_start(
                'playback_track_start', player.PLAYER, player.PLAYER.current
            )

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

    def destroy(self):
        """
            Cleanups
        """
        self.tooltip.destroy()
        PlaylistButtonControl.destroy(self)

    def do_button_press_event(self, event):
        """
            Trigger normal toggle action or seek
        """
        if event.button == Gdk.BUTTON_PRIMARY:
            PlaylistButtonControl.do_button_press_event(self, event)
        elif event.button == Gdk.BUTTON_MIDDLE:
            event = event.copy()
            event.button = Gdk.BUTTON_PRIMARY
            x, y = self.translate_coordinates(
                self.progressbar, int(event.x), int(event.y)
            )
            event.x, event.y = float(x), float(y)
            self.progressbar.emit('button-press-event', event)

    def do_button_release_event(self, event):
        if event.button == Gdk.BUTTON_PRIMARY:
            PlaylistButtonControl.do_button_release_event(self, event)
        elif event.button == Gdk.BUTTON_MIDDLE:
            event = event.copy()
            event.button = Gdk.BUTTON_PRIMARY
            x, y = self.translate_coordinates(
                self.progressbar, int(event.x), int(event.y)
            )
            event.x, event.y = float(x), float(y)
            self.progressbar.emit('button-release-event', event)

    def do_motion_notify_event(self, event):
        event = event.copy()
        x, y = self.translate_coordinates(self.progressbar, int(event.x), int(event.y))
        event.x, event.y = float(x), float(y)
        self.progressbar.emit('motion-notify-event', event)

    def do_leave_notify_event(self, event):
        event = event.copy()
        x, y = self.translate_coordinates(self.progressbar, int(event.x), int(event.y))
        event.x, event.y = float(x), float(y)
        self.progressbar.emit('leave-notify-event', event)
Example #12
0
    def __init__(self):
        gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = gtk.HBox()
        self.arrow = gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_OUT)
        box.pack_start(self.arrow, expand=False)
        self.label = gtk.Label('')
        self.label.props.ellipsize = pango.ELLIPSIZE_END
        box.pack_start(self.label)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option('plugin/minimode/track_title_format',
                                '$tracknumber - $title'))

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option('plugin/minimode/'
                'playlist_button_popup_width', 350),
            settings.get_option('plugin/minimode/'
                'playlist_button_popup_height', 400)
        )
        scrollwindow = gtk.ScrolledWindow()
        scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrollwindow.set_shadow_type(gtk.SHADOW_IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = gtk.AccelGroup()
        key, modifier = gtk.accelerator_parse('<Control>J')
        accel_group.connect_group(key, modifier, gtk.ACCEL_VISIBLE,
            self.on_accelerator_activate)
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None
        self._toplevel_hide_id = None
        self._toplevel_window_state_event_id = None

        self.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.view.targets,
            gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_DEFAULT |
            gtk.gdk.ACTION_MOVE)

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_callback(self.on_track_tags_changed,
            'track_tags_changed')
        event.add_callback(self.on_option_set,
            'plugin_minimode_option_set')
        self.on_option_set('plugin_minimode_option_set', settings,
            'plugin/minimode/track_title_format')
Example #13
0
class PlaylistButtonControl(gtk.ToggleButton, BaseControl, QueueAdapter):
    name = 'playlist_button'
    title = _('Playlist button')
    description = _('Access the current playlist')
    __gsignals__ = {'scroll-event': 'override'}
    
    def __init__(self):
        gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = gtk.HBox()
        self.arrow = gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_OUT)
        box.pack_start(self.arrow, expand=False)
        self.label = gtk.Label('')
        self.label.props.ellipsize = pango.ELLIPSIZE_END
        box.pack_start(self.label)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option('plugin/minimode/track_title_format',
                                '$tracknumber - $title'))

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option('plugin/minimode/'
                'playlist_button_popup_width', 350),
            settings.get_option('plugin/minimode/'
                'playlist_button_popup_height', 400)
        )
        scrollwindow = gtk.ScrolledWindow()
        scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrollwindow.set_shadow_type(gtk.SHADOW_IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = gtk.AccelGroup()
        key, modifier = gtk.accelerator_parse('<Control>J')
        accel_group.connect_group(key, modifier, gtk.ACCEL_VISIBLE,
            self.on_accelerator_activate)
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None
        self._toplevel_hide_id = None
        self._toplevel_window_state_event_id = None

        self.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.view.targets,
            gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_DEFAULT |
            gtk.gdk.ACTION_MOVE)

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_callback(self.on_track_tags_changed,
            'track_tags_changed')
        event.add_callback(self.on_option_set,
            'plugin_minimode_option_set')
        self.on_option_set('plugin_minimode_option_set', settings,
            'plugin/minimode/track_title_format')

    def destroy(self):
        """
            Cleanups
        """
        self.tooltip.destroy()
        QueueAdapter.destroy(self)
        gtk.ToggleButton.destroy(self)

    def update_playlist(self, playlist):
        """
            Updates the internally stored playlist
        """
        columns = self.view.get_model().columns
        model = PlaylistModel(playlist, columns, player.PLAYER)
        self.view.set_model(model)

    def do_hierarchy_changed(self, previous_toplevel):
        """
            Sets up automatic hiding on parent hide
        """
        if self._toplevel_hide_id is not None:
            previous_toplevel.disconnect(
                self._toplevel_hide_id)
            previous_toplevel.disconnect(
                self._toplevel_window_state_event_id)

        toplevel = self.get_toplevel()

        if isinstance(toplevel, gtk.Window):
            self._toplevel_hide_id = toplevel.connect(
                'hide', self.on_toplevel_hide)
            self._toplevel_window_state_event_id = toplevel.connect(
                'window-state-event', self.on_toplevel_window_state_event)
            self.popup.set_transient_for(toplevel)

    def do_scroll_event(self, event):
        """
            Changes the current track
        """
        if event.direction == gtk.gdk.SCROLL_UP:
            self.view.playlist.prev()
        elif event.direction == gtk.gdk.SCROLL_DOWN:
            self.view.playlist.next()
        else:
            return

        position = self.view.playlist.current_position

        try:
            track = self.view.playlist[position]
        except IndexError:
            pass
        else:
            player.QUEUE.play(track)

    def do_toggled(self):
        """
            Shows or hides the playlist
        """
        if self.get_active():
            self.arrow.props.arrow_type = gtk.ARROW_DOWN
            self.popup.show_all()
        else:
            self.popup.hide()
            self.arrow.props.arrow_type = gtk.ARROW_RIGHT

    def on_accelerator_activate(self, accel_group, acceleratable,
                                keyval, modifier):
        """
            Shows the current track
        """
        self.view.scroll_to_cell(self.view.playlist.current_position)
        self.view.set_cursor(self.view.playlist.current_position)

    def on_drag_motion(self, widget, context, x, y, time):
        """
            Prepares to show the playlist
        """
        # Defer display of the playlist
        if self._drag_motion_timeout_id is None:
            self._drag_motion_timeout_id = glib.timeout_add(
                500, lambda: self.set_active(True))

        # Prevent hiding of the playlist
        if self._drag_leave_timeout_id is not None:
            glib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

    def on_drag_leave(self, widget, context, time):
        """
            Prepares to hide the playlist
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            glib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        if self._drag_leave_timeout_id is not None:
            glib.source_remove(self._drag_leave_timeout_id)

        # Defer hiding of the playlist
        self._drag_leave_timeout_id = glib.timeout_add(
            500, lambda: self.set_active(False))

    def on_drag_data_received(self, widget, context, x, y,
                              selection, info, time):
        """
            Handles dropped data
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            glib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        # Enable hiding of the playlist on re-enter
        if self._drag_leave_timeout_id is not None:
            glib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

        self.view.emit('drag-data-received', context, x, y,
            selection, info, time)

    def on_toplevel_hide(self, widget):
        """
            Hides the playlist
        """
        self.set_active(False)

    def on_toplevel_window_state_event(self, widget, event):
        """
            Hides the playlist
        """
        self.set_active(False)

    def on_popup_configure_event(self, widget, event):
        """
            Saves the window size after resizing
        """
        width = settings.get_option('plugin/minimode/'
            'playlist_button_popup_width', 350)
        height = settings.get_option('plugin/minimode/'
            'playlist_button_popup_height', 400)

        if event.width != width:
            settings.set_option('plugin/minimode/'
                'playlist_button_popup_width', event.width)

        if event.height != height:
            settings.set_option('plugin/minimode/'
                'playlist_button_popup_height', event.height)

    def on_queue_current_playlist_changed(self, event, queue, playlist):
        """
            Updates the list on queue changes
        """
        glib.idle_add(self.update_playlist, playlist)

    def on_queue_current_position_changed(self, event, playlist, positions):
        """
            Updates the list on queue changes
        """
        try:
            track = playlist[positions[0]]
        except IndexError:
            text = ''
        else:
            text = self.formatter.format(track)

        glib.idle_add(self.label.set_text, text)

    def on_track_tags_changed(self, event, track, tag):
        """
            Updates the button on tag changes
        """
        playlist = self.view.playlist
        track_position = playlist.index(track)

        if track in playlist and track_position == playlist.current_position:
            glib.idle_add(self.label.set_text, self.formatter.format(track))

    def on_option_set(self, event, settings, option):
        """
            Updates control upon setting change
        """
        if option == 'plugin/minimode/track_title_format':
            glib.idle_add(self.formatter.set_property,
                'format',
                settings.get_option(option, _('$tracknumber - $title'))
            )
Example #14
0
class BaseTrayIcon(object):
    """
        Trayicon base, needs to be derived from
    """
    def __init__(self, main):
        self.main = main
        self.VOLUME_STEP = 0.05

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)
        self.tooltip.set_display_progress(True)

        self.menu = menu.ProviderMenu('tray-icon-context', self)
        self.update_icon()
        self.connect_events()
        event.log_event('tray_icon_toggled', self, True)

    def destroy(self):
        """
            Unhides the window and removes the tray icon
        """
        # FIXME: Allow other windows too
        if not self.main.window.get_property('visible'):
            self.main.window.deiconify()
            self.main.window.present()

        self.disconnect_events()
        self.set_visible(False)
        self.tooltip.destroy()
        event.log_event('tray_icon_toggled', self, False)

    def connect_events(self):
        """
            Connects various callbacks with events
        """
        self.connect('button-press-event', self.on_button_press_event)
        self.connect('scroll-event', self.on_scroll_event)

        event.add_ui_callback(self.on_playback_change_state, 'playback_player_end', player.PLAYER)
        event.add_ui_callback(self.on_playback_change_state, 'playback_track_start', player.PLAYER)
        event.add_ui_callback(self.on_playback_change_state, 'playback_toggle_pause', player.PLAYER)
        event.add_ui_callback(self.on_playback_change_state, 'playback_error', player.PLAYER)

    def disconnect_events(self):
        """
            Disconnects various callbacks from events
        """
        event.remove_callback(self.on_playback_change_state, 'playback_player_end', player.PLAYER)
        event.remove_callback(self.on_playback_change_state, 'playback_track_start', player.PLAYER)
        event.remove_callback(self.on_playback_change_state, 'playback_toggle_pause', player.PLAYER)
        event.remove_callback(self.on_playback_change_state, 'playback_error', player.PLAYER)

    def update_icon(self):
        """
            Updates icon appearance based
            on current playback state
        """
        if player.PLAYER.current is None:
            self.set_from_icon_name('exaile')
            self.set_tooltip(_('Exaile Music Player'))
        elif player.PLAYER.is_paused():
            self.set_from_icon_name('exaile-pause')
        else:
            self.set_from_icon_name('exaile-play')

    def set_from_icon_name(self, icon_name):
        """
            Updates the tray icon
        """
        pass

    def set_tooltip(self, tooltip_text):
        """
            Updates the tray icon tooltip
        """
        pass

    def set_visible(self, visible):
        """
            Shows or hides the tray icon
        """
        pass

    def get_menu_position(self, menu, icon):
        """
            Returns coordinates for
            the best menu position
        """
        return (0, 0, False)

    def on_button_press_event(self, widget, event):
        """
            Toggles main window visibility and
            pause as well as opens the context menu
        """
        if event.button == 1:
            self.main.toggle_visible(bringtofront=True)
        if event.button == 2:
            playback.playpause( player.PLAYER )
        if event.button == 3:
            self.menu.popup(None, None, self.get_menu_position, self,
                event.button, event.time)

    def on_scroll_event(self, widget, event):
        """
            Changes volume and skips tracks on scroll
        """
        if event.get_state() & Gdk.ModifierType.SHIFT_MASK:
            if event.direction == Gdk.ScrollDirection.UP:
                player.QUEUE.prev()
            elif event.direction == Gdk.ScrollDirection.DOWN:
                player.QUEUE.next()
        else:
            if event.direction == Gdk.ScrollDirection.UP:
                volume = settings.get_option('player/volume', 1)
                settings.set_option('player/volume', min(volume + self.VOLUME_STEP, 1))
                return True
            elif event.direction == Gdk.ScrollDirection.DOWN:
                volume = settings.get_option('player/volume', 1)
                settings.set_option('player/volume', max(0, volume - self.VOLUME_STEP))
                return True
            elif event.direction == Gdk.ScrollDirection.LEFT:
                player.QUEUE.prev()
            elif event.direction == Gdk.ScrollDirection.RIGHT:
                player.QUEUE.next()

    def on_playback_change_state(self, event, player, current):
        """
            Updates tray icon appearance
            on playback state change
        """
        self.update_icon()
Example #15
0
    def __init__(self):
        gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = gtk.HBox()
        self.arrow = gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_OUT)
        box.pack_start(self.arrow, expand=False)
        self.label = gtk.Label('')
        self.label.props.ellipsize = pango.ELLIPSIZE_END
        box.pack_start(self.label)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option('plugin/minimode/track_title_format',
                                '$tracknumber - $title'))

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_width', 350),
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_height', 400))
        scrollwindow = gtk.ScrolledWindow()
        scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrollwindow.set_shadow_type(gtk.SHADOW_IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = gtk.AccelGroup()
        key, modifier = gtk.accelerator_parse('<Control>J')
        accel_group.connect_group(key, modifier, gtk.ACCEL_VISIBLE,
                                  self.on_accelerator_activate)
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None
        self._toplevel_hide_id = None
        self._toplevel_window_state_event_id = None

        self.drag_dest_set(
            gtk.DEST_DEFAULT_ALL, self.view.targets,
            gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE)

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_callback(self.on_track_tags_changed, 'track_tags_changed')
        event.add_callback(self.on_option_set, 'plugin_minimode_option_set')
        self.on_option_set('plugin_minimode_option_set', settings,
                           'plugin/minimode/track_title_format')
Example #16
0
class PlaylistButtonControl(gtk.ToggleButton, BaseControl, QueueAdapter):
    name = 'playlist_button'
    title = _('Playlist button')
    description = _('Access the current playlist')
    __gsignals__ = {'scroll-event': 'override'}

    def __init__(self):
        gtk.ToggleButton.__init__(self)
        BaseControl.__init__(self)
        QueueAdapter.__init__(self, player.QUEUE)

        self.set_focus_on_click(False)
        self.set_size_request(200, -1)
        box = gtk.HBox()
        self.arrow = gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_OUT)
        box.pack_start(self.arrow, expand=False)
        self.label = gtk.Label('')
        self.label.props.ellipsize = pango.ELLIPSIZE_END
        box.pack_start(self.label)
        self.add(box)

        self.formatter = TrackFormatter(
            settings.get_option('plugin/minimode/track_title_format',
                                '$tracknumber - $title'))

        self.view = PlaylistView(player.QUEUE.current_playlist, player.PLAYER)
        self.popup = AttachedWindow(self)
        self.popup.set_default_size(
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_width', 350),
            settings.get_option(
                'plugin/minimode/'
                'playlist_button_popup_height', 400))
        scrollwindow = gtk.ScrolledWindow()
        scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrollwindow.set_shadow_type(gtk.SHADOW_IN)
        scrollwindow.add(self.view)
        self.popup.add(scrollwindow)
        self.popup.connect('configure-event', self.on_popup_configure_event)

        accel_group = gtk.AccelGroup()
        key, modifier = gtk.accelerator_parse('<Control>J')
        accel_group.connect_group(key, modifier, gtk.ACCEL_VISIBLE,
                                  self.on_accelerator_activate)
        self.popup.add_accel_group(accel_group)

        self.tooltip = TrackToolTip(self, player.PLAYER)
        self.tooltip.set_auto_update(True)

        if player.PLAYER.current is not None:
            self.label.set_text(self.formatter.format(player.PLAYER.current))

        self._drag_motion_timeout_id = None
        self._drag_leave_timeout_id = None
        self._toplevel_hide_id = None
        self._toplevel_window_state_event_id = None

        self.drag_dest_set(
            gtk.DEST_DEFAULT_ALL, self.view.targets,
            gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE)

        self.connect('drag-motion', self.on_drag_motion)
        self.connect('drag-leave', self.on_drag_leave)
        self.connect('drag-data-received', self.on_drag_data_received)
        self.view.connect('drag-motion', self.on_drag_motion)
        self.view.connect('drag-leave', self.on_drag_leave)
        event.add_callback(self.on_track_tags_changed, 'track_tags_changed')
        event.add_callback(self.on_option_set, 'plugin_minimode_option_set')
        self.on_option_set('plugin_minimode_option_set', settings,
                           'plugin/minimode/track_title_format')

    def destroy(self):
        """
            Cleanups
        """
        self.tooltip.destroy()
        QueueAdapter.destroy(self)
        gtk.ToggleButton.destroy(self)

    def update_playlist(self, playlist):
        """
            Updates the internally stored playlist
        """
        columns = self.view.get_model().columns
        model = PlaylistModel(playlist, columns, player.PLAYER)
        self.view.set_model(model)

    def do_hierarchy_changed(self, previous_toplevel):
        """
            Sets up automatic hiding on parent hide
        """
        if self._toplevel_hide_id is not None:
            previous_toplevel.disconnect(self._toplevel_hide_id)
            previous_toplevel.disconnect(self._toplevel_window_state_event_id)

        toplevel = self.get_toplevel()

        if isinstance(toplevel, gtk.Window):
            self._toplevel_hide_id = toplevel.connect('hide',
                                                      self.on_toplevel_hide)
            self._toplevel_window_state_event_id = toplevel.connect(
                'window-state-event', self.on_toplevel_window_state_event)
            self.popup.set_transient_for(toplevel)

    def do_scroll_event(self, event):
        """
            Changes the current track
        """
        if event.direction == gtk.gdk.SCROLL_UP:
            self.view.playlist.prev()
        elif event.direction == gtk.gdk.SCROLL_DOWN:
            self.view.playlist.next()
        else:
            return

        position = self.view.playlist.current_position

        try:
            track = self.view.playlist[position]
        except IndexError:
            pass
        else:
            player.QUEUE.play(track)

    def do_toggled(self):
        """
            Shows or hides the playlist
        """
        if self.get_active():
            self.arrow.props.arrow_type = gtk.ARROW_DOWN
            self.popup.show_all()
        else:
            self.popup.hide()
            self.arrow.props.arrow_type = gtk.ARROW_RIGHT

    def on_accelerator_activate(self, accel_group, acceleratable, keyval,
                                modifier):
        """
            Shows the current track
        """
        self.view.scroll_to_cell(self.view.playlist.current_position)
        self.view.set_cursor(self.view.playlist.current_position)

    def on_drag_motion(self, widget, context, x, y, time):
        """
            Prepares to show the playlist
        """
        # Defer display of the playlist
        if self._drag_motion_timeout_id is None:
            self._drag_motion_timeout_id = glib.timeout_add(
                500, lambda: self.set_active(True))

        # Prevent hiding of the playlist
        if self._drag_leave_timeout_id is not None:
            glib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

    def on_drag_leave(self, widget, context, time):
        """
            Prepares to hide the playlist
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            glib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        if self._drag_leave_timeout_id is not None:
            glib.source_remove(self._drag_leave_timeout_id)

        # Defer hiding of the playlist
        self._drag_leave_timeout_id = glib.timeout_add(
            500, lambda: self.set_active(False))

    def on_drag_data_received(self, widget, context, x, y, selection, info,
                              time):
        """
            Handles dropped data
        """
        # Enable display of the playlist on re-enter
        if self._drag_motion_timeout_id is not None:
            glib.source_remove(self._drag_motion_timeout_id)
            self._drag_motion_timeout_id = None

        # Enable hiding of the playlist on re-enter
        if self._drag_leave_timeout_id is not None:
            glib.source_remove(self._drag_leave_timeout_id)
            self._drag_leave_timeout_id = None

        self.view.emit('drag-data-received', context, x, y, selection, info,
                       time)

    def on_toplevel_hide(self, widget):
        """
            Hides the playlist
        """
        self.set_active(False)

    def on_toplevel_window_state_event(self, widget, event):
        """
            Hides the playlist
        """
        self.set_active(False)

    def on_popup_configure_event(self, widget, event):
        """
            Saves the window size after resizing
        """
        width = settings.get_option(
            'plugin/minimode/'
            'playlist_button_popup_width', 350)
        height = settings.get_option(
            'plugin/minimode/'
            'playlist_button_popup_height', 400)

        if event.width != width:
            settings.set_option(
                'plugin/minimode/'
                'playlist_button_popup_width', event.width)

        if event.height != height:
            settings.set_option(
                'plugin/minimode/'
                'playlist_button_popup_height', event.height)

    def on_queue_current_playlist_changed(self, event, queue, playlist):
        """
            Updates the list on queue changes
        """
        glib.idle_add(self.update_playlist, playlist)

    def on_queue_current_position_changed(self, event, playlist, positions):
        """
            Updates the list on queue changes
        """
        try:
            track = playlist[positions[0]]
        except IndexError:
            text = ''
        else:
            text = self.formatter.format(track)

        glib.idle_add(self.label.set_text, text)

    def on_track_tags_changed(self, event, track, tag):
        """
            Updates the button on tag changes
        """
        playlist = self.view.playlist
        track_position = playlist.index(track)

        if track in playlist and track_position == playlist.current_position:
            glib.idle_add(self.label.set_text, self.formatter.format(track))

    def on_option_set(self, event, settings, option):
        """
            Updates control upon setting change
        """
        if option == 'plugin/minimode/track_title_format':
            glib.idle_add(
                self.formatter.set_property, 'format',
                settings.get_option(option, _('$tracknumber - $title')))