diff --git a/imgsrc/code.svg b/imgsrc/code.svg new file mode 100644 index 0000000000..c186a40dcb --- /dev/null +++ b/imgsrc/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/code.png b/resources/images/code.png new file mode 100644 index 0000000000..3980b0e00c Binary files /dev/null and b/resources/images/code.png differ diff --git a/src/calibre/ebooks/constants.py b/src/calibre/ebooks/constants.py new file mode 100644 index 0000000000..b01f44fa6d --- /dev/null +++ b/src/calibre/ebooks/constants.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2014, Kovid Goyal ' + +html5_tags = ( # {{{ +frozenset('''\ +html +head +title +base +link +meta +style +script +noscript +body +section +nav +article +aside +h1 +h2 +h3 +h4 +h5 +h6 +header +footer +address +p +hr +br +pre +dialog +blockquote +ol +ul +li +dl +dt +dd +a +q +cite +em +strong +small +mark +dfn +abbr +time +progress +meter +code +var +samp +kbd +sub +sup +span +i +b +bdo +ruby +rt +rp +ins +del +figure +img +iframe +embed +object +param +video +audio +source +canvas +map +area +table +caption +colgroup +col +tbody +thead +tfoot +tr +td +th +form +fieldset +label +input +button +select +datalist +optgroup +option +textarea +output +details +command +bb +menu +legend +div'''.splitlines())) # }}} diff --git a/src/calibre/gui2/tweak_book/__init__.py b/src/calibre/gui2/tweak_book/__init__.py index bfdc16b610..621c45eff2 100644 --- a/src/calibre/gui2/tweak_book/__init__.py +++ b/src/calibre/gui2/tweak_book/__init__.py @@ -42,6 +42,7 @@ d['folders_for_types'] = {'style':'styles', 'image':'images', 'font':'fonts', 'a d['pretty_print_on_open'] = False d['disable_completion_popup_for_search'] = False d['saved_searches'] = [] +d['insert_tag_mru'] = ['p', 'div', 'li', 'h1', 'h2', 'h3', 'h4', 'em', 'strong', 'td', 'tr'] del d diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 9622757207..67c985b6fd 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -39,7 +39,7 @@ from calibre.gui2.tweak_book.preferences import Preferences from calibre.gui2.tweak_book.search import validate_search_request, run_search from calibre.gui2.tweak_book.widgets import ( RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink, - InsertSemantics, BusyCursor) + InsertSemantics, BusyCursor, InsertTag) _diff_dialogs = [] @@ -642,6 +642,10 @@ class Boss(QObject): d = InsertLink(current_container(), edname, initial_text=ed.get_smart_selection(), parent=self.gui) if d.exec_() == d.Accepted: ed.insert_hyperlink(d.href, d.text) + elif action[0] == 'insert_tag': + d = InsertTag(parent=self.gui) + if d.exec_() == d.Accepted: + ed.insert_tag(d.tag) else: ed.action_triggered(action) diff --git a/src/calibre/gui2/tweak_book/editor/smart/html.py b/src/calibre/gui2/tweak_book/editor/smart/html.py index a0b2a1ba77..32aa06d587 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/html.py +++ b/src/calibre/gui2/tweak_book/editor/smart/html.py @@ -232,3 +232,12 @@ class HTMLSmarts(NullSmarts): if text: c.insertText(text) editor.setTextCursor(c) + + def insert_tag(self, editor, name): + text = self.get_smart_selection(editor, update=True) + c = editor.textCursor() + pos = min(c.position(), c.anchor()) + c.insertText('<{0}>{1}'.format(name, text)) + c.setPosition(pos + 1 + len(name)) + editor.setTextCursor(c) + diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index fb949f63eb..ed90f821d7 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -612,6 +612,10 @@ class TextEdit(PlainTextEdit): if hasattr(self.smarts, 'insert_hyperlink'): self.smarts.insert_hyperlink(self, target, text) + def insert_tag(self, tag): + if hasattr(self.smarts, 'insert_tag'): + self.smarts.insert_tag(self, tag) + def keyPressEvent(self, ev): if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier: if self.replace_possible_unicode_sequence(): diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 81a54d17a9..175eb4d2b0 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -7,13 +7,14 @@ __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' import unicodedata +from functools import partial from PyQt4.Qt import ( QMainWindow, Qt, QApplication, pyqtSignal, QMenu, qDrawShadeRect, QPainter, QImage, QColor, QIcon, QPixmap, QToolButton) from calibre.gui2 import error_dialog -from calibre.gui2.tweak_book import actions, current_container +from calibre.gui2.tweak_book import actions, current_container, tprefs from calibre.gui2.tweak_book.editor.text import TextEdit def create_icon(text, palette=None, sz=32, divider=2): @@ -65,6 +66,10 @@ def register_text_editor_actions(reg, palette): ac = reg(create_icon(name), text, ('rename_block_tag', name), 'rename-block-tag-' + name, 'Ctrl+%d' % (i + 1), desc) ac.setToolTip(desc) + ac = reg('code', _('Insert &tag'), ('insert_tag',), 'insert-tag', ('Ctrl+<'), _('Insert tag')) + ac.setToolTip(_('

Insert tag

Insert a tag, if some text is selected the tag will be inserted around the selected text')) + + class Editor(QMainWindow): has_line_numbers = True @@ -147,6 +152,23 @@ class Editor(QMainWindow): def insert_hyperlink(self, href, text): self.editor.insert_hyperlink(href, text) + def _build_insert_tag_button_menu(self): + m = self.insert_tag_button.menu() + m.clear() + for name in tprefs['insert_tag_mru']: + m.addAction(name, partial(self.insert_tag, name)) + + def insert_tag(self, name): + self.editor.insert_tag(name) + mru = tprefs['insert_tag_mru'] + try: + mru.remove(name) + except ValueError: + pass + mru.insert(0, name) + tprefs['insert_tag_mru'] = mru + self._build_insert_tag_button_menu() + def undo(self): self.editor.undo() @@ -198,6 +220,7 @@ class Editor(QMainWindow): for x in ('cut', 'copy', 'paste'): b.addAction(actions['editor-%s' % x]) self.tools_bar = b = self.addToolBar(_('Editor tools')) + b.setObjectName('tools_bar') if self.syntax == 'html': b.addAction(actions['fix-html-current']) if self.syntax in {'xml', 'html', 'css'}: @@ -206,8 +229,18 @@ class Editor(QMainWindow): b.addAction(actions['insert-image']) if self.syntax == 'html': b.addAction(actions['insert-hyperlink']) + if self.syntax in {'xml', 'html'}: + b.addAction(actions['insert-tag']) + w = self.insert_tag_button = b.widgetForAction(actions['insert-tag']) + w.setPopupMode(QToolButton.MenuButtonPopup) + w.m = m = QMenu() + w.setMenu(m) + w.setContextMenuPolicy(Qt.CustomContextMenu) + w.customContextMenuRequested.connect(self.insert_tag_button.showMenu) + self._build_insert_tag_button_menu() if self.syntax == 'html': self.format_bar = b = self.addToolBar(_('Format text')) + b.setObjectName('html_format_bar') for x in ('bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'color', 'background-color'): b.addAction(actions['format-text-%s' % x]) ac = b.addAction(QIcon(I('format-text-heading.png')), _('Change paragraph to heading')) diff --git a/src/calibre/gui2/tweak_book/widgets.py b/src/calibre/gui2/tweak_book/widgets.py index e3ddc0d13b..fbb021830f 100644 --- a/src/calibre/gui2/tweak_book/widgets.py +++ b/src/calibre/gui2/tweak_book/widgets.py @@ -22,6 +22,7 @@ from calibre.gui2 import error_dialog, choose_files, choose_save_file, NONE, inf from calibre.gui2.tweak_book import tprefs from calibre.utils.icu import primary_sort_key, sort_key from calibre.utils.matcher import get_char, Matcher +from calibre.gui2.complete2 import EditWithComplete ROOT = QModelIndex() @@ -69,6 +70,39 @@ class Dialog(QDialog): def setup_ui(self): raise NotImplementedError('You must implement this method in Dialog subclasses') +class InsertTag(Dialog): # {{{ + + def __init__(self, parent=None): + Dialog.__init__(self, _('Choose tag name'), 'insert-tag', parent=parent) + + def setup_ui(self): + from calibre.ebooks.constants import html5_tags + self.l = l = QVBoxLayout(self) + self.setLayout(l) + + self.la = la = QLabel(_('Specify the name of the &tag to insert:')) + l.addWidget(la) + + self.tag_input = ti = EditWithComplete(self) + ti.set_separator(None) + ti.all_items = html5_tags | frozenset(tprefs['insert_tag_mru']) + la.setBuddy(ti) + l.addWidget(ti) + l.addWidget(self.bb) + ti.setFocus(Qt.OtherFocusReason) + + @property + def tag(self): + return unicode(self.tag_input.text()).strip() + + @classmethod + def test(cls): + d = cls() + if d.exec_() == d.Accepted: + print (d.tag) + +# }}} + class RationalizeFolders(Dialog): # {{{ TYPE_MAP = ( @@ -852,4 +886,4 @@ class InsertSemantics(Dialog): if __name__ == '__main__': app = QApplication([]) - InsertSemantics.test() + InsertTag.test()