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}{0}>'.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()