def __init__(self):
        super(OnScreenKeyboard, self).__init__()

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.END)

        self.props.margin = 30

        smallest_width, smallest_height = status.screen.get_smallest_monitor_sizes(
        )

        self.max_width = min(smallest_width, LARGEST_OSK_WIDTH) - 60
        self.max_height = min(smallest_height / 3, LARGEST_OSK_HEIGHT) - 60
        # print(self.max_width, self.max_height)

        self._group_stack = None

        self.base_stack = Gtk.Stack()
        self.add(self.base_stack)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                      halign=Gtk.Align.CENTER,
                      valign=Gtk.Align.END)

        activate_button = TransparentButton("input-keyboard-symbolic",
                                            Gtk.IconSize.LARGE_TOOLBAR)
        activate_button.connect("clicked", self.on_activate_button_clicked)

        box.pack_start(activate_button, False, False, 0)
        box.show_all()

        self.base_stack.add_named(box, "disabled")
        self.base_stack.show_all()

        if settings.get_osk_a11y_active():
            self.build_and_show_keyboard()
    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")
        # size = Gtk.IconSize.MENU

        player_status = self.player.get_playback_status()

        # Player buttons

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton("media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button,
                                           "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button,
                                           "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic", size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button,
                                           "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [self.previous_button,
                                                     self.play_pause_button,
                                                     self.next_button]

        # Position labels and bar

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 4)

        vbox.set_valign(Gtk.Align.CENTER)

        position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        vbox.pack_start(position_length_box, True, True, 2)

        self.current_pos_label = BlinkingLabel("", 400)
        self.current_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_start(self.current_pos_label, False, False, 2)

        self.max_pos_label = BlinkingLabel("", 400)
        self.max_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_end(self.max_pos_label, False, False, 2)

        self.position_bar = PositionBar()
        vbox.pack_end(self.position_bar, True, True, 2)

        # Track info

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player,
                                           "position-changed",
                                           self.on_position_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self,
                                           "destroy",
                                           self.on_widget_destroy)

        self.update_position_timer(player_status)
class PlayerControl(Gtk.Box):
    def __init__(self):
        super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL)

        self.watcher = singletons.MediaPlayerWatcher
        self.player = self.watcher.get_best_player()

        if self.player:
            self.build_layout()

    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")
        # size = Gtk.IconSize.MENU

        player_status = self.player.get_playback_status()

        # Player buttons

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton("media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button,
                                           "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button,
                                           "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic", size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button,
                                           "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [self.previous_button,
                                                     self.play_pause_button,
                                                     self.next_button]

        # Position labels and bar

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 4)

        vbox.set_valign(Gtk.Align.CENTER)

        position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        vbox.pack_start(position_length_box, True, True, 2)

        self.current_pos_label = BlinkingLabel("", 400)
        self.current_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_start(self.current_pos_label, False, False, 2)

        self.max_pos_label = BlinkingLabel("", 400)
        self.max_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_end(self.max_pos_label, False, False, 2)

        self.position_bar = PositionBar()
        vbox.pack_end(self.position_bar, True, True, 2)

        # Track info

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player,
                                           "position-changed",
                                           self.on_position_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self,
                                           "destroy",
                                           self.on_widget_destroy)

        self.update_position_timer(player_status)

    def on_previous_clicked(self, button, data=None):
        self.player.go_previous()

    def on_next_clicked(self, button, data=None):
        self.player.go_next()

    def on_play_pause_clicked(self, button, data=None):
        self.player.play_pause()

    def get_play_pause_icon_name(self, status):
        if status == PlaybackStatus.Playing:
            icon_name = "media-playback-pause-symbolic"
        else:
            icon_name = "media-playback-start-symbolic"

        return icon_name

    def position_to_time_string(self, position):
        delta = datetime.timedelta(microseconds=position)

        duration = datetime.datetime.utcfromtimestamp(delta.total_seconds())

        if duration.hour < 0:
            return _("--:--")

        if duration.hour < 1:
            return duration.strftime(_("%M:%S"))
        else:
            return duration.strftime(_("%H:%M:%S"))

    def on_playback_status_changed(self, player, status, data=None):
        self.update_buttons(status)
        self.update_position_timer(status)
        self.update_position_display()

        self.update_position_values_appearance(status)

    def update_position_values_appearance(self, status):
        if status == PlaybackStatus.Paused:
            self.max_pos_label.set_blinking(True)
            self.current_pos_label.set_blinking(True)
        else:
            self.max_pos_label.set_blinking(False)
            self.current_pos_label.set_blinking(False)

    def on_metadata_changed(self, player):
        self.max_pos_label.set_text(self.position_to_time_string(self.player.get_max_position()))
        self.update_labels()

    def update_labels(self):
        self.track_name_label.set_text(self.player.get_track_name())

        artist_name = self.player.get_artist_name()
        album_name = self.player.get_album_name()

        if artist_name != "" and album_name != "":
            self.album_artist_label.set_text("%s - %s" % (self.player.get_artist_name(), self.player.get_album_name()))
        elif artist_name != "":
            self.album_artist_label.set_text(artist_name)
        elif album_name != "":
            self.album_artist_label.set_text(album_name)
        else:
            self.album_artist_label.set_text("")

    def pause_blink_step(self):
        self.max_pos_label.set_visible(not self.max_pos_label.get_visible())
        self.current_pos_label.set_visible(not self.current_pos_label.get_visible())

        return True

    def update_buttons(self, status):
        self.play_pause_button.set_sensitive(self.player.get_can_play_pause())
        self.next_button.set_sensitive(self.player.get_can_go_next())
        self.previous_button.set_sensitive(self.player.get_can_go_previous())

        icon_name = self.get_play_pause_icon_name(status)

        size = Gtk.IconSize.from_name("audio-button")

        image = Gtk.Image.new_from_icon_name(icon_name, size)
        self.play_pause_button.set_image(image)

    def update_position_display(self):
        if self.player.get_position() < self.player.get_max_position():
            value = self.player.get_position() / self.player.get_max_position()
        else:
            value = 1.0
        value = utils.CLAMP(value, 0.0, 1.0)
        self.position_bar.set_fraction(value)

        self.current_pos_label.set_text(self.position_to_time_string(self.player.get_position()))

        return True

    def update_position_timer(self, status):
        if status == PlaybackStatus.Playing:
            trackers.timer_tracker_get().start("position-timer", self.player.get_rate() * 1000, self.update_position_display)
        else:
            trackers.timer_tracker_get().cancel("position-timer")

    def on_position_changed(self, player, position, data=None):
        self.update_position_display()

    def on_widget_destroy(self, widget, data=None):
        trackers.con_tracker_get().disconnect(self.player,
                                              "position-changed",
                                              self.on_position_changed)

        trackers.con_tracker_get().disconnect(self.player,
                                              "status-changed",
                                              self.on_playback_status_changed)

        trackers.con_tracker_get().disconnect(self,
                                              "destroy",
                                              self.on_widget_destroy)

        trackers.timer_tracker_get().cancel("position-timer")

    def should_show(self):
        return self.player != None
    def __init__(self):
        super(UnlockDialog, self).__init__()

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)

        self.real_name = None
        self.user_name = None

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.face_image.get_style_context().add_class("faceimage")
        self.face_image.set_no_show_all(True)

        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry, "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry, "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, False, False, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton(
            "screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_tooltip_text(_("Unlock"))
        trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        self.auth_switch_button = TransparentButton(
            "screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_switch_button.set_tooltip_text(_("Switch User"))

        trackers.con_tracker_get().connect(self.auth_switch_button, "clicked",
                                           self.on_switch_user_clicked)

        button_box.pack_start(self.auth_switch_button, False, False, 4)

        status.focusWidgets = [
            self.password_entry, self.auth_unlock_button,
            self.auth_switch_button
        ]

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                                spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        self.account_client = singletons.AccountsServiceClient
        if self.account_client.is_loaded:
            self.on_account_client_loaded(self.account_client)
        else:
            trackers.con_tracker_get().connect(self.account_client,
                                               "account-loaded",
                                               self.on_account_client_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap, "state-changed",
                                           self.keymap_handler)

        trackers.con_tracker_get().connect_after(self,
                                                 "notify::child-revealed",
                                                 self.on_revealed)

        self.auth_client = AuthClient()

        trackers.con_tracker_get().connect(self.auth_client, "auth-success",
                                           self.on_authentication_success)
        trackers.con_tracker_get().connect(self.auth_client, "auth-failure",
                                           self.on_authentication_failure)
        trackers.con_tracker_get().connect(self.auth_client, "auth-cancel",
                                           self.on_authentication_cancelled)
        trackers.con_tracker_get().connect(self.auth_client, "auth-busy",
                                           self.on_authentication_busy_changed)
        trackers.con_tracker_get().connect(
            self.auth_client, "auth-prompt",
            self.on_authentication_prompt_changed)
class UnlockDialog(BaseWindow):
    """
    The main widget for the unlock dialog - this is a direct child of
    the Stage's GtkOverlay.

    It has a number of parts, namely:
        - The user face image.
        - The user's real name (or username if the real name is unavailable)
        - The password entry widget
        - Unlock and Switch User buttons
        - A caps lock warning label
        - An invalid password error label
    """
    __gsignals__ = {
        'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'authenticate-success': (GObject.SignalFlags.RUN_LAST, None, ()),
        'authenticate-failure': (GObject.SignalFlags.RUN_LAST, None, ()),
        'authenticate-cancel': (GObject.SignalFlags.RUN_LAST, None, ())
    }

    def __init__(self):
        super(UnlockDialog, self).__init__()

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)

        self.real_name = None
        self.user_name = None

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.face_image.get_style_context().add_class("faceimage")
        self.face_image.set_no_show_all(True)

        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry, "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry, "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, False, False, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton(
            "screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_tooltip_text(_("Unlock"))
        trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        self.auth_switch_button = TransparentButton(
            "screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_switch_button.set_tooltip_text(_("Switch User"))

        trackers.con_tracker_get().connect(self.auth_switch_button, "clicked",
                                           self.on_switch_user_clicked)

        button_box.pack_start(self.auth_switch_button, False, False, 4)

        status.focusWidgets = [
            self.password_entry, self.auth_unlock_button,
            self.auth_switch_button
        ]

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                                spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        self.account_client = singletons.AccountsServiceClient
        if self.account_client.is_loaded:
            self.on_account_client_loaded(self.account_client)
        else:
            trackers.con_tracker_get().connect(self.account_client,
                                               "account-loaded",
                                               self.on_account_client_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap, "state-changed",
                                           self.keymap_handler)

        trackers.con_tracker_get().connect_after(self,
                                                 "notify::child-revealed",
                                                 self.on_revealed)

        self.auth_client = AuthClient()

        trackers.con_tracker_get().connect(self.auth_client, "auth-success",
                                           self.on_authentication_success)
        trackers.con_tracker_get().connect(self.auth_client, "auth-failure",
                                           self.on_authentication_failure)
        trackers.con_tracker_get().connect(self.auth_client, "auth-cancel",
                                           self.on_authentication_cancelled)
        trackers.con_tracker_get().connect(self.auth_client, "auth-busy",
                                           self.on_authentication_busy_changed)
        trackers.con_tracker_get().connect(
            self.auth_client, "auth-prompt",
            self.on_authentication_prompt_changed)

    def initialize_auth_client(self):
        return self.auth_client.initialize()

    def cancel_auth_client(self):
        self.auth_client.cancel()

    def on_authentication_success(self, auth_client):
        self.clear_entry()
        self.emit("authenticate-success")

    def on_authentication_failure(self, auth_client):
        """
        Called upon authentication failure, clears the password, sets an error message,
        and refocuses the password entry.
        """
        self.clear_entry()
        self.auth_message_label.set_text(_("Incorrect password"))

        self.emit("authenticate-failure")
        self.emit("uninhibit-timeout")

    def on_authentication_cancelled(self, auth_client):
        self.emit("authenticate-cancel")

    def on_authentication_busy_changed(self, auth_client, busy):
        if busy:
            self.auth_message_label.set_text("")
            self.clear_entry()
            self.entry_box.set_sensitive(False)
            self.password_entry.start_progress()
            self.password_entry.set_placeholder_text(_("Checking..."))
        else:
            self.entry_box.set_sensitive(True)
            self.password_entry.stop_progress()
            self.password_entry.set_placeholder_text(
                self.password_entry.placeholder_text)

    def on_authentication_prompt_changed(self, auth_client, prompt):
        if "password:"******"Please enter your password...")
        else:
            prompt = prompt.replace(":", "")

        self.password_entry.placeholder_text = prompt
        self.password_entry.set_placeholder_text(
            self.password_entry.placeholder_text)

    def cancel(self):
        """
        Clears the auth message text if we have any.
        """
        self.auth_message_label.set_text("")

    def on_revealed(self, widget, child):
        """
        Updates the capslock state and ensures the password is cleared when we're first shown.
        """
        if self.get_child_revealed():
            self.keymap_handler(self.keymap)
        else:
            self.password_entry.set_text("")

    def queue_key_event(self, event):
        """
        Takes a propagated key event from the stage and passes it to the entry widget,
        possibly queueing up the first character of the password.
        """
        if not self.password_entry.get_realized():
            self.password_entry.realize()
            self.password_entry.grab_focus()

        self.password_entry.event(event)

    def keymap_handler(self, keymap):
        """
        Handler for the GdkKeymap changing - updates our capslock indicator label.
        """
        if keymap.get_caps_lock_state():
            self.capslock_label.set_text(_("You have the Caps Lock key on."))
        else:
            self.capslock_label.set_text("")

    def on_account_client_loaded(self, client):
        """
        Handler for the AccountsService - requests the user real name and .face image.
        """
        if client.get_real_name() != None:
            self.real_name = client.get_real_name()
            self.update_realname_label()

        if client.get_face_path() != None:
            self.face_image.set_from_path(client.get_face_path())
            self.face_image.show()

    def on_password_entry_text_changed(self, editable):
        """
        Handler for the password entry text changing - this controls the sensitivity
        of the unlock button, as well as returning visual focus to the entry any time
        a key event is received.
        """

        if not self.password_entry.has_focus():
            self.password_entry.grab_focus()

    def on_password_entry_button_press(self, widget, event):
        """
        Prevents the usual copy/paste popup when right-clicking the PasswordEntry.
        """
        if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
            return Gdk.EVENT_STOP

        return Gdk.EVENT_PROPAGATE

    def on_unlock_clicked(self, button=None):
        """
        Callback for the unlock button.  Activates the 'progress' animation
        in the GtkEntry, and attempts to authenticate the password.  During this
        time, we also inhibit the unlock timeout, so we don't fade out while waiting
        for an authentication result (highly unlikely.)
        """
        self.emit("inhibit-timeout")

        text = self.password_entry.get_text()

        # We must end with a newline, fgets relies upon that to continue.
        if text[-1:] != "\n":
            text += "\n"

        self.auth_client.message_to_child(text)

    def on_auth_enter_key(self, widget):
        """
        Implicitly activates the unlock button when the Enter/Return key is pressed.
        """

        self.on_unlock_clicked()

    def on_switch_user_clicked(self, widget):
        """
        Callback for the switch-user button.
        """
        utils.do_user_switch()

    def clear_entry(self):
        """
        Clear the password entry widget.
        """
        self.password_entry.set_text("")

    def update_realname_label(self):
        """
        Updates the name label to the current real_name.
        """
        self.realname_label.set_text(self.real_name)
Exemple #6
0
class PlayerControl(Gtk.Box):
    """
    Provides info and controls for any active music or other media player.  It is a
    a direct child of the AudioPanel, and is only shown if there is an active mpris
    interface we can connect to.
    """
    def __init__(self):
        super(PlayerControl,
              self).__init__(orientation=Gtk.Orientation.HORIZONTAL)

        self.watcher = singletons.MediaPlayerWatcher
        self.player = self.watcher.get_best_player()

        if self.player:
            self.build_layout()

    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")

        player_status = self.player.get_playback_status()

        # Player buttons

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton(
            "media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button, "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(
            self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button, "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic",
                                             size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button, "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [
            self.previous_button, self.play_pause_button, self.next_button
        ]

        # Position labels and bar

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 4)

        vbox.set_valign(Gtk.Align.CENTER)

        position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        vbox.pack_start(position_length_box, True, True, 2)

        self.current_pos_label = BlinkingLabel("", 400)
        self.current_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_start(self.current_pos_label, False, False, 2)

        self.max_pos_label = BlinkingLabel("", 400)
        self.max_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_end(self.max_pos_label, False, False, 2)

        self.position_bar = PositionBar()
        vbox.pack_end(self.position_bar, True, True, 2)

        # Track info

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player, "position-changed",
                                           self.on_position_changed)

        trackers.con_tracker_get().connect(self.player, "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player, "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self, "destroy",
                                           self.on_widget_destroy)

        self.update_position_timer(player_status)

    def on_previous_clicked(self, button, data=None):
        self.player.go_previous()

    def on_next_clicked(self, button, data=None):
        self.player.go_next()

    def on_play_pause_clicked(self, button, data=None):
        self.player.play_pause()

    def get_play_pause_icon_name(self, status):
        if status == PlaybackStatus.Playing:
            icon_name = "media-playback-pause-symbolic"
        else:
            icon_name = "media-playback-start-symbolic"

        return icon_name

    def position_to_time_string(self, position):
        """
        We receive track position and track length values in microseconds.
        This function formats this into readable HH:MM:SS format, and handles
        any invalid values.
        """
        delta = datetime.timedelta(microseconds=position)

        duration = datetime.datetime.utcfromtimestamp(delta.total_seconds())

        if duration.hour < 0:
            return _("--:--")

        if duration.hour < 1:
            return duration.strftime(_("%M:%S"))
        else:
            return duration.strftime(_("%H:%M:%S"))

    def on_playback_status_changed(self, player, status, data=None):
        self.update_buttons(status)
        self.update_position_timer(status)
        self.update_position_display()

        self.update_position_values_appearance(status)

    def update_position_values_appearance(self, status):
        """
        When the player is paused, we blink the position/length values.
        """
        if status == PlaybackStatus.Paused:
            self.max_pos_label.set_blinking(True)
            self.current_pos_label.set_blinking(True)
        else:
            self.max_pos_label.set_blinking(False)
            self.current_pos_label.set_blinking(False)

    def on_metadata_changed(self, player):
        """
        Update max position and labels when the player metadata changes
        """
        self.max_pos_label.set_text(
            self.position_to_time_string(self.player.get_max_position()))
        self.update_labels()

    def update_labels(self):
        """
        Construct the track and artist-album labels as well as possible.
        """
        self.track_name_label.set_text(self.player.get_track_name())

        artist_name = self.player.get_artist_name()
        album_name = self.player.get_album_name()

        if artist_name != "" and album_name != "":
            self.album_artist_label.set_text(
                "%s - %s" %
                (self.player.get_artist_name(), self.player.get_album_name()))
        elif artist_name != "":
            self.album_artist_label.set_text(artist_name)
        elif album_name != "":
            self.album_artist_label.set_text(album_name)
        else:
            self.album_artist_label.set_text("")

    def update_buttons(self, status):
        """
        Updates the player buttons based on the current state
        """
        self.play_pause_button.set_sensitive(self.player.get_can_play_pause())
        self.next_button.set_sensitive(self.player.get_can_go_next())
        self.previous_button.set_sensitive(self.player.get_can_go_previous())

        icon_name = self.get_play_pause_icon_name(status)

        size = Gtk.IconSize.from_name("audio-button")

        image = Gtk.Image.new_from_icon_name(icon_name, size)
        self.play_pause_button.set_image(image)

    def update_position_display(self):
        """
        Updates the position values and bar to reflect the current state.
        """
        if self.player.get_position() < self.player.get_max_position():
            value = self.player.get_position() / self.player.get_max_position()
        else:
            value = 1.0
        value = utils.CLAMP(value, 0.0, 1.0)
        self.position_bar.set_fraction(value)

        self.current_pos_label.set_text(
            self.position_to_time_string(self.player.get_position()))

        return True

    def update_position_timer(self, status):
        """
        Starts or stops the position update timer - this is based upon the provided rate
        property of the player, which is defined as the recommended update frequency for position
        data.
        """
        if status == PlaybackStatus.Playing:
            trackers.timer_tracker_get().start("position-timer",
                                               self.player.get_rate() * 1000,
                                               self.update_position_display)
        else:
            trackers.timer_tracker_get().cancel("position-timer")

    def on_position_changed(self, player, position, data=None):
        """
        Callback for an explicit position change from the player.
        """
        self.update_position_display()

    def on_widget_destroy(self, widget, data=None):
        trackers.con_tracker_get().disconnect(self.player, "position-changed",
                                              self.on_position_changed)

        trackers.con_tracker_get().disconnect(self.player, "status-changed",
                                              self.on_playback_status_changed)

        trackers.con_tracker_get().disconnect(self, "destroy",
                                              self.on_widget_destroy)

        trackers.timer_tracker_get().cancel("position-timer")

    def should_show(self):
        """
        Checked by the AudioPanel, whether or not this widget should be displayed.
        """
        return self.player != None
class PlayerControl(Gtk.Box):
    """
    Provides info and controls for any active music or other media player.  It is a
    a direct child of the AudioPanel, and is only shown if there is an active mpris
    interface we can connect to.
    """
    def __init__(self):
        super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL)

        self.watcher = singletons.MediaPlayerWatcher
        self.player = self.watcher.get_best_player()

        if self.player:
            self.build_layout()

    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")

        player_status = self.player.get_playback_status()

        # Player buttons

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton("media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button,
                                           "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button,
                                           "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic", size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button,
                                           "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [self.previous_button,
                                                     self.play_pause_button,
                                                     self.next_button]

        # Position labels and bar

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 4)

        vbox.set_valign(Gtk.Align.CENTER)

        position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        vbox.pack_start(position_length_box, True, True, 2)

        self.current_pos_label = BlinkingLabel("", 400)
        self.current_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_start(self.current_pos_label, False, False, 2)

        self.max_pos_label = BlinkingLabel("", 400)
        self.max_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_end(self.max_pos_label, False, False, 2)

        self.position_bar = PositionBar()
        vbox.pack_end(self.position_bar, True, True, 2)

        # Track info

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player,
                                           "position-changed",
                                           self.on_position_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self,
                                           "destroy",
                                           self.on_widget_destroy)

        self.update_position_timer(player_status)

    def on_previous_clicked(self, button, data=None):
        self.player.go_previous()

    def on_next_clicked(self, button, data=None):
        self.player.go_next()

    def on_play_pause_clicked(self, button, data=None):
        self.player.play_pause()

    def get_play_pause_icon_name(self, status):
        if status == PlaybackStatus.Playing:
            icon_name = "media-playback-pause-symbolic"
        else:
            icon_name = "media-playback-start-symbolic"

        return icon_name

    def position_to_time_string(self, position):
        """
        We receive track position and track length values in microseconds.
        This function formats this into readable HH:MM:SS format, and handles
        any invalid values.
        """
        delta = datetime.timedelta(microseconds=position)

        duration = datetime.datetime.utcfromtimestamp(delta.total_seconds())

        if duration.hour < 0:
            return _("--:--")

        if duration.hour < 1:
            return duration.strftime(_("%M:%S"))
        else:
            return duration.strftime(_("%H:%M:%S"))

    def on_playback_status_changed(self, player, status, data=None):
        self.update_buttons(status)
        self.update_position_timer(status)
        self.update_position_display()

        self.update_position_values_appearance(status)

    def update_position_values_appearance(self, status):
        """
        When the player is paused, we blink the position/length values.
        """
        if status == PlaybackStatus.Paused:
            self.max_pos_label.set_blinking(True)
            self.current_pos_label.set_blinking(True)
        else:
            self.max_pos_label.set_blinking(False)
            self.current_pos_label.set_blinking(False)

    def on_metadata_changed(self, player):
        """
        Update max position and labels when the player metadata changes
        """
        self.max_pos_label.set_text(self.position_to_time_string(self.player.get_max_position()))
        self.update_labels()

    def update_labels(self):
        """
        Construct the track and artist-album labels as well as possible.
        """
        self.track_name_label.set_text(self.player.get_track_name())

        artist_name = self.player.get_artist_name()
        album_name = self.player.get_album_name()

        if artist_name != "" and album_name != "":
            self.album_artist_label.set_text("%s - %s" % (self.player.get_artist_name(), self.player.get_album_name()))
        elif artist_name != "":
            self.album_artist_label.set_text(artist_name)
        elif album_name != "":
            self.album_artist_label.set_text(album_name)
        else:
            self.album_artist_label.set_text("")

    def update_buttons(self, status):
        """
        Updates the player buttons based on the current state
        """
        self.play_pause_button.set_sensitive(self.player.get_can_play_pause())
        self.next_button.set_sensitive(self.player.get_can_go_next())
        self.previous_button.set_sensitive(self.player.get_can_go_previous())

        icon_name = self.get_play_pause_icon_name(status)

        size = Gtk.IconSize.from_name("audio-button")

        image = Gtk.Image.new_from_icon_name(icon_name, size)
        self.play_pause_button.set_image(image)

    def update_position_display(self):
        """
        Updates the position values and bar to reflect the current state.
        """
        if self.player.get_position() < self.player.get_max_position():
            value = self.player.get_position() / self.player.get_max_position()
        else:
            value = 1.0
        value = utils.CLAMP(value, 0.0, 1.0)
        self.position_bar.set_fraction(value)

        self.current_pos_label.set_text(self.position_to_time_string(self.player.get_position()))

        return True

    def update_position_timer(self, status):
        """
        Starts or stops the position update timer - this is based upon the provided rate
        property of the player, which is defined as the recommended update frequency for position
        data.
        """
        if status == PlaybackStatus.Playing:
            trackers.timer_tracker_get().start("position-timer", self.player.get_rate() * 1000, self.update_position_display)
        else:
            trackers.timer_tracker_get().cancel("position-timer")

    def on_position_changed(self, player, position, data=None):
        """
        Callback for an explicit position change from the player.
        """
        self.update_position_display()

    def on_widget_destroy(self, widget, data=None):
        trackers.con_tracker_get().disconnect(self.player,
                                              "position-changed",
                                              self.on_position_changed)

        trackers.con_tracker_get().disconnect(self.player,
                                              "status-changed",
                                              self.on_playback_status_changed)

        trackers.con_tracker_get().disconnect(self,
                                              "destroy",
                                              self.on_widget_destroy)

        trackers.timer_tracker_get().cancel("position-timer")

    def should_show(self):
        """
        Checked by the AudioPanel, whether or not this widget should be displayed.
        """
        return self.player != None
Exemple #8
0
    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")
        # size = Gtk.IconSize.MENU

        player_status = self.player.get_playback_status()

        # Player buttons

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton(
            "media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button, "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(
            self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button, "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic",
                                             size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button, "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [
            self.previous_button, self.play_pause_button, self.next_button
        ]

        # Position labels and bar

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 4)

        vbox.set_valign(Gtk.Align.CENTER)

        position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        vbox.pack_start(position_length_box, True, True, 2)

        self.current_pos_label = BlinkingLabel("", 400)
        self.current_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_start(self.current_pos_label, False, False, 2)

        self.max_pos_label = BlinkingLabel("", 400)
        self.max_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_end(self.max_pos_label, False, False, 2)

        self.position_bar = PositionBar()
        vbox.pack_end(self.position_bar, True, True, 2)

        # Track info

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player, "position-changed",
                                           self.on_position_changed)

        trackers.con_tracker_get().connect(self.player, "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player, "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self, "destroy",
                                           self.on_widget_destroy)

        self.update_position_timer(player_status)
    def __init__(self):
        super(UnlockDialog, self).__init__()

        settings = Gio.Settings.new("org.cinnamon.desktop.lockdown")

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)

        self.real_name = None
        self.user_name = None

        self.bounce_rect = None
        self.bounce_count = 0

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.face_image.get_style_context().add_class("faceimage")
        self.face_image.set_no_show_all(True)

        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry,
                                           "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, False, False, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton("screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_tooltip_text(_("Unlock"))
        trackers.con_tracker_get().connect(self.auth_unlock_button,
                                           "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        status.focusWidgets = [self.password_entry, self.auth_unlock_button]

        if not settings.get_boolean("disable-user-switching"):
            self.auth_switch_button = TransparentButton("screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
            self.auth_switch_button.set_tooltip_text(_("Switch User"))
            trackers.con_tracker_get().connect(self.auth_switch_button, "clicked", self.on_switch_user_clicked)
            button_box.pack_start(self.auth_switch_button, False, False, 4)
            status.focusWidgets.append(self.auth_switch_button)

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        self.account_client = singletons.AccountsServiceClient
        if self.account_client.is_loaded:
            self.on_account_client_loaded(self.account_client)
        else:
            trackers.con_tracker_get().connect(self.account_client,
                                               "account-loaded",
                                               self.on_account_client_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap,
                                           "state-changed",
                                           self.keymap_handler)

        self.keymap_handler(self.keymap)

        self.auth_client = AuthClient()

        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-success",
                                           self.on_authentication_success)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-failure",
                                           self.on_authentication_failure)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-cancel",
                                           self.on_authentication_cancelled)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-busy",
                                           self.on_authentication_busy_changed)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-prompt",
                                           self.on_authentication_prompt_changed)

        self.box.show_all()
class UnlockDialog(BaseWindow):
    """
    The main widget for the unlock dialog - this is a direct child of
    the Stage's GtkOverlay.

    It has a number of parts, namely:
        - The user face image.
        - The user's real name (or username if the real name is unavailable)
        - The password entry widget
        - Unlock and Switch User buttons
        - A caps lock warning label
        - An invalid password error label
    """
    __gsignals__ = {
        'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'authenticate-success': (GObject.SignalFlags.RUN_LAST, None, ()),
        'authenticate-failure': (GObject.SignalFlags.RUN_LAST, None, ()),
        'authenticate-cancel': (GObject.SignalFlags.RUN_LAST, None, ())
    }

    def __init__(self):
        super(UnlockDialog, self).__init__()

        settings = Gio.Settings.new("org.cinnamon.desktop.lockdown")

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)

        self.real_name = None
        self.user_name = None

        self.bounce_rect = None
        self.bounce_count = 0

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.face_image.get_style_context().add_class("faceimage")
        self.face_image.set_no_show_all(True)

        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry,
                                           "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, False, False, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton("screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_tooltip_text(_("Unlock"))
        trackers.con_tracker_get().connect(self.auth_unlock_button,
                                           "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        status.focusWidgets = [self.password_entry, self.auth_unlock_button]

        if not settings.get_boolean("disable-user-switching"):
            self.auth_switch_button = TransparentButton("screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
            self.auth_switch_button.set_tooltip_text(_("Switch User"))
            trackers.con_tracker_get().connect(self.auth_switch_button, "clicked", self.on_switch_user_clicked)
            button_box.pack_start(self.auth_switch_button, False, False, 4)
            status.focusWidgets.append(self.auth_switch_button)

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        self.account_client = singletons.AccountsServiceClient
        if self.account_client.is_loaded:
            self.on_account_client_loaded(self.account_client)
        else:
            trackers.con_tracker_get().connect(self.account_client,
                                               "account-loaded",
                                               self.on_account_client_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap,
                                           "state-changed",
                                           self.keymap_handler)

        self.keymap_handler(self.keymap)

        self.auth_client = AuthClient()

        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-success",
                                           self.on_authentication_success)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-failure",
                                           self.on_authentication_failure)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-cancel",
                                           self.on_authentication_cancelled)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-busy",
                                           self.on_authentication_busy_changed)
        trackers.con_tracker_get().connect(self.auth_client,
                                           "auth-prompt",
                                           self.on_authentication_prompt_changed)

        self.box.show_all()

    def initialize_auth_client(self):
        return self.auth_client.initialize()

    def cancel_auth_client(self):
        self.clear_entry()

        self.auth_client.cancel()

    def on_authentication_success(self, auth_client):
        self.clear_entry()
        self.emit("authenticate-success")

    def on_authentication_failure(self, auth_client):
        """
        Called upon authentication failure, clears the password, sets an error message,
        and refocuses the password entry.
        """
        self.clear_entry()
        self.auth_message_label.set_text(_("Incorrect password"))

        self.emit("authenticate-failure")
        self.emit("uninhibit-timeout")

    def on_authentication_cancelled(self, auth_client):
        self.emit("authenticate-cancel")

    def on_authentication_busy_changed(self, auth_client, busy):
        if busy:
            self.auth_message_label.set_text("")
            self.clear_entry()
            self.entry_box.set_sensitive(False)
            self.password_entry.start_progress()
            self.password_entry.set_placeholder_text (_("Checking..."))
        else:
            self.entry_box.set_sensitive(True)
            self.password_entry.stop_progress()
            self.password_entry.set_placeholder_text (self.password_entry.placeholder_text)

    def on_authentication_prompt_changed(self, auth_client, prompt):
        if "password:"******"Please enter your password...")
        else:
            prompt = prompt.replace(":", "")

        self.password_entry.placeholder_text = prompt
        self.password_entry.set_placeholder_text(self.password_entry.placeholder_text)

    def cancel(self):
        """
        Clears the auth message text if we have any.
        """
        self.auth_message_label.set_text("")

    def queue_key_event(self, event):
        """
        Takes a propagated key event from the stage and passes it to the entry widget,
        possibly queueing up the first character of the password.
        """
        if not self.password_entry.get_realized():
            self.password_entry.realize()
            self.password_entry.grab_focus()

        self.password_entry.event(event)

    def keymap_handler(self, keymap):
        """
        Handler for the GdkKeymap changing - updates our capslock indicator label.
        """
        if keymap.get_caps_lock_state():
            self.capslock_label.set_text(_("You have the Caps Lock key on."))
        else:
            self.capslock_label.set_text("")

    def on_account_client_loaded(self, client):
        """
        Handler for the AccountsService - requests the user real name and .face image.
        """
        if client.get_real_name() != None:
            self.real_name = client.get_real_name()
            self.update_realname_label()

        if client.get_face_path() != None:
            self.face_image.set_from_path(client.get_face_path())
            self.face_image.show()

    def on_password_entry_text_changed(self, editable):
        """
        Handler for the password entry text changing - this controls the sensitivity
        of the unlock button, as well as returning visual focus to the entry any time
        a key event is received.
        """

        if not self.password_entry.has_focus():
            self.password_entry.grab_focus()

    def on_password_entry_button_press(self, widget, event):
        """
        Prevents the usual copy/paste popup when right-clicking the PasswordEntry.
        """
        if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
            return Gdk.EVENT_STOP

        return Gdk.EVENT_PROPAGATE

    def on_unlock_clicked(self, button=None):
        """
        Callback for the unlock button.  Activates the 'progress' animation
        in the GtkEntry, and attempts to authenticate the password.  During this
        time, we also inhibit the unlock timeout, so we don't fade out while waiting
        for an authentication result (highly unlikely.)
        """
        self.emit("inhibit-timeout")

        text = self.password_entry.get_text()

        # We must end with a newline, fgets relies upon that to continue.
        if text[-1:] != "\n":
            text += "\n"

        self.auth_client.message_to_child(text)

    def on_auth_enter_key(self, widget):
        """
        Implicitly activates the unlock button when the Enter/Return key is pressed.
        """

        self.on_unlock_clicked()

    def on_switch_user_clicked(self, widget):
        """
        Callback for the switch-user button.
        """
        utils.do_user_switch()

    def clear_entry(self):
        """
        Clear the password entry widget.
        """
        self.password_entry.set_text("")

    def update_realname_label(self):
        """
        Updates the name label to the current real_name.
        """
        self.realname_label.set_text(self.real_name)

    def blink(self):
        GObject.timeout_add(75, self.on_blink_tick)

    def on_blink_tick(self, data=None):
        window = self.get_window()

        if window == None:
            return False

        x, y = window.get_position()

        if self.bounce_count < 6:
            if self.bounce_count % 2 == 0:
                y += 6
            else:
                y -= 6
            self.get_window().move(x, y)
            self.queue_draw()

            self.bounce_count += 1

            return True

        self.bounce_count = 0
        return False
Exemple #11
0
    def __init__(self):
        super(UnlockDialog, self).__init__()

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)
        self.set_size_request(350, -1)

        self.real_name = None
        self.user_name = None

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry, "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry, "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, True, True, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton(
            "screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_sensitive(False)

        trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        self.auth_switch_button = TransparentButton(
            "screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        trackers.con_tracker_get().connect(self.auth_switch_button, "clicked",
                                           self.on_switch_user_clicked)

        button_box.pack_start(self.auth_switch_button, False, False, 4)

        status.focusWidgets = [
            self.password_entry, self.auth_unlock_button,
            self.auth_switch_button
        ]

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                                spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        global acc_service

        if acc_service is not None:
            self.on_accounts_service_loaded(acc_service, None)
        else:
            acc_service = AccountsService.UserManager.get_default().get_user(
                self.user_name)

        trackers.con_tracker_get().connect(acc_service, "notify::is-loaded",
                                           self.on_accounts_service_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap, "state-changed",
                                           self.keymap_handler)

        trackers.con_tracker_get().connect_after(self,
                                                 "notify::child-revealed",
                                                 self.on_revealed)
Exemple #12
0
class UnlockDialog(BaseWindow):
    __gsignals__ = {
        'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'auth-success': (GObject.SignalFlags.RUN_LAST, None, ()),
        'auth-failure': (GObject.SignalFlags.RUN_LAST, None, ())
    }

    def __init__(self):
        super(UnlockDialog, self).__init__()

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)
        self.set_size_request(350, -1)

        self.real_name = None
        self.user_name = None

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry, "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry, "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, True, True, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton(
            "screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_sensitive(False)

        trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        self.auth_switch_button = TransparentButton(
            "screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        trackers.con_tracker_get().connect(self.auth_switch_button, "clicked",
                                           self.on_switch_user_clicked)

        button_box.pack_start(self.auth_switch_button, False, False, 4)

        status.focusWidgets = [
            self.password_entry, self.auth_unlock_button,
            self.auth_switch_button
        ]

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                                spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        global acc_service

        if acc_service is not None:
            self.on_accounts_service_loaded(acc_service, None)
        else:
            acc_service = AccountsService.UserManager.get_default().get_user(
                self.user_name)

        trackers.con_tracker_get().connect(acc_service, "notify::is-loaded",
                                           self.on_accounts_service_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap, "state-changed",
                                           self.keymap_handler)

        trackers.con_tracker_get().connect_after(self,
                                                 "notify::child-revealed",
                                                 self.on_revealed)

    def cancel(self):
        self.auth_message_label.set_text("")

    def on_revealed(self, widget, child):
        if self.get_child_revealed():
            self.keymap_handler(self.keymap)
        else:
            self.password_entry.set_text("")

    def queue_key_event(self, event):
        if not self.password_entry.get_realized():
            self.password_entry.realize()

        self.password_entry.event(event)

    def keymap_handler(self, keymap):
        if keymap.get_caps_lock_state():
            self.capslock_label.set_text(_("You have the Caps Lock key on."))
        else:
            self.capslock_label.set_text("")

    def on_accounts_service_loaded(self, service, param):
        self.real_name = service.get_real_name()
        self.update_realname_label()

        for path in [
                os.path.join(service.get_home_dir(), ".face"),
                service.get_icon_file(),
                "/usr/share/cinnamon/faces/user-generic.png"
        ]:
            if os.path.exists(path):
                self.face_image.set_from_file(path)
                break

    def on_password_entry_text_changed(self, editable):
        if not self.password_entry.has_focus():
            self.password_entry.grab_focus()
        self.auth_unlock_button.set_sensitive(editable.get_text() != "")

    def on_password_entry_button_press(self, widget, event):
        if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
            return Gdk.EVENT_STOP

        return Gdk.EVENT_PROPAGATE

    def on_unlock_clicked(self, button=None):
        self.emit("inhibit-timeout")

        text = self.password_entry.get_text()
        self.start_progress()

        self.password_entry.set_placeholder_text(_("Checking..."))

        self.authenticate(text)

    def on_auth_enter_key(self, widget):
        if widget.get_text() == "":
            return

        self.on_unlock_clicked()

    def on_switch_user_clicked(self, widget):
        utils.do_user_switch()

    def pulse(self):
        self.password_entry.progress_pulse()
        return True

    def start_progress(self):
        self.password_entry.set_progress_pulse_step(0.2)
        trackers.timer_tracker_get().start("auth-progress", 100, self.pulse)

    def stop_progress(self):
        trackers.timer_tracker_get().cancel("auth-progress")
        self.password_entry.set_progress_fraction(0.0)

    def clear_entry(self):
        self.password_entry.set_text("")

    def update_realname_label(self):
        self.realname_label.set_text(self.real_name)

    def authenticate(self, password):
        CinnamonDesktop.desktop_check_user_password(self.user_name, password,
                                                    self.authenticate_callback)

    def authenticate_callback(self, success, data=None):
        self.stop_progress()

        if success:
            self.clear_entry()
            self.emit("auth-success")
        else:
            self.authentication_failed()
            self.emit("auth-failure")

    def authentication_failed(self):
        self.clear_entry()

        self.password_entry.set_placeholder_text(_("Enter password..."))
        self.auth_message_label.set_text(_("Password incorrect - try again."))

        self.password_entry.grab_focus()

        self.emit("uninhibit-timeout")
    def __init__(self):
        super(UnlockDialog, self).__init__()

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)
        self.set_size_request(350, -1)

        self.real_name = None
        self.user_name = None

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.face_image.get_style_context().add_class("faceimage")
        self.face_image.set_no_show_all(True)

        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry,
                                           "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, True, True, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton("screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_sensitive(False)

        trackers.con_tracker_get().connect(self.auth_unlock_button,
                                           "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        self.auth_switch_button = TransparentButton("screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        trackers.con_tracker_get().connect(self.auth_switch_button,
                                           "clicked",
                                           self.on_switch_user_clicked)

        button_box.pack_start(self.auth_switch_button, False, False, 4)

        status.focusWidgets = [self.password_entry,
                               self.auth_unlock_button,
                               self.auth_switch_button]

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        self.account_client = singletons.AccountsServiceClient
        if self.account_client.is_loaded:
            self.on_account_client_loaded(self.account_client)
        else:
            trackers.con_tracker_get().connect(self.account_client,
                                               "account-loaded",
                                               self.on_account_client_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap,
                                           "state-changed",
                                           self.keymap_handler)

        trackers.con_tracker_get().connect_after(self,
                                                 "notify::child-revealed",
                                                 self.on_revealed)
class UnlockDialog(BaseWindow):
    """
    The main widget for the unlock dialog - this is a direct child of
    the Stage's GtkOverlay.

    It has a number of parts, namely:
        - The user face image.
        - The user's real name (or username if the real name is unavailable)
        - The password entry widget
        - Unlock and Switch User buttons
        - A caps lock warning label
        - An invalid password error label
    """
    __gsignals__ = {
        'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()),
        'auth-success': (GObject.SignalFlags.RUN_LAST, None, ()),
        'auth-failure': (GObject.SignalFlags.RUN_LAST, None, ())
    }

    def __init__(self):
        super(UnlockDialog, self).__init__()

        self.set_halign(Gtk.Align.CENTER)
        self.set_valign(Gtk.Align.CENTER)
        self.set_size_request(350, -1)

        self.real_name = None
        self.user_name = None

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.box.get_style_context().add_class("unlockbox")
        self.add(self.box)

        self.face_image = FramedImage()
        self.face_image.set_halign(Gtk.Align.CENTER)
        self.face_image.get_style_context().add_class("faceimage")
        self.face_image.set_no_show_all(True)

        self.box.pack_start(self.face_image, False, False, 10)

        self.realname_label = Gtk.Label(None)
        self.realname_label.set_alignment(0, 0.5)
        self.realname_label.set_halign(Gtk.Align.CENTER)

        self.box.pack_start(self.realname_label, False, False, 10)

        self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.box.pack_start(self.entry_box, True, True, 2)

        self.password_entry = PasswordEntry()

        trackers.con_tracker_get().connect(self.password_entry,
                                           "changed",
                                           self.on_password_entry_text_changed)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "button-press-event",
                                           self.on_password_entry_button_press)

        trackers.con_tracker_get().connect(self.password_entry,
                                           "activate",
                                           self.on_auth_enter_key)

        self.entry_box.pack_start(self.password_entry, True, True, 15)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.entry_box.pack_end(button_box, False, False, 0)

        self.auth_unlock_button = TransparentButton("screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        self.auth_unlock_button.set_sensitive(False)

        trackers.con_tracker_get().connect(self.auth_unlock_button,
                                           "clicked",
                                           self.on_unlock_clicked)

        button_box.pack_start(self.auth_unlock_button, False, False, 4)

        self.auth_switch_button = TransparentButton("screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
        trackers.con_tracker_get().connect(self.auth_switch_button,
                                           "clicked",
                                           self.on_switch_user_clicked)

        button_box.pack_start(self.auth_switch_button, False, False, 4)

        status.focusWidgets = [self.password_entry,
                               self.auth_unlock_button,
                               self.auth_switch_button]

        vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)

        self.capslock_label = Gtk.Label("")
        self.capslock_label.get_style_context().add_class("caps-message")
        self.capslock_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.capslock_label, False, False, 2)

        self.auth_message_label = Gtk.Label("")
        self.auth_message_label.get_style_context().add_class("auth-message")
        self.auth_message_label.set_alignment(0.5, 0.5)
        vbox_messages.pack_start(self.auth_message_label, False, False, 2)

        self.box.pack_start(vbox_messages, False, False, 0)

        self.real_name = utils.get_user_display_name()
        self.user_name = utils.get_user_name()

        self.update_realname_label()

        self.account_client = singletons.AccountsServiceClient
        if self.account_client.is_loaded:
            self.on_account_client_loaded(self.account_client)
        else:
            trackers.con_tracker_get().connect(self.account_client,
                                               "account-loaded",
                                               self.on_account_client_loaded)

        self.keymap = Gdk.Keymap.get_default()

        trackers.con_tracker_get().connect(self.keymap,
                                           "state-changed",
                                           self.keymap_handler)

        trackers.con_tracker_get().connect_after(self,
                                                 "notify::child-revealed",
                                                 self.on_revealed)

    def cancel(self):
        """
        Clears the auth message text if we have any.
        """
        self.auth_message_label.set_text("")

    def on_revealed(self, widget, child):
        """
        Updates the capslock state and ensures the password is cleared when we're first shown.
        """
        if self.get_child_revealed():
            self.keymap_handler(self.keymap)
        else:
            self.password_entry.set_text("")

    def queue_key_event(self, event):
        """
        Takes a propagated key event from the stage and passes it to the entry widget,
        possibly queueing up the first character of the password.
        """
        if not self.password_entry.get_realized():
            self.password_entry.realize()

        self.password_entry.event(event)

    def keymap_handler(self, keymap):
        """
        Handler for the GdkKeymap changing - updates our capslock indicator label.
        """
        if keymap.get_caps_lock_state():
            self.capslock_label.set_text(_("You have the Caps Lock key on."))
        else:
            self.capslock_label.set_text("")

    def on_account_client_loaded(self, client):
        """
        Handler for the AccountsService - requests the user real name and .face image.
        """
        if client.get_real_name() != None:
            self.real_name = client.get_real_name()
            self.update_realname_label()

        if client.get_face_path() != None:
            self.face_image.set_from_path(client.get_face_path())
            self.face_image.show()

    def on_password_entry_text_changed(self, editable):
        """
        Handler for the password entry text changing - this controls the sensitivity
        of the unlock button, as well as returning visual focus to the entry any time
        a key event is received.
        """
        if not self.password_entry.has_focus():
            self.password_entry.grab_focus()
        self.auth_unlock_button.set_sensitive(editable.get_text() != "")

    def on_password_entry_button_press(self, widget, event):
        """
        Prevents the usual copy/paste popup when right-clicking the PasswordEntry.
        """
        if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
            return Gdk.EVENT_STOP

        return Gdk.EVENT_PROPAGATE

    def on_unlock_clicked(self, button=None):
        """
        Callback for the unlock button.  Activates the 'progress' animation
        in the GtkEntry, and attempts to authenticate the password.  During this
        time, we also inhibit the unlock timeout, so we don't fade out while waiting
        for an authentication result (highly unlikely.)
        """
        self.emit("inhibit-timeout")

        text = self.password_entry.get_text()

        self.password_entry.start_progress()
        self.password_entry.set_placeholder_text (_("Checking..."))

        self.authenticate(text)

    def on_auth_enter_key(self, widget):
        """
        Implicitly activates the unlock button when the Enter/Return key is pressed.
        """
        if widget.get_text() == "":
            return

        self.on_unlock_clicked()

    def on_switch_user_clicked(self, widget):
        """
        Callback for the switch-user button.
        """
        utils.do_user_switch()

    def clear_entry(self):
        """
        Clear the password entry widget.
        """
        self.password_entry.set_text("")

    def update_realname_label(self):
        """
        Updates the name label to the current real_name.
        """
        self.realname_label.set_text(self.real_name)

    def authenticate(self, password):
        """
        Authenticates the entered password against the system PAM provider.
        """
        CinnamonDesktop.desktop_check_user_password(self.user_name,
                                                    password,
                                                    self.authenticate_callback)

    def authenticate_callback(self, success, data=None):
        """
        Callback for check_user_password.  Send this back to the Stage so it can
        react how it needs to (destroying itself or flashing the unlock widget on failure.)
        """
        self.password_entry.stop_progress()

        if success:
            self.clear_entry()
            self.emit("auth-success")
        else:
            self.authentication_failed()
            self.emit("auth-failure")

    def authentication_failed(self):
        """
        Called upon authentication failure, clears the password, sets an error message,
        and refocuses the password entry.
        """
        self.clear_entry()

        self.password_entry.set_placeholder_text (_("Please enter your password..."))
        self.auth_message_label.set_text(_("Incorrect password"))

        self.password_entry.grab_focus()

        self.emit("uninhibit-timeout")
Exemple #15
0
class PlayerControl(Gtk.Box):
    """
    Provides info and controls for any active music or other media player.  It is a
    a direct child of the AudioPanel, and is only shown if there is an active mpris
    interface we can connect to.
    """
    def __init__(self):
        super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL)

        self.watcher = singletons.MediaPlayerWatcher
        self.player = self.watcher.get_best_player()

        if self.player:
            self.build_layout()

    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")

        player_status = self.player.get_playback_status()

        # Player buttons

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton("media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button,
                                           "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button,
                                           "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic", size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button,
                                           "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [self.previous_button,
                                                     self.play_pause_button,
                                                     self.next_button]

        # Track info

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player,
                                           "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self,
                                           "destroy",
                                           self.on_widget_destroy)

    def on_previous_clicked(self, button, data=None):
        self.player.go_previous()

    def on_next_clicked(self, button, data=None):
        self.player.go_next()

    def on_play_pause_clicked(self, button, data=None):
        self.player.play_pause()

    def get_play_pause_icon_name(self, status):
        if status == PlaybackStatus.Playing:
            icon_name = "media-playback-pause-symbolic"
        else:
            icon_name = "media-playback-start-symbolic"

        return icon_name

    def on_playback_status_changed(self, player, status, data=None):
        self.update_buttons(status)

    def on_metadata_changed(self, player):
        """
        Update labels when the player metadata changes
        """
        self.update_labels()

    def update_labels(self):
        """
        Construct the track and artist-album labels as well as possible.
        """
        self.track_name_label.set_text(self.player.get_track_name())

        artist_name = self.player.get_artist_name()
        album_name = self.player.get_album_name()

        if artist_name != "" and album_name != "":
            self.album_artist_label.set_text("%s - %s" % (self.player.get_artist_name(), self.player.get_album_name()))
        elif artist_name != "":
            self.album_artist_label.set_text(artist_name)
        elif album_name != "":
            self.album_artist_label.set_text(album_name)
        else:
            self.album_artist_label.set_text("")

    def update_buttons(self, status):
        """
        Updates the player buttons based on the current state
        """
        self.play_pause_button.set_sensitive(self.player.get_can_play_pause())
        self.next_button.set_sensitive(self.player.get_can_go_next())
        self.previous_button.set_sensitive(self.player.get_can_go_previous())

        icon_name = self.get_play_pause_icon_name(status)

        size = Gtk.IconSize.from_name("audio-button")

        image = Gtk.Image.new_from_icon_name(icon_name, size)
        self.play_pause_button.set_image(image)

    def on_widget_destroy(self, widget, data=None):
        trackers.con_tracker_get().disconnect(self.player,
                                              "status-changed",
                                              self.on_playback_status_changed)

        trackers.con_tracker_get().disconnect(self,
                                              "destroy",
                                              self.on_widget_destroy)

    def should_show(self):
        """
        Checked by the AudioPanel, whether or not this widget should be displayed.
        """
        return self.player != None
Exemple #16
0
    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")

        player_status = self.player.get_playback_status()

        # Player buttons

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton("media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button,
                                           "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button,
                                           "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic", size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button,
                                           "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [self.previous_button,
                                                     self.play_pause_button,
                                                     self.next_button]

        # Track info

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player,
                                           "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player,
                                           "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self,
                                           "destroy",
                                           self.on_widget_destroy)
Exemple #17
0
class PlayerControl(Gtk.Box):
    def __init__(self):
        super(PlayerControl,
              self).__init__(orientation=Gtk.Orientation.HORIZONTAL)

        self.watcher = singletons.MediaPlayerWatcher
        self.player = self.watcher.get_best_player()

        if self.player:
            self.build_layout()

    def build_layout(self):
        size = Gtk.IconSize.from_name("audio-button")
        # size = Gtk.IconSize.MENU

        player_status = self.player.get_playback_status()

        # Player buttons

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        button_box.set_homogeneous(True)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 2)
        vbox.pack_start(button_box, True, True, 0)
        vbox.set_valign(Gtk.Align.CENTER)

        self.previous_button = TransparentButton(
            "media-skip-backward-symbolic", size)
        self.previous_button.show()
        trackers.con_tracker_get().connect(self.previous_button, "clicked",
                                           self.on_previous_clicked)

        button_box.pack_start(self.previous_button, True, True, 2)

        self.play_pause_button = TransparentButton(
            self.get_play_pause_icon_name(player_status), size)
        self.play_pause_button.show()
        trackers.con_tracker_get().connect(self.play_pause_button, "clicked",
                                           self.on_play_pause_clicked)
        button_box.pack_start(self.play_pause_button, True, True, 2)

        self.next_button = TransparentButton("media-skip-forward-symbolic",
                                             size)
        self.next_button.show()
        trackers.con_tracker_get().connect(self.next_button, "clicked",
                                           self.on_next_clicked)
        button_box.pack_start(self.next_button, True, True, 2)

        self.update_buttons(player_status)

        status.focusWidgets = status.focusWidgets + [
            self.previous_button, self.play_pause_button, self.next_button
        ]

        # Position labels and bar

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 4)

        vbox.set_valign(Gtk.Align.CENTER)

        position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        vbox.pack_start(position_length_box, True, True, 2)

        self.current_pos_label = BlinkingLabel("", 400)
        self.current_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_start(self.current_pos_label, False, False, 2)

        self.max_pos_label = BlinkingLabel("", 400)
        self.max_pos_label.get_style_context().add_class("positionlabel")
        position_length_box.pack_end(self.max_pos_label, False, False, 2)

        self.position_bar = PositionBar()
        vbox.pack_end(self.position_bar, True, True, 2)

        # Track info

        self.pack_start(Gtk.VSeparator(), True, True, 2)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.pack_start(vbox, True, True, 6)

        vbox.set_valign(Gtk.Align.CENTER)

        self.track_name_label = MarqueeLabel("")
        self.track_name_label.get_style_context().add_class("trackname")
        vbox.pack_start(self.track_name_label, True, True, 2)

        self.album_artist_label = MarqueeLabel("")
        self.album_artist_label.get_style_context().add_class("albumartist")
        vbox.pack_end(self.album_artist_label, True, True, 2)

        self.show_all()

        trackers.con_tracker_get().connect(self.player, "position-changed",
                                           self.on_position_changed)

        trackers.con_tracker_get().connect(self.player, "status-changed",
                                           self.on_playback_status_changed)

        trackers.con_tracker_get().connect(self.player, "metadata-changed",
                                           self.on_metadata_changed)

        self.on_playback_status_changed(self.player, player_status)
        self.on_metadata_changed(self.player)

        trackers.con_tracker_get().connect(self, "destroy",
                                           self.on_widget_destroy)

        self.update_position_timer(player_status)

    def on_previous_clicked(self, button, data=None):
        self.player.go_previous()

    def on_next_clicked(self, button, data=None):
        self.player.go_next()

    def on_play_pause_clicked(self, button, data=None):
        self.player.play_pause()

    def get_play_pause_icon_name(self, status):
        if status == PlaybackStatus.Playing:
            icon_name = "media-playback-pause-symbolic"
        else:
            icon_name = "media-playback-start-symbolic"

        return icon_name

    def position_to_time_string(self, position):
        delta = datetime.timedelta(microseconds=position)

        duration = datetime.datetime.utcfromtimestamp(delta.total_seconds())

        if duration.hour < 0:
            return _("--:--")

        if duration.hour < 1:
            return duration.strftime(_("%M:%S"))
        else:
            return duration.strftime(_("%H:%M:%S"))

    def on_playback_status_changed(self, player, status, data=None):
        self.update_buttons(status)
        self.update_position_timer(status)
        self.update_position_display()

        self.update_position_values_appearance(status)

    def update_position_values_appearance(self, status):
        if status == PlaybackStatus.Paused:
            self.max_pos_label.set_blinking(True)
            self.current_pos_label.set_blinking(True)
        else:
            self.max_pos_label.set_blinking(False)
            self.current_pos_label.set_blinking(False)

    def on_metadata_changed(self, player):
        self.max_pos_label.set_text(
            self.position_to_time_string(self.player.get_max_position()))
        self.update_labels()

    def update_labels(self):
        self.track_name_label.set_text(self.player.get_track_name())

        artist_name = self.player.get_artist_name()
        album_name = self.player.get_album_name()

        if artist_name != "" and album_name != "":
            self.album_artist_label.set_text(
                "%s - %s" %
                (self.player.get_artist_name(), self.player.get_album_name()))
        elif artist_name != "":
            self.album_artist_label.set_text(artist_name)
        elif album_name != "":
            self.album_artist_label.set_text(album_name)
        else:
            self.album_artist_label.set_text("")

    def pause_blink_step(self):
        self.max_pos_label.set_visible(not self.max_pos_label.get_visible())
        self.current_pos_label.set_visible(
            not self.current_pos_label.get_visible())

        return True

    def update_buttons(self, status):
        self.play_pause_button.set_sensitive(self.player.get_can_play_pause())
        self.next_button.set_sensitive(self.player.get_can_go_next())
        self.previous_button.set_sensitive(self.player.get_can_go_previous())

        icon_name = self.get_play_pause_icon_name(status)

        size = Gtk.IconSize.from_name("audio-button")

        image = Gtk.Image.new_from_icon_name(icon_name, size)
        self.play_pause_button.set_image(image)

    def update_position_display(self):
        if self.player.get_position() < self.player.get_max_position():
            value = self.player.get_position() / self.player.get_max_position()
        else:
            value = 1.0
        value = utils.CLAMP(value, 0.0, 1.0)
        self.position_bar.set_fraction(value)

        self.current_pos_label.set_text(
            self.position_to_time_string(self.player.get_position()))

        return True

    def update_position_timer(self, status):
        if status == PlaybackStatus.Playing:
            trackers.timer_tracker_get().start("position-timer",
                                               self.player.get_rate() * 1000,
                                               self.update_position_display)
        else:
            trackers.timer_tracker_get().cancel("position-timer")

    def on_position_changed(self, player, position, data=None):
        self.update_position_display()

    def on_widget_destroy(self, widget, data=None):
        trackers.con_tracker_get().disconnect(self.player, "position-changed",
                                              self.on_position_changed)

        trackers.con_tracker_get().disconnect(self.player, "status-changed",
                                              self.on_playback_status_changed)

        trackers.con_tracker_get().disconnect(self, "destroy",
                                              self.on_widget_destroy)

        trackers.timer_tracker_get().cancel("position-timer")

    def should_show(self):
        return self.player != None