from gettext import gettext as _ from apostrophe.helpers import user_action class FormatInserter: """Manages insertion of formatting. Methods can be called directly, as well as be used as signal callbacks.""" def insert_italic(self, text_view, _data=None): """Insert italic or mark a selection as bold""" self.__wrap(text_view, "_", _("italic text")) def insert_bold(self, text_view, _data=None): """Insert bold or mark a selection as bold""" self.__wrap(text_view, "**", _("bold text")) def insert_strikethrough(self, text_view, _data=None): """Insert strikethrough or mark a selection as strikethrough""" self.__wrap(text_view, "~~", _("strikethrough text")) def insert_horizontal_rule(self, text_view, _data=None): """Insert horizontal rule""" text_buffer = text_view.get_buffer() with user_action(text_buffer): text_buffer.insert_at_cursor("\n\n---\n") text_view.scroll_mark_onscreen(text_buffer.get_insert()) def insert_list_item(self, text_view, _data=None): """Insert list item or mark a selection as list item""" text_buffer = text_view.get_buffer() if text_buffer.get_has_selection(): (start, end) = text_buffer.get_selection_bounds() if start.starts_line(): with user_action(text_buffer): text = text_buffer.get_text(start, end, False) if text.startswith(("- ", "* ", "+ ")): delete_end = start.copy() delete_end.forward_chars(2) text_buffer.delete(start, delete_end) else: text_buffer.insert(start, "- ") else: helptext = _("Item") text_length = len(helptext) cursor_mark = text_buffer.get_insert() cursor_iter = text_buffer.get_iter_at_mark(cursor_mark) start_ext = cursor_iter.copy() start_ext.backward_lines(3) text = text_buffer.get_text(cursor_iter, start_ext, False) lines = text.splitlines() with user_action(text_buffer): for line in reversed(lines): if line and line.startswith(("- ", "* ", "+ ")): if cursor_iter.starts_line(): text_buffer.insert_at_cursor(line[:2] + helptext) else: text_buffer.insert_at_cursor("\n" + line[:2] + helptext) break else: if not lines[-1] and not lines[-2]: text_buffer.insert_at_cursor("- " + helptext) elif not lines[-1]: if cursor_iter.starts_line(): text_buffer.insert_at_cursor("- " + helptext) else: text_buffer.insert_at_cursor("\n- " + helptext) else: text_buffer.insert_at_cursor("\n\n- " + helptext) break self.__select_text(text_view, 0, text_length) def insert_ordered_list_item(self, _text_view, _data=None): # TODO: implement ordered lists pass def insert_header(self, text_view, _data=None): """Insert header or mark a selection as a list header""" text_buffer = text_view.get_buffer() with user_action(text_buffer): if text_buffer.get_has_selection(): (start, end) = text_buffer.get_selection_bounds() text = text_buffer.get_text(start, end, False) text_buffer.delete(start, end) else: text = _("Header") text_buffer.insert_at_cursor("#" + " " + text) self.__select_text(text_view, 0, len(text)) @staticmethod def __wrap(text_view, wrap, helptext=""): """Inserts wrap format to the selected text (helper text when nothing selected)""" text_buffer = text_view.get_buffer() with user_action(text_buffer): if text_buffer.get_has_selection(): # Find current highlighting (start, end) = text_buffer.get_selection_bounds() moved = False if (start.get_offset() >= len(wrap) and end.get_offset() <= text_buffer.get_char_count() - len(wrap)): moved = True ext_start = start.copy() ext_start.backward_chars(len(wrap)) ext_end = end.copy() ext_end.forward_chars(len(wrap)) text = text_buffer.get_text(ext_start, ext_end, True) else: text = text_buffer.get_text(start, end, True) if moved and text.startswith(wrap) and text.endswith(wrap): text = text[len(wrap):-len(wrap)] new_text = text text_buffer.delete(ext_start, ext_end) move_back = 0 else: if moved: text = text[len(wrap):-len(wrap)] new_text = text.lstrip().rstrip() text = text.replace(new_text, wrap + new_text + wrap) text_buffer.delete(start, end) move_back = len(wrap) text_buffer.insert_at_cursor(text) text_length = len(new_text) else: text_buffer.insert_at_cursor(wrap + helptext + wrap) text_length = len(helptext) move_back = len(wrap) cursor_mark = text_buffer.get_insert() cursor_iter = text_buffer.get_iter_at_mark(cursor_mark) cursor_iter.backward_chars(move_back) text_buffer.move_mark_by_name('selection_bound', cursor_iter) cursor_iter.backward_chars(text_length) text_buffer.move_mark_by_name('insert', cursor_iter) @staticmethod def __select_text(text_view, offset, length): """Selects text starting at the current cursor minus offset, length characters.""" text_buffer = text_view.get_buffer() cursor_mark = text_buffer.get_insert() cursor_iter = text_buffer.get_iter_at_mark(cursor_mark) cursor_iter.backward_chars(offset) text_buffer.move_mark_by_name('selection_bound', cursor_iter) cursor_iter.backward_chars(length) text_buffer.move_mark_by_name('insert', cursor_iter)