def insert_abbreviation(self, text, title): # escape HTML text = utility.escape_html_chars(text) title = utility.escape_html_chars(title) # unicode assert isinstance(text, unicode) assert isinstance(title, unicode) # create new tag div_id = "".join(random.choice(string.ascii_letters) for _ in xrange(20)) self.editor_instance.web.eval("""\ var abbr = document.createElement('abbr'); var text = document.createTextNode('%s'); abbr.appendChild(text); abbr.setAttribute('title', '%s'); abbr.id = '%s'; var marker = '@#!'; var toBeInserted = marker + abbr.outerHTML + marker; document.execCommand('insertHTML', false, toBeInserted); var elem = document.getElementById('%s'); var leftString = elem.previousSibling.nodeValue; var rightString = elem.nextSibling.nodeValue; elem.previousSibling.nodeValue = leftString.substring(0, (leftString.length - marker.length)); elem.nextSibling.nodeValue = rightString.substring(marker.length); elem.removeAttribute('id'); """ % (text, title, div_id, div_id))
def insert_abbreviation(self, text, title): # escape HTML text = utility.escape_html_chars(text) title = utility.escape_html_chars(title) # unicode # assert isinstance(text, unicode) # assert isinstance(title, unicode) # create new tag div_id = "".join( random.choice(string.ascii_letters) for _ in xrange(20)) self.editor_instance.web.eval("""\ var abbr = document.createElement('abbr'); var text = document.createTextNode('%s'); abbr.appendChild(text); abbr.setAttribute('title', '%s'); abbr.id = '%s'; var marker = '@#!'; var toBeInserted = marker + abbr.outerHTML + marker; document.execCommand('insertHTML', false, toBeInserted); var elem = document.getElementById('%s'); var leftString = elem.previousSibling.nodeValue; var rightString = elem.nextSibling.nodeValue; elem.previousSibling.nodeValue = leftString.substring(0, (leftString.length - marker.length)); elem.nextSibling.nodeValue = rightString.substring(marker.length); elem.removeAttribute('id'); """ % (text, title, div_id, div_id))
def start(self): self.backup_html = self.html # definition lists have some quirks has_def_list = False if "<dl>" in self.html: has_def_list = True self.create_correct_md_for_def_list() self.html = self.note.fields[self.current_field] # first, we reverse engineer the Markdown from the rendered card clean_md = utility.strip_html_from_markdown(self.html) if has_def_list: clean_md = utility.remove_leading_whitespace_from_dd_element( clean_md) clean_md = utility.remove_whitespace_before_abbreviation_definition( clean_md) clean_md_escaped = utility.escape_html_chars(clean_md) if not clean_md: return # HTML --> Markdown if self.has_data and self.isconverted == "True": # check if the stored data and the current text differ from each other compare_md = self.convert_markdown_to_html(self.md) # handle quirks compare_md = utility.put_colons_in_html_def_list(compare_md) compare_md = utility.strip_html_from_markdown(compare_md) if has_def_list: compare_md = utility.remove_leading_whitespace_from_dd_element( compare_md) compare_md = utility.remove_whitespace_before_abbreviation_definition( compare_md) # escape HTML if we haven't done so already if not any(x in compare_md for x in ("&", """, "'", ">", "<")): compare_md = utility.escape_html_chars(compare_md) if utility.is_same_markdown(clean_md_escaped, compare_md) or self.p.get( const.MARKDOWN_ALWAYS_REVERT): self.revert_to_stored_markdown() else: self.handle_conflict() # Markdown --> HTML else: new_html = self.convert_markdown_to_html(clean_md) # needed for proper display of images if "<img" in new_html: new_html = utility.unescape_html(new_html) html_with_data = utility.insert_md_data(self.note_id_field, "True", clean_md_escaped, new_html) self.insert_into_field(html_with_data, self.current_field) # resolve quirks self.align_elements()
def setupUI(self): self.widget = QtGui.QDialog(self.parentWindow) # self.widget.resize(500, 300) self.widget.setWindowTitle("Add a definition list") self.dt_part = QtGui.QLineEdit(self.widget) self.dt_part.setPlaceholderText("definition term") dl_part_label = QtGui.QLabel("Definition term:") self.dd_part = QtGui.QTextEdit(self.widget) self.dd_part.setAcceptRichText(False) if self.selection: self.dd_part.setText(self.selection) dd_part_label = QtGui.QLabel("Definition description:") addButton = QtGui.QPushButton("&Add more", self.widget) addButton.clicked.connect(self.addMore) buttonBox = QtGui.QDialogButtonBox(self.widget) buttonBox.addButton(addButton, QtGui.QDialogButtonBox.ActionRole) buttonBox.addButton(QtGui.QDialogButtonBox.Ok) buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) buttonBox.accepted.connect(self.widget.accept) buttonBox.rejected.connect(self.widget.reject) vbox = QtGui.QVBoxLayout(self.widget) vbox.addWidget(dl_part_label) vbox.addWidget(self.dt_part) vbox.addWidget(dd_part_label) vbox.addWidget(self.dd_part) vbox.addWidget(buttonBox) self.widget.setLayout(vbox) if self.widget.exec_() == QtGui.QDialog.Accepted: # we won't allow any empty terms if not self.dt_part.text() == "": self.data.append( (self.dt_part.text(), self.dd_part.toPlainText())) # if OK button pressed, but empty self.data list if not self.data: return # create all the terms and descriptions result = "<dl>" for key, value in self.data: key = utility.escape_html_chars(key) value = utility.escape_html_chars(value) result = result + "<dt><b>" + key + "</b></dt><dd>" + \ value + "</dd>" # we need the break <br /> at the end to "get out" of the <dl> result = result + "</dl><br />" self.editor_instance.web.eval( "document.execCommand('insertHTML', false, %s);" % json.dumps(result))
def setupUI(self): self.widget = QtGui.QDialog(self.parentWindow) # self.widget.resize(500, 300) self.widget.setWindowTitle("Add a definition list") self.dt_part = QtGui.QLineEdit(self.widget) self.dt_part.setPlaceholderText("definition term") dl_part_label = QtGui.QLabel("Definition term:") self.dd_part = QtGui.QTextEdit(self.widget) self.dd_part.setAcceptRichText(False) if self.selection: self.dd_part.setText(self.selection) dd_part_label = QtGui.QLabel("Definition description:") addButton = QtGui.QPushButton("&Add more", self.widget) addButton.clicked.connect(self.addMore) buttonBox = QtGui.QDialogButtonBox(self.widget) buttonBox.addButton(addButton, QtGui.QDialogButtonBox.ActionRole) buttonBox.addButton(QtGui.QDialogButtonBox.Ok) buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) buttonBox.accepted.connect(self.widget.accept) buttonBox.rejected.connect(self.widget.reject) vbox = QtGui.QVBoxLayout(self.widget) vbox.addWidget(dl_part_label) vbox.addWidget(self.dt_part) vbox.addWidget(dd_part_label) vbox.addWidget(self.dd_part) vbox.addWidget(buttonBox) self.widget.setLayout(vbox) if self.widget.exec_() == QtGui.QDialog.Accepted: # we won't allow any empty terms if not self.dt_part.text() == "": self.data.append((self.dt_part.text(), self.dd_part.toPlainText())) # if OK button pressed, but empty self.data list if not self.data: return # create all the terms and descriptions result = "<dl>" for key, value in self.data: key = utility.escape_html_chars(key) value = utility.escape_html_chars(value) result = result + "<dt><b>" + key + "</b></dt><dd>" + \ value + "</dd>" # we need the break <br /> at the end to "get out" of the <dl> result = result + "</dl><br />" self.editor_instance.web.eval("document.execCommand('insertHTML', false, %s);" % json.dumps(result))
def apply_markdown(self): self.cancel_html = self.html has_def_list = False if "<dl>" in self.html: has_def_list = True self.create_correct_md_for_def_list() self.html = self.note.fields[self.current_field] clean_md = utility.convert_html_to_markdown(self.html) if has_def_list: clean_md = utility.remove_leading_whitespace_from_dd_element( clean_md) clean_md = utility.remove_whitespace_before_abbreviation_definition( clean_md) clean_md_escaped = utility.escape_html_chars(clean_md) if not clean_md: return # check for changed Markdown between the stored data and the current text if self.has_data and self.isconverted == "True": compare_md = utility.convert_markdown_to_html(self.md) compare_md = utility.put_colons_in_html_def_list(compare_md) compare_md = utility.convert_html_to_markdown(compare_md) if has_def_list: compare_md = utility.remove_leading_whitespace_from_dd_element( compare_md) compare_md = utility.remove_whitespace_before_abbreviation_definition( compare_md) if not any(x in compare_md for x in ("&", """, "'", ">", "<")): compare_md_escaped = utility.escape_html_chars(compare_md) compare_md = compare_md_escaped if (utility.is_same_markdown(clean_md_escaped, compare_md) or self.p.get(const.MARKDOWN_ALWAYS_REVERT)): self.revert_to_stored_markdown() else: self.handle_conflict() else: # make abbreviations behave correctly new_html = utility.convert_markdown_to_html(clean_md) # needed for proper display of images if "<img" in new_html: new_html = utility.unescape_html(new_html) html_with_data = utility.make_data_ready_to_insert( self.current_note_id_and_field, "True", clean_md_escaped, new_html) self.insert_markup_in_field(html_with_data, self.editor_instance.currentField) self.align_elements() const.MARKDOWN_PREFS["disable_buttons"] = True self.warn_about_changes(self.editor_instance, self.current_field, const.MARKDOWN_BG_COLOR)
def apply_markdown(self): self.cancel_html = self.html has_def_list = False if "<dl>" in self.html: has_def_list = True self.create_correct_md_for_def_list() self.html = self.note.fields[self.current_field] clean_md = utility.convert_html_to_markdown(self.html) if has_def_list: clean_md = utility.remove_leading_whitespace_from_dd_element(clean_md) clean_md = utility.remove_whitespace_before_abbreviation_definition( clean_md) clean_md_escaped = utility.escape_html_chars(clean_md) if not clean_md: return # check for changed Markdown between the stored data and the current text if (self.has_data and self.isconverted == "True"): compare_md = utility.convert_markdown_to_html(self.md) compare_md = utility.put_colons_in_html_def_list(compare_md) compare_md = utility.convert_html_to_markdown(compare_md) if has_def_list: compare_md = utility.remove_leading_whitespace_from_dd_element(compare_md) compare_md = utility.remove_whitespace_before_abbreviation_definition( compare_md) if not any(x in compare_md for x in("&", """, "'", ">", "<")): compare_md_escaped = utility.escape_html_chars(compare_md) compare_md = compare_md_escaped if (utility.is_same_markdown(clean_md_escaped, compare_md) or preferences.PREFS.get(const.MARKDOWN_ALWAYS_REVERT)): self.revert_to_stored_markdown() else: self.handle_conflict() else: # make abbreviations behave correctly new_html = utility.convert_markdown_to_html(clean_md) # needed for proper display of images if "<img" in new_html: new_html = utility.unescape_html(new_html) html_with_data = utility.make_data_ready_to_insert( self.current_note_id_and_field, "True", clean_md_escaped, new_html) self.insert_markup_in_field( html_with_data, self.editor_instance.currentField) self.align_elements() const.MARKDOWN_PREFS["disable_buttons"] = True self.warn_about_changes(self.editor_instance, self.current_field, const.MARKDOWN_BG_COLOR)
def create_anchor(url, text): """ Create a hyperlink string, where `url` is the hyperlink reference and `text` the content of the tag. """ assert isinstance(url, unicode), "Input `url` is not Unicode" assert isinstance(text, unicode), "Input `text` is not Unicode" text = utility.escape_html_chars(text) return u"<a href=\"{0}\">{1}</a>".format(url, text)
def overwrite_stored_data(self): """ Create new Markdown from the current HTML. """ clean_md = utility.strip_html_from_markdown(self.html, keep_empty_lines=True) clean_md = utility.remove_whitespace_before_abbreviation_definition(clean_md) if "<dl" in self.html: clean_md = utility.remove_leading_whitespace_from_dd_element(clean_md, add_newline=True) if re.search(const.IS_LINK_OR_IMG_REGEX, clean_md): clean_md = utility.escape_html_chars(clean_md) new_html = utility.convert_clean_md_to_html(clean_md, put_breaks=True) self.insert_into_field(new_html, self.current_field) self.remove_warn_msg(self.editor, self.current_field)
def create_anchor(self, url, text): """ Create a hyperlink string, where `url` is the hyperlink reference and `text` the content of the tag. """ assert isinstance(url, unicode), "Input `url` is not Unicode" assert isinstance(text, unicode), "Input `text` is not Unicode" # uncomment to check for and force `http` prefix # pattern = re.compile(r"(?i)https?://") # match = re.match(pattern, url) # if not match: # url = u"http://" + url text = utility.escape_html_chars(text) return u"<a href=\"{0}\">{1}</a>".format(url, text)
def overwrite_stored_data(self): """ Create new Markdown from the current HTML. """ clean_md = utility.convert_html_to_markdown(self.html, keep_empty_lines=True) clean_md = utility.remove_whitespace_before_abbreviation_definition( clean_md) if "<dl" in self.html: clean_md = utility.remove_leading_whitespace_from_dd_element( clean_md, add_newline=True) if re.search(const.IS_LINK_OR_IMG_REGEX, clean_md): clean_md = utility.escape_html_chars(clean_md) new_html = utility.convert_clean_md_to_html(clean_md, put_breaks=True) self.insert_markup_in_field(new_html, self.current_field) const.MARKDOWN_PREFS["disable_buttons"] = False const.MARKDOWN_PREFS["isconverted"] = False self.remove_warn_msg(self.editor_instance, self.current_field)
def overwrite_stored_data(self): """ Create new Markdown from the current HTML. """ clean_md = utility.convert_html_to_markdown( self.html, keep_empty_lines=True) clean_md = utility.remove_whitespace_before_abbreviation_definition( clean_md) if "<dl" in self.html: clean_md = utility.remove_leading_whitespace_from_dd_element( clean_md, add_newline=True) if re.search(const.IS_LINK_OR_IMG_REGEX, clean_md): clean_md = utility.escape_html_chars(clean_md) new_html = utility.convert_clean_md_to_html(clean_md, put_breaks=True) self.insert_markup_in_field(new_html, self.current_field) const.MARKDOWN_PREFS["disable_buttons"] = False const.MARKDOWN_PREFS["isconverted"] = False self.remove_warn_msg(self.editor_instance, self.current_field)
def wrap_in_tags(self, tag, class_name=None): """ Wrap selected text in a tag, optionally giving it a class. """ selection = self.web.selectedText() if not selection: return selection = utility.escape_html_chars(selection) tag_string_begin = ("<{0} class='{1}'>".format(tag, class_name) if class_name else "<{0}>".format(tag)) tag_string_end = "</{0}>".format(tag) html = self.note.fields[self.currentField] if "<li><br /></li>" in html: # an empty list means trouble, because somehow Anki will also make the # line in which we want to put a <code> tag a list if we continue replacement = tag_string_begin + selection + tag_string_end self.web.eval("document.execCommand('insertHTML', false, %s);" % json.dumps(replacement)) self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() html_after = self.note.fields[self.currentField] if html_after != html: # you're in luck! return else: # nothing happened :( this is another Anki bug :( that has to do # with <code> tags following <div> tags return # Due to a bug in Anki or BeautifulSoup, we cannot use a simple # wrap operation like with <a>. So this is a very hackish way of making # sure that a <code> tag may precede or follow a <div> and that the tag # won't eat the character immediately preceding or following it pattern = "@%*!" len_p = len(pattern) # first, wrap the selection up in a pattern that the user is unlikely # to use in its own cards self.web.eval("wrap('{0}', '{1}')".format(pattern, pattern[::-1])) # focus the field, so that changes are saved # this causes the cursor to go to the end of the field self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() html = self.note.fields[self.currentField] begin = html.find(pattern) end = html.find(pattern[::-1], begin) html = (html[:begin] + tag_string_begin + selection + tag_string_end + html[end+len_p:]) # delete the current HTML and replace it by our new & improved one self.note.fields[self.currentField] = html # reload the note: this is needed on OS X, because it will otherwise # falsely show that the formatting of the element at the start of # the HTML has spread across the entire note self.loadNote() # focus the field, so that changes are saved self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField)
def wrap_in_tags(self, tag, class_name=None): """ Wrap selected text in a tag, optionally giving it a class. """ selection = self.web.selectedText() if not selection: return selection = utility.escape_html_chars(selection) tag_string_begin = ("<{0} class='{1}'>".format(tag, class_name) if class_name else "<{0}>".format(tag)) tag_string_end = "</{0}>".format(tag) html = self.note.fields[self.currentField] if "<li><br /></li>" in html: # an empty list means trouble, because somehow Anki will also make the # line in which we want to put a <code> tag a list if we continue replacement = tag_string_begin + selection + tag_string_end self.web.eval("document.execCommand('insertHTML', false, %s);" % json.dumps(replacement)) self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() html_after = self.note.fields[self.currentField] if html_after != html: # you're in luck! return else: # nothing happened :( this is a quirk that has to do with <code> tags following <div> tags return # Due to a bug in Anki or BeautifulSoup, we cannot use a simple # wrap operation like with <a>. So this is a very hackish way of making # sure that a <code> tag may precede or follow a <div> and that the tag # won't eat the character immediately preceding or following it pattern = "@%*!" len_p = len(pattern) # first, wrap the selection up in a pattern that the user is unlikely # to use in its own cards self.web.eval("wrap('{0}', '{1}')".format(pattern, pattern[::-1])) # focus the field, so that changes are saved # this causes the cursor to go to the end of the field self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() html = self.note.fields[self.currentField] begin = html.find(pattern) end = html.find(pattern[::-1], begin) html = (html[:begin] + tag_string_begin + selection + tag_string_end + html[end+len_p:]) # delete the current HTML and replace it by our new & improved one self.note.fields[self.currentField] = html # reload the note: this is needed on OS X, because it will otherwise # falsely show that the formatting of the element at the start of # the HTML has spread across the entire note self.loadNote() # focus the field, so that changes are saved self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField)
def create_table_from_selection(self): """ Create a table out of the selected text. """ # there is no text to make a table from if not self.selected_text: return False # there is a single line of text if not self.selected_text.count(u"\n"): return False # there is no content in table if all(c in (u"|", u"\n") for c in self.selected_text): return False # split on newlines first = [x for x in self.selected_text.split(u"\n") if x] # split on pipes second = list() for elem in first[:]: new_elem = [x.strip() for x in elem.split(u"|")] new_elem = [utility.escape_html_chars(word) for word in new_elem] second.append(new_elem) # keep track of the max number of cols # so as to make all rows of equal length max_num_cols = len(max(second, key=len)) # decide how much horizontal space each column may take width = 100 / max_num_cols # check for "-|-|-" alignment row if all(x.strip(u":") in (u"-", u"") for x in second[1]): start = 2 align_line = second[1] len_align_line = len(align_line) if len_align_line < max_num_cols: align_line += [u"-"] * (max_num_cols - len_align_line) alignments = list() for elem in second[1]: alignments.append(utility.check_alignment(elem)) else: alignments = [u"left"] * max_num_cols start = 1 # create a table head_row = u"" head_html = \ u"<th align=\"{0}\" style=\"width: {1}%; padding: 5px;" \ + u"border-bottom: 2px solid #00B3FF\">{2}</th>" for elem, alignment in zip(second[0], alignments): head_row += (head_html.format(alignment, width, elem)) extra_cols = u"" if len(second[0]) < max_num_cols: diff = len(second[0]) - max_num_cols assert diff < 0, "Difference between len(second[0]) and max_num_cols is positive" extra_html = \ u"<th align=\"{0}\" style=\"width: {1}%; padding: 5px;" \ + u"border-bottom: 2px solid #00B3FF\"></th>" for alignment in alignments[diff:]: extra_cols += (extra_html.format(alignment, width)) head_row += extra_cols body_rows = u"" for row in second[start:]: body_rows += u"<tr>" body_html = \ u"<td style=\"text-align: {0}; padding: 5px; border-bottom:" \ + u"1px solid #B0B0B0\">{1}</td>" for elem, alignment in zip(row, alignments): body_rows += (body_html.format(alignment, elem)) # if particular row is not up to par with number of cols extra_cols = "" if len(row) < max_num_cols: diff = len(row) - max_num_cols assert diff < 0, "Difference between len(row) and max_num_cols is positive" extra_html = \ u"<td style=\"text-align: {0}; padding: 5px;" \ + u"border-bottom: 1px solid #B0B0B0\"></td>" for alignment in alignments[diff:]: extra_cols += (extra_html.format(alignment)) body_rows += extra_cols + "</tr>" html = u""" <table style="width: 100%; border-collapse: collapse;"> <thead> <tr> {0} </tr> </thead> <tbody> {1} </tbody> </table>""".format(head_row, body_rows) self.editor_instance.web.eval( "document.execCommand('insertHTML', false, %s);" % json.dumps(html)) return True
def create_custom_heading(self, selected_text=None): dialog = QtGui.QDialog(self.parent_window) dialog.setWindowTitle("Create custom heading") text_label = QtGui.QLabel("Text:", dialog) text_line_edit = QtGui.QLineEdit(dialog) if selected_text: text_line_edit.setText(selected_text) text_hbox = QtGui.QHBoxLayout() text_hbox.addWidget(text_label) text_hbox.addWidget(text_line_edit) stylesheet = """ QGroupBox { border: 1px inset lightgrey; border-radius: 5px; margin-top: 10px; font-weight: bold; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top; padding:0 3px 0 3px; } """ groupbox = QtGui.QGroupBox("Choose a size", dialog) groupbox.setStyleSheet(stylesheet) radio_button1 = QtGui.QRadioButton("Biggest", dialog) radio_button1.setChecked(True) radio_button2 = QtGui.QRadioButton("Big", dialog) radio_button3 = QtGui.QRadioButton("Medium", dialog) radio_button4 = QtGui.QRadioButton("Small", dialog) radio_button5 = QtGui.QRadioButton("Smaller", dialog) radio_button6 = QtGui.QRadioButton("Tiny", dialog) radio_button_group = QtGui.QButtonGroup(dialog) radio_button_group.addButton(radio_button1) radio_button_group.setId(radio_button1, 1) radio_button_group.addButton(radio_button2) radio_button_group.setId(radio_button2, 2) radio_button_group.addButton(radio_button3) radio_button_group.setId(radio_button3, 3) radio_button_group.addButton(radio_button4) radio_button_group.setId(radio_button4, 4) radio_button_group.addButton(radio_button5) radio_button_group.setId(radio_button5, 5) radio_button_group.addButton(radio_button6) radio_button_group.setId(radio_button6, 6) radio_hbox = QtGui.QHBoxLayout() radio_hbox.addWidget(radio_button1) radio_hbox.addWidget(radio_button2) radio_hbox.addWidget(radio_button3) radio_hbox.addWidget(radio_button4) radio_hbox.addWidget(radio_button5) radio_hbox.addWidget(radio_button6) groupbox.setLayout(radio_hbox) button_box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, dialog) button_box.accepted.connect(dialog.accept) button_box.rejected.connect(dialog.reject) vbox = QtGui.QVBoxLayout() vbox.addLayout(text_hbox) vbox.addStretch(1) vbox.addWidget(groupbox) vbox.addWidget(button_box) vbox.setSizeConstraint(QtGui.QLayout.SetFixedSize) dialog.setLayout(vbox) if dialog.exec_() == QtGui.QDialog.Accepted: text = unicode(text_line_edit.text()) size_heading = radio_button_group.id( radio_button_group.checkedButton()) heading_tag = "h" + str(size_heading) if text == "": return else: text = utility.escape_html_chars(text) start_tag = "<{0}>".format(heading_tag) end_tag = "</{0}>".format(heading_tag) if selected_text: self.editor_instance.web.eval( "wrap('{0}', '{1}')".format(start_tag, end_tag)) self.cleanup_headings() else: result = u"{0}{1}{2}".format(start_tag, text, end_tag) self.editor_instance.web.eval( "document.execCommand('insertHTML', false, %s);" % json.dumps(unicode(result)))
def create_custom_heading(self, selected_text=None): dialog = QtGui.QDialog(self.parent_window) dialog.setWindowTitle("Create custom heading") text_label = QtGui.QLabel("Text:", dialog) text_line_edit = QtGui.QLineEdit(dialog) if selected_text: text_line_edit.setText(selected_text) text_hbox = QtGui.QHBoxLayout() text_hbox.addWidget(text_label) text_hbox.addWidget(text_line_edit) groupbox = QtGui.QGroupBox("Choose a size", dialog) groupbox.setStyleSheet(const.QGROUPBOX_STYLE) radio_button1 = QtGui.QRadioButton("Biggest", dialog) radio_button1.setChecked(True) radio_button2 = QtGui.QRadioButton("Big", dialog) radio_button3 = QtGui.QRadioButton("Medium", dialog) radio_button4 = QtGui.QRadioButton("Small", dialog) radio_button5 = QtGui.QRadioButton("Smaller", dialog) radio_button6 = QtGui.QRadioButton("Tiny", dialog) radio_button_group = QtGui.QButtonGroup(dialog) radio_button_group.addButton(radio_button1) radio_button_group.setId(radio_button1, 1) radio_button_group.addButton(radio_button2) radio_button_group.setId(radio_button2, 2) radio_button_group.addButton(radio_button3) radio_button_group.setId(radio_button3, 3) radio_button_group.addButton(radio_button4) radio_button_group.setId(radio_button4, 4) radio_button_group.addButton(radio_button5) radio_button_group.setId(radio_button5, 5) radio_button_group.addButton(radio_button6) radio_button_group.setId(radio_button6, 6) radio_hbox = QtGui.QHBoxLayout() radio_hbox.addWidget(radio_button1) radio_hbox.addWidget(radio_button2) radio_hbox.addWidget(radio_button3) radio_hbox.addWidget(radio_button4) radio_hbox.addWidget(radio_button5) radio_hbox.addWidget(radio_button6) groupbox.setLayout(radio_hbox) button_box = QtGui.QDialogButtonBox( QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, dialog) button_box.accepted.connect(dialog.accept) button_box.rejected.connect(dialog.reject) vbox = QtGui.QVBoxLayout() vbox.addLayout(text_hbox) vbox.addStretch(1) vbox.addWidget(groupbox) vbox.addWidget(button_box) vbox.setSizeConstraint(QtGui.QLayout.SetFixedSize) dialog.setLayout(vbox) if dialog.exec_() == QtGui.QDialog.Accepted: text = unicode(text_line_edit.text()) size_heading = radio_button_group.id( radio_button_group.checkedButton()) heading_tag = "h" + str(size_heading) if text == "": return else: text = utility.escape_html_chars(text) start_tag = "<{0}>".format(heading_tag) end_tag = "</{0}>".format(heading_tag) if selected_text: self.editor_instance.web.eval("wrap('{0}', '{1}')".format( start_tag, end_tag)) self.cleanup_headings() else: result = u"{0}{1}{2}".format(start_tag, text, end_tag) self.editor_instance.web.eval( "document.execCommand('insertHTML', false, %s);" % json.dumps(unicode(result)))
def create_table_from_selection(self): """ Create a table out of the selected text. """ # there is no text to make a table from if not self.selected_text: return False # there is a single line of text if not self.selected_text.count(u"\n"): return False # there is no content in table if all(c in (u"|", u"\n") for c in self.selected_text): return False # split on newlines first = [x for x in self.selected_text.split(u"\n") if x] # split on pipes second = list() for elem in first[:]: new_elem = [x.strip() for x in elem.split(u"|")] new_elem = [utility.escape_html_chars(word) for word in new_elem] second.append(new_elem) # keep track of the max number of cols # so as to make all rows of equal length max_num_cols = len(max(second, key=len)) # decide how much horizontal space each column may take width = 100 / max_num_cols # check for "-|-|-" alignment row if all(x.strip(u":") in (u"-", u"") for x in second[1]): start = 2 align_line = second[1] len_align_line = len(align_line) if len_align_line < max_num_cols: align_line += [u"-"] * (max_num_cols - len_align_line) alignments = list() for elem in second[1]: alignments.append(utility.check_alignment(elem)) else: alignments = [u"left"] * max_num_cols start = 1 # create a table head_row = u"" head_html = u"<th {0}>{1}</th>" for elem, alignment in zip(second[0], alignments): head_row += head_html.format( self.HEAD_STYLING.format(alignment, width), elem) extra_cols = u"" if len(second[0]) < max_num_cols: diff = len(second[0]) - max_num_cols assert diff < 0, \ "Difference between len(second[0]) and max_num_cols is positive" for alignment in alignments[diff:]: extra_cols += head_html.format( self.HEAD_STYLING.format(alignment, width), u"") head_row += extra_cols body_rows = u"" for row in second[start:]: body_rows += u"<tr>" body_html = u"<td {0}>{1}</td>" for elem, alignment in zip(row, alignments): body_rows += body_html.format( self.BODY_STYLING.format(alignment), elem) # if particular row is not up to par with number of cols extra_cols = "" if len(row) < max_num_cols: diff = len(row) - max_num_cols assert diff < 0, \ "Difference between len(row) and max_num_cols is positive" # use the correct alignment for the last few rows for alignment in alignments[diff:]: extra_cols += body_html.format( self.BODY_STYLING.format(alignment), u"") body_rows += extra_cols + u"</tr>" html = u""" <table {0}> <thead> <tr> {1} </tr> </thead> <tbody> {2} </tbody> </table>""".format(self.TABLE_STYLING, head_row, body_rows) self.editor_instance.web.eval( "document.execCommand('insertHTML', false, %s);" % json.dumps(html)) return True