Beispiel #1
0
    def __init__(self, filename=None):
        self.__internal_window = curses.newwin(curses.LINES - 1, curses.COLS, 0, 0) # ht, wd, begin_y, begin_x
        self.__internal_window.keypad(True)
        self.__maxy, self.__maxx = self.__internal_window.getmaxyx()

        self.__mini_window = MiniWindow()
        self.__buffer = Buffer(filename)
        self.__current_cursor_coordinates = ()

        # represents the first line of the internal window
        self.__top = 0

        # represents the last line of the internal window
        self.__bottom = curses.LINES - 1

        self.__left = 0

        self.__right = curses.COLS

        # since current_line_number is used to index through the contents, it is initialized to 0
        self.__current_line_number = 0

        self.__current_line_character_number = 0

        self.open_file_or_create_it()
 def __make_notebook_window(self, notebook):
     if global_settings.mini_mode:
         global MiniWindow
         from mini_window import MiniWindow
         return MiniWindow(notebook)
     else:
         global NotebookWindow
         from notebook_window import NotebookWindow
         return NotebookWindow(notebook)
Beispiel #3
0
class Window:
    logger = pyscium_logger.get_logger(__name__, 'window.log')

    def __init__(self, filename=None):
        self.__internal_window = curses.newwin(curses.LINES - 1, curses.COLS, 0, 0) # ht, wd, begin_y, begin_x
        self.__internal_window.keypad(True)
        self.__maxy, self.__maxx = self.__internal_window.getmaxyx()

        self.__mini_window = MiniWindow()
        self.__buffer = Buffer(filename)
        self.__current_cursor_coordinates = ()

        # represents the first line of the internal window
        self.__top = 0

        # represents the last line of the internal window
        self.__bottom = curses.LINES - 1

        self.__left = 0

        self.__right = curses.COLS

        # since current_line_number is used to index through the contents, it is initialized to 0
        self.__current_line_number = 0

        self.__current_line_character_number = 0

        self.open_file_or_create_it()

    def get_current_cursor_coordinates(self):
        return self.__current_cursor_coordinates

    def set_current_cursor_coordinates(self):
        self.__current_cursor_coordinates = curses.getsyx()

    def increment_top(self):
        self.__top += 1

    def decrement_top(self):
        if self.__top > 0:
            self.__top -= 1

    def increment_bottom(self):
        self.__bottom += 1

    def decrement_bottom(self):
        self.__bottom -= 1

    def set_left(self, left):
        self.__left = left

    def set_right(self, right):
        self.__right = right

    def increment_left(self):
        self.__left += 1

    def decrement_left(self):
        self.__left -= 1

    def increment_right(self):
        self.__right += 1

    def decrement_right(self):
        self.__right -= 1

    def get_current_line_number(self):
        return self.__current_line_number

    def increment_current_line_number(self):
        self.__current_line_number += 1

    def decrement_current_line_number(self):
        self.__current_line_number -= 1

    def get_current_line_character_number(self):
        return self.__current_line_character_number

    def set_current_line_character_number(self, character_number):
        self.__current_line_character_number = character_number

    def increment_current_line_character_number(self):
        self.__current_line_character_number += 1

    def decrement_current_line_character_number(self):
        self.__current_line_character_number -= 1

    def get_internal_window(self):
        return self.__internal_window

    def move_to_beginning_of_line(self):
        y, x = self.__internal_window.getyx()
        maxy, maxx = self.__internal_window.getmaxyx()

        self.set_left(0)
        self.set_right(maxx)
        self.set_current_line_character_number(0)

        self.display_buffer_contents()
        self.__internal_window.move(y, 0)

    def move_to_end_of_line(self):
        y, x = self.__internal_window.getyx()
        maxy, maxx = self.__internal_window.getmaxyx()
        current_line_number = self.get_current_line_number()

        current_line_length = self.__buffer.get_length_of_line(current_line_number)

        if current_line_length > maxx:
            self.set_left(current_line_length - maxx)
            self.set_right(current_line_length)
            self.display_buffer_contents()
            self.__internal_window.move(y, maxx - 1)

        else:
            self.display_buffer_contents()

            if current_line_length  > 0:
                self.__internal_window.move(y, current_line_length - 1)
            elif current_line_length == 0:
                self.__internal_window.move(y, current_line_length)

        self.set_current_line_character_number(current_line_length - 1)

    def move_forward_one_char(self):
        y, x = self.__internal_window.getyx()
        maxy, maxx = self.__internal_window.getmaxyx()
        buffer_end_y, buffer_end_x = self.__buffer.get_buffer_end()
        current_line_number = self.get_current_line_number()

        current_line_character_number = self.get_current_line_character_number()
        x_of_last_character_of_line_on_y = self.__buffer.get_length_of_line(current_line_number) - 1

        if x < maxx - 1 and current_line_character_number < x_of_last_character_of_line_on_y:
            self.__internal_window.move(y, x + 1)
            self.increment_current_line_character_number()

        elif x == maxx - 1 and current_line_character_number < x_of_last_character_of_line_on_y:
            self.increment_left()
            self.increment_right()
            self.increment_current_line_character_number()
            self.display_buffer_contents()

            self.__internal_window.move(y, maxx - 1)

        elif current_line_number == buffer_end_y:
            curses.beep()

        else:
            self.move_to_next_line()

    def move_backward_one_char(self):
        y, x = self.__internal_window.getyx()
        buffer_start_y, buffer_start_x = self.__buffer.get_buffer_start()
        current_line_number = self.get_current_line_number()
        current_line_character_number = self.get_current_line_character_number()

        if x > 0:
            self.__internal_window.move(y, x - 1)
            self.decrement_current_line_character_number()

        elif x == 0 and current_line_character_number > 0:
            self.decrement_left()
            self.decrement_right()
            self.decrement_current_line_character_number()
            self.display_buffer_contents()

            self.__internal_window.move(y, 0)

        elif x == 0 and current_line_character_number == 0:
            self.move_to_previous_line()
            self.move_to_end_of_line()
            current_line_number = self.get_current_line_number()
            current_line_character_number = self.__buffer.get_length_of_line(current_line_number) - 1
            self.set_current_line_character_number(current_line_character_number)

        elif current_line_number == buffer_start_y:
            curses.beep()

    def move_to_next_line(self):
        y, x = self.__internal_window.getyx()
        maxy, maxx = self.__internal_window.getmaxyx()
        current_line_number = self.get_current_line_number()
        buffer_end_y, buffer_end_x = self.__buffer.get_buffer_end()

        if current_line_number != buffer_end_y:
            next_line_y = y + 1
            main_window_height = curses.LINES - 1
            if next_line_y != main_window_height:
                self.set_left(0)
                self.set_right(maxx)
                self.set_current_line_character_number(0)

                self.display_buffer_contents()
                self.__internal_window.move(y + 1, 0)
            elif next_line_y == main_window_height:
                self.increment_top()
                self.increment_bottom()

                self.set_left(0)
                self.set_right(maxx)
                self.set_current_line_character_number(0)

                self.display_buffer_contents()
                self.__internal_window.move(y, 0)

            if current_line_number < buffer_end_y:
                self.increment_current_line_number()
        else:
            curses.beep()

    def move_to_previous_line(self):
        y, x = self.__internal_window.getyx()

        current_line_number = self.get_current_line_number()
        (buffer_start_y, buffer_start_x) = self.__buffer.get_buffer_start()
        if current_line_number != buffer_start_y:

            prev_line_y = y - 1

            if prev_line_y == -1:
                self.decrement_top()
                self.decrement_bottom()

                self.display_buffer_contents()
                self.__internal_window.move(0, 0)

            else:
                self.__internal_window.move(y - 1, 0)

            if current_line_number > buffer_start_y:
                self.decrement_current_line_number()

        else:
            curses.beep()

    def backspace(self):
        y, x = self.__internal_window.getyx()
        maxy, maxx = self.__internal_window.getmaxyx()
        current_line_number = self.get_current_line_number()
        current_line_character_number = self.get_current_line_character_number()
        length_of_line_to_append_to = self.__buffer.get_length_of_line(current_line_number - 1)
        prev_line_y = y - 1

        if y > 0 and x > 0:
            self.move_backward_one_char()
            self.__internal_window.delch()
            self.__buffer.delete_character(current_line_number, current_line_character_number - 1)

            self.display_buffer_contents()
            self.__internal_window.move(y, x - 1)
        elif y > 0 and x == 0:
            if length_of_line_to_append_to > maxx:
                self.set_left(length_of_line_to_append_to - 1 - maxx // 2)
                self.set_right(length_of_line_to_append_to - 1 + maxx // 2)

                self.__buffer.remove_line_and_append_at_position(current_line_number, current_line_number - 1)
                self.set_current_line_character_number(length_of_line_to_append_to - 1)
                self.decrement_current_line_number()

                self.display_buffer_contents()
                self.__internal_window.move(prev_line_y, maxx // 2)
            else:
                self.__buffer.remove_line_and_append_at_position(current_line_number, current_line_number - 1)
                self.set_current_line_character_number(length_of_line_to_append_to - 1)
                self.decrement_current_line_number()

                self.display_buffer_contents()
                self.__internal_window.move(prev_line_y, length_of_line_to_append_to - 1)
        elif y == 0 and x > 0:
            self.move_backward_one_char()
            self.__internal_window.delch()
            self.__buffer.delete_character(current_line_number, current_line_character_number - 1)

            self.display_buffer_contents()
            self.__internal_window.move(y, x - 1)
        elif current_line_character_number > 0 and x == 0:
            self.decrement_left()
            self.decrement_right()
            self.decrement_current_line_character_number()

            self.display_buffer_contents()
            self.__internal_window.move(y, 0)
        elif y == 0 and current_line_number != 0:
            self.set_left(length_of_line_to_append_to - 1 - maxx // 2)
            self.set_right(length_of_line_to_append_to - 1 + maxx // 2)

            self.__buffer.remove_line_and_append_at_position(current_line_number, current_line_number - 1)
            self.set_current_line_character_number(length_of_line_to_append_to)
            self.decrement_current_line_number()

            self.decrement_top()
            self.decrement_bottom()

            self.display_buffer_contents()
            self.__internal_window.move(prev_line_y, maxx // 2)
        elif current_line_number == 0 and x == 0:
            curses.beep()

    def newline(self):
        newline = 10
        main_window_height = curses.LINES - 1
        maxy, maxx = self.__internal_window.getmaxyx()
        y, x = self.__internal_window.getyx()
        next_line_y = y + 1
        next_line_x = 0
        current_line_number = self.get_current_line_number()
        current_line_character_number = self.get_current_line_character_number()
        self.__internal_window.insch(newline)
        self.__buffer.ins_ch(newline, current_line_number, current_line_character_number)

        self.__buffer.move_current_line_to_next_line(current_line_number, current_line_character_number + 1)
        self.set_left(0)
        self.set_right(maxx)
        self.set_current_line_character_number(0)
        self.increment_current_line_number()

        if next_line_y == main_window_height:
            self.increment_top()
            self.increment_bottom()

        self.display_buffer_contents()

        if next_line_y != main_window_height:
            self.__internal_window.move(next_line_y, next_line_x)

        else:
            self.__internal_window.move(y, 0)

    def ins_ch(self, ch):
        y, x = self.__internal_window.getyx()
        current_line_number = self.get_current_line_number()
        current_line_character_number = self.get_current_line_character_number()
        self.__internal_window.insch(ch)
        self.__buffer.ins_ch(ch, current_line_number, current_line_character_number)

        self.move_forward_one_char()

    def display_buffer_contents(self):
        self.__internal_window.erase()

        data = self.__buffer.get_contents()
        try:
            for index, line in enumerate(data[self.__top : self.__bottom]):
                string = ''.join(line[self.__left : self.__right])

                if string:
                    self.__internal_window.addstr(string)
                else:
                    self.__internal_window.addstr('\n')

        except TypeError as e:
            Window.logger.exception(e)

        except curses.error as e:
            Window.logger.exception(e)

    def open_file_or_create_it(self):
        filename = self.__buffer.get_file_name()

        if filename is not None:
            if Path(filename).is_file():
                self.display_buffer_contents()
                self.__internal_window.move(0, 0)

            else:
                file_util.create_file(self.__buffer.get_file_name())

    def save_file(self):
        self.set_current_cursor_coordinates()

        if self.__buffer.get_file_name() is not None:

            if self.__buffer.get_is_modified():
                self.__buffer.save_file()
                self.__mini_window.display_message_in_mini_buffer('Changes saved')
                self.restore_cursor()
            else:
                self.__mini_window.display_message_in_mini_buffer('No changes need to be saved')
                self.restore_cursor()

        else:
            filename = self.__mini_window.get_file_name()
            if filename is not None:

                file_path = Path(filename)
                if file_path.parent.exists():
                    file_util.create_file(file_path)
                    self.__buffer.set_file_name(file_path)
                    self.__buffer.save_file()
                    self.__mini_window.display_message_in_mini_buffer('Changes saved')

            self.restore_cursor()

    def close_file(self):
        if self.__buffer.get_file_name() is not None:
            file_util.close_file(self.__buffer.get_file_name())

    def restore_cursor(self):
        current_cursor_coordinates = self.get_current_cursor_coordinates()
        self.__internal_window.move(current_cursor_coordinates[0],
                                    current_cursor_coordinates[1])
class DoubanFMPlugin(GObject.Object, Peas.Activatable, PeasGtk.Configurable):
	__gtype_name__ = 'DoubanFMPlugin'
	object = GObject.Property(type=GObject.Object)
	
	def __init__(self):
		super(DoubanFMPlugin, self).__init__()
		self.current_song = None
		self.config_dialog = None
		self.channels_sidebar = None
		self.mini_window = None

	def do_create_configure_widget(self):
		"""
		Show a dialog when the preferrence button is being clicked.
		"""
		if self.config_dialog == None:
			self.config_dialog = ConfigDialog()
		return self.config_dialog.config_box

	def do_activate(self):
		"""
		Do some initialization work when the plugin is being activated.
		"""
		self.shell = self.object
		self.player = self.shell.props.shell_player

		# create and register an entry type
		self.db = self.shell.props.db
		self.entry_type = DoubanFMEntryType()
		self.db.register_entry_type(self.entry_type)
		self.entry_type.can_sync_metadata = True
		self.entry_type.sync_metadata = None

		# find and load doubanfm icon
		theme = Gtk.IconTheme.get_default()
		width, height = Gtk.icon_size_lookup(Gtk.IconSize.LARGE_TOOLBAR)[1:]
		icon = theme.load_icon('doubanfm', width, 0)

		# create a source under 'stores' group
		group = RB.DisplayPageGroup.get_by_id ('stores')
		self.source = GObject.new(DoubanFMSource, shell=self.shell,
			entry_type=self.entry_type, pixbuf=icon, plugin=self)
		self.shell.register_entry_type_for_source(self.source, self.entry_type)
		self.shell.append_display_page(self.source, group)

		# create ui, connect ui to actions, and add them to ui manager
		self.ui_manager = self.shell.props.ui_manager
		self.build_actions()
		self.ui_manager.insert_action_group(self.action_group)
		self.ui_merge_id = self.ui_manager.add_ui_from_file(PLUGIN_DIR + UI_FILE)
		self.change_menu_item_state(False)
		self.ui_manager.ensure_update()

		# connect signals
		self.player.connect('playing-source-changed', self.on_playing_source_changed)
		self.set_handle_signals(True)
		
	def do_deactivate(self):
		"""
		Do some clean work when the plugin is being deactivated.
		"""
		self.ui_manager.remove_ui(self.ui_merge_id)
		self.ui_manager.remove_action_group(self.action_group)
		self.ui_manager.ensure_update()
		self.action_group = None
		self.actions = None
		self.ui_manager = None
		self.db.commit()
		self.db = None
		self.entry_type = None
		self.source.delete_thyself()
		self.source = None
		self.player = None
		self.handlers = None
		self.shell = None

	def set_handle_signals(self, handle):
		if handle:
			self.handlers = [
				self.player.connect('playing-song-changed', self.on_playing_song_changed),
				self.player.connect('elapsed-changed', self.on_elapsed_changed)
			]
		else:
			for handler in self.handlers:
				self.player.disconnect(handler)

	def build_actions(self):
		"""
		create actions and add them to action group.
		"""
		self.actions = [
			Gtk.Action('FMMenu', '豆瓣FM(_D)', None, None),
			Gtk.Action('FavSong', '喜欢(_F)', None, None),
			Gtk.Action('DelSong', '不再播放(_D)', None, None),
			Gtk.Action('SkipSong', '跳过(_S)', None, None),
			Gtk.Action('NewPlaylist', '刷新列表(_N)', None, None),
			Gtk.Action('ShowMiniWindow', 'Mini窗口(_M)', None, Gtk.STOCK_LEAVE_FULLSCREEN)
		]
		methods = [self.on_fav_song, self.on_del_song, self.on_skip_song,
			self.on_new_playlist, self.show_mini_window]

		for (action, method) in zip(self.actions[1:], methods):
			action.connect('activate', method)

		self.action_group = Gtk.ActionGroup('DoubanFMActions')
		for action in self.actions:
			self.action_group.add_action(action)

	def change_menu_item_state(self, state):
		for action in self.actions[1:]:
			action.set_sensitive(state)

	def build_submenu(self, menu):
		channels = self.source.doubanfm.channels
		for channel_id in sorted(channels):
			sub_item = Gtk.MenuItem(channels[channel_id])
			sub_item.connect('activate', self.on_set_channel, channel_id)
			menu.append(sub_item)
			sub_item.show()
	
	def show_sidebar(self, show):
		if show:
			if self.channels_sidebar == None:
				self.channels_sidebar = ChannelsSidebar(self.source)
			self.shell.add_widget(self.channels_sidebar.channels_box,
				RB.ShellUILocation.RIGHT_SIDEBAR,
				False, True)
		else:
			self.shell.remove_widget(self.channels_sidebar.channels_box,
				RB.ShellUILocation.RIGHT_SIDEBAR)

	def show_mini_window(self, action):
		if self.mini_window == None:
			self.mini_window = MiniWindow(self)
		self.mini_window.set_visibile(True)

	def on_set_channel(self, action, channel_id):
		GLib.idle_add(self.source.set_channel, channel_id)

	def on_fav_song(self, action):
		self.actions[1].set_label('喜欢(_F)' if self.current_song.like else '取消喜欢(_U)')
		GLib.idle_add(self.source.unfav_song if self.current_song.like else
				self.source.fav_song, self.current_song)

	def on_del_song(self, action):
		GLib.idle_add(self.source.del_song, self.current_song)

	def on_skip_song(self, action):
		GLib.idle_add(self.source.skip_song, self.current_song)

	def on_new_playlist(self, action):
		GLib.idle_add(self.source.new_playlist)

	def initialize(self):
		entry = self.player.get_playing_entry()
		self.on_playing_song_changed(self.player, entry)

	def on_playing_source_changed(self, player, source):
		source_match = source is self.source
		self.change_menu_item_state(source_match)
		self.set_handle_signals(source_match)

	def on_playing_song_changed(self, player, entry):
		if entry != None:
			song_title = entry.get_string(RB.RhythmDBPropType.TITLE)
			self.current_song = self.source.get_song_by_title(song_title)
			self.actions[1].set_label('取消喜欢(_U)' if self.current_song.like else '喜欢(_F)')
		else:
			GLib.idle_add(self.source.new_playlist)

	def on_elapsed_changed(self, player, elapsed):
		if self.current_song != None:
			if elapsed == self.current_song.length:
				GLib.idle_add(self.source.played_song, self.current_song)
	def show_mini_window(self, action):
		if self.mini_window == None:
			self.mini_window = MiniWindow(self)
		self.mini_window.set_visibile(True)
class DoubanFMPlugin(GObject.Object, Peas.Activatable, PeasGtk.Configurable):
	__gtype_name__ = 'DoubanFMPlugin'
	object = GObject.Property(type=GObject.Object)
	
	def __init__(self):
		super(DoubanFMPlugin, self).__init__()
		self.current_song = None
		self.config_dialog = None
		self.channels_sidebar = None
		self.mini_window = None

	def do_create_configure_widget(self):
		if self.config_dialog == None:
			self.config_dialog = ConfigDialog()
		return self.config_dialog.config_box

	def do_activate(self):
		self.shell = self.object
		self.player = self.shell.props.shell_player

		# create and register an entry type
		self.db = self.shell.props.db
		self.entry_type = DoubanFMEntryType()
		self.db.register_entry_type(self.entry_type)
		self.entry_type.can_sync_metadata = True
		self.entry_type.sync_metadata = None

		# load doubanfm icon from file
		icon = GdkPixbuf.Pixbuf.new_from_file(PLUGIN_DIR + ICON_FILE)

		# create a source under 'stores' group
		group = RB.DisplayPageGroup.get_by_id ('stores')
		self.source = GObject.new(DoubanFMSource, shell=self.shell,
			entry_type=self.entry_type, pixbuf=icon, plugin=self)
		self.shell.register_entry_type_for_source(self.source, self.entry_type)
		self.shell.append_display_page(self.source, group)

		# create ui, connect ui to actions, and add them to ui manager
		self.ui_manager = self.shell.props.ui_manager
		self.build_actions()
		self.ui_manager.insert_action_group(self.action_group)
		self.ui_merge_id = self.ui_manager.add_ui_from_file(PLUGIN_DIR + UI_FILE)
		self.change_menu_item_state(False)
		self.ui_manager.ensure_update()

		# connect signals
		self.player.connect('playing-source-changed', self.on_playing_source_changed)
		self.set_handle_signals(True)
		
	def do_deactivate(self):
		self.ui_manager.remove_ui(self.ui_merge_id)
		self.ui_manager.remove_action_group(self.action_group)
		self.ui_manager.ensure_update()
		self.action_group = None
		self.actions = None
		self.ui_manager = None
		self.db.commit()
		self.db = None
		self.entry_type = None
		self.source.delete_thyself()
		self.source = None
		self.player = None
		self.handlers = None
		self.shell = None

	def set_handle_signals(self, handle):
		if handle:
			self.handlers = [
				self.player.connect('playing-song-changed', self.on_playing_song_changed),
				self.player.connect('elapsed-changed', self.on_elapsed_changed)
			]
		else:
			for handler in self.handlers:
				self.player.disconnect(handler)

	def build_actions(self):
		self.actions = [
			Gtk.Action('FMMenu', '豆瓣 FM(_D)', None, None),
			Gtk.Action('FavSong', '喜欢(_F)', None, None),
			Gtk.Action('DelSong', '不再播放(_D)', None, None),
			Gtk.Action('SkipSong', '跳过(_S)', None, None),
			Gtk.Action('NewPlaylist', '刷新列表(_N)', None, None),
			Gtk.Action('ShowMiniWindow', 'Mini窗口(_M)', None, Gtk.STOCK_LEAVE_FULLSCREEN)
		]
		methods = [self.on_fav_song, self.on_del_song, self.on_skip_song,
			self.on_new_playlist, self.show_mini_window]

		for (action, method) in zip(self.actions[1:], methods):
			action.connect('activate', method)

		self.action_group = Gtk.ActionGroup('DoubanFMActions')
		for action in self.actions:
			self.action_group.add_action(action)

	def change_menu_item_state(self, state):
		for action in self.actions[1:]:
			action.set_sensitive(state)

	def build_submenu(self, menu):
		channels = self.source.doubanfm.channels
		for channel_id in sorted(channels):
			sub_item = Gtk.MenuItem(channels[channel_id])
			sub_item.connect('activate', self.on_set_channel, channel_id)
			menu.append(sub_item)
			sub_item.show()
	
	def show_sidebar(self, show):
		if show:
			if self.channels_sidebar == None:
				self.channels_sidebar = ChannelsSidebar(self.source)
			self.shell.add_widget(self.channels_sidebar.channels_box,
				RB.ShellUILocation.RIGHT_SIDEBAR,
				False, True)
		else:
			self.shell.remove_widget(self.channels_sidebar.channels_box,
				RB.ShellUILocation.RIGHT_SIDEBAR)

	def show_mini_window(self, action):
		if self.mini_window == None:
			self.mini_window = MiniWindow(self)
		self.mini_window.set_visibile(True)

	def on_set_channel(self, action, channel_id):
		self.source.set_channel(channel_id)

	def on_fav_song(self, action):
		if self.current_song.like:
			self.actions[1].set_label('喜欢(_F)')
		 	self.source.unfav_song(self.current_song)
		else:
			self.actions[1].set_label('取消喜欢(_U)')
			self.source.fav_song(self.current_song)

	def on_del_song(self, action):
		self.source.del_song(self.current_song)

	def on_skip_song(self, action):
		self.source.skip_song(self.current_song)

	def on_new_playlist(self, action):
		self.source.new_playlist()

	def initialize(self):
		entry = self.player.get_playing_entry()
		self.on_playing_song_changed(self.player, entry)

	def on_playing_source_changed(self, player, source):
		source_match = source is self.source
		self.change_menu_item_state(source_match)
		self.set_handle_signals(source_match)

	def on_playing_song_changed(self, player, entry):
		if entry != None:
			song_title = entry.get_string(RB.RhythmDBPropType.TITLE)
			self.current_song = self.source.get_song_by_title(song_title)
			self.actions[1].set_label('取消喜欢(_U)' if self.current_song.like else '喜欢(_F)')
		else:
			self.source.new_playlist()

	def on_elapsed_changed(self, player, elapsed):
		if self.current_song != None:
			if elapsed == self.current_song.length:
				self.source.played_song(self.current_song)
	def show_mini_window(self, action):
		if self.mini_window == None:
			self.mini_window = MiniWindow(self)
		self.mini_window.set_visibile(True)