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)
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)