%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/tweak_book/editor/ |
| Current File : //lib/calibre/calibre/gui2/tweak_book/editor/widget.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import math
import unicodedata
from functools import partial
from qt.core import (
QAction, QApplication, QColor, QIcon, QImage, QInputDialog, QMainWindow, QMenu,
QPainter, QPixmap, QSize, Qt, QTextCursor, QToolButton, pyqtSignal,
qDrawShadeRect
)
from calibre import prints
from calibre.constants import DEBUG
from calibre.ebooks.chardet import replace_encoding_declarations
from calibre.gui2 import error_dialog, open_url
from calibre.gui2.tweak_book import (
actions, current_container, dictionaries, editor_name, editor_toolbar_actions,
editors, tprefs, update_mark_text_action
)
from calibre.gui2.tweak_book.editor import (
CLASS_ATTRIBUTE_PROPERTY, CSS_PROPERTY, LINK_PROPERTY, SPELL_PROPERTY,
TAG_NAME_PROPERTY
)
from calibre.gui2.tweak_book.editor.help import help_url
from calibre.gui2.tweak_book.editor.text import TextEdit
from calibre.utils.icu import utf16_length
from polyglot.builtins import itervalues, string_or_bytes
def create_icon(text, palette=None, sz=None, divider=2, fill='white'):
if isinstance(fill, string_or_bytes):
fill = QColor(fill)
sz = sz or int(math.ceil(tprefs['toolbar_icon_size'] * QApplication.instance().devicePixelRatio()))
if palette is None:
palette = QApplication.palette()
img = QImage(sz, sz, QImage.Format.Format_ARGB32)
img.fill(Qt.GlobalColor.transparent)
p = QPainter(img)
p.setRenderHints(QPainter.RenderHint.TextAntialiasing | QPainter.RenderHint.Antialiasing)
if fill is not None:
qDrawShadeRect(p, img.rect(), palette, fill=fill, lineWidth=1, midLineWidth=1)
f = p.font()
f.setFamily('Liberation Sans'), f.setPixelSize(int(sz // divider)), f.setBold(True)
p.setFont(f), p.setPen(QColor('#2271d5'))
p.drawText(img.rect().adjusted(2, 2, -2, -2), Qt.AlignmentFlag.AlignCenter, text)
p.end()
return QIcon(QPixmap.fromImage(img))
def register_text_editor_actions(_reg, palette):
def reg(*args, **kw):
ac = _reg(*args)
for s in kw.get('syntaxes', ('format',)):
editor_toolbar_actions[s][args[3]] = ac
return ac
ac = reg('format-text-bold.png', _('&Bold'), ('format_text', 'bold'), 'format-text-bold', 'Ctrl+B', _('Make the selected text bold'))
ac.setToolTip(_('<h3>Bold</h3>Make the selected text bold'))
ac = reg('format-text-italic.png', _('&Italic'), ('format_text', 'italic'), 'format-text-italic', 'Ctrl+I', _('Make the selected text italic'))
ac.setToolTip(_('<h3>Italic</h3>Make the selected text italic'))
ac = reg('format-text-underline.png', _('&Underline'), ('format_text', 'underline'), 'format-text-underline', (), _('Underline the selected text'))
ac.setToolTip(_('<h3>Underline</h3>Underline the selected text'))
ac = reg('format-text-strikethrough.png', _('&Strikethrough'), ('format_text', 'strikethrough'),
'format-text-strikethrough', (), _('Draw a line through the selected text'))
ac.setToolTip(_('<h3>Strikethrough</h3>Draw a line through the selected text'))
ac = reg('format-text-superscript.png', _('&Superscript'), ('format_text', 'superscript'),
'format-text-superscript', (), _('Make the selected text a superscript'))
ac.setToolTip(_('<h3>Superscript</h3>Set the selected text slightly smaller and above the normal line'))
ac = reg('format-text-subscript.png', _('&Subscript'), ('format_text', 'subscript'),
'format-text-subscript', (), _('Make the selected text a subscript'))
ac.setToolTip(_('<h3>Subscript</h3>Set the selected text slightly smaller and below the normal line'))
ac = reg('format-text-color.png', _('&Color'), ('format_text', 'color'), 'format-text-color', (), _('Change text color'))
ac.setToolTip(_('<h3>Color</h3>Change the color of the selected text'))
ac = reg('format-fill-color.png', _('&Background color'), ('format_text', 'background-color'),
'format-text-background-color', (), _('Change background color of text'))
ac.setToolTip(_('<h3>Background color</h3>Change the background color of the selected text'))
ac = reg('format-justify-left.png', _('Align &left'), ('format_text', 'justify_left'), 'format-text-justify-left', (), _('Align left'))
ac.setToolTip(_('<h3>Align left</h3>Align the paragraph to the left'))
ac = reg('format-justify-center.png', _('&Center'), ('format_text', 'justify_center'), 'format-text-justify-center', (), _('Center'))
ac.setToolTip(_('<h3>Center</h3>Center the paragraph'))
ac = reg('format-justify-right.png', _('Align &right'), ('format_text', 'justify_right'), 'format-text-justify-right', (), _('Align right'))
ac.setToolTip(_('<h3>Align right</h3>Align the paragraph to the right'))
ac = reg('format-justify-fill.png', _('&Justify'), ('format_text', 'justify_justify'), 'format-text-justify-fill', (), _('Justify'))
ac.setToolTip(_('<h3>Justify</h3>Align the paragraph to both the left and right margins'))
ac = reg('sort.png', _('&Sort style rules'), ('sort_css',), 'editor-sort-css', (),
_('Sort the style rules'), syntaxes=('css',))
ac = reg('view-image.png', _('&Insert image'), ('insert_resource', 'image'), 'insert-image', (),
_('Insert an image into the text'), syntaxes=('html', 'css'))
ac.setToolTip(_('<h3>Insert image</h3>Insert an image into the text'))
ac = reg('insert-link.png', _('Insert &hyperlink'), ('insert_hyperlink',), 'insert-hyperlink', (), _('Insert hyperlink'), syntaxes=('html',))
ac.setToolTip(_('<h3>Insert hyperlink</h3>Insert a hyperlink into the text'))
ac = reg(create_icon('/*', divider=1, fill=None), _('Smart &comment'), ('smart_comment',), 'editor-smart-comment', ('Ctrl+`',), _(
'Smart comment (toggle block comments)'), syntaxes=())
ac.setToolTip(_('<h3>Smart comment</h3>Comment or uncomment text<br><br>'
'If the cursor is inside an existing block comment, uncomment it, otherwise comment out the selected text.'))
for i, name in enumerate(('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p')):
text = ('&' + name) if name == 'p' else (name[0] + '&' + name[1])
desc = _('Convert the paragraph to <%s>') % name
ac = reg(create_icon(name), text, ('rename_block_tag', name), 'rename-block-tag-' + name, 'Ctrl+%d' % (i + 1), desc, syntaxes=())
ac.setToolTip(desc)
for transform, text in [
('upper', _('&Upper case')), ('lower', _('&Lower case')), ('swap', _('&Swap case')),
('title', _('&Title case')), ('capitalize', _('&Capitalize'))]:
desc = _('Change the case of the selected text: %s') % text
ac = reg(None, text, ('change_case', transform), 'transform-case-' + transform, (), desc, syntaxes=())
ac.setToolTip(desc)
ac = reg('code.png', _('Insert &tag'), ('insert_tag',), 'insert-tag', ('Ctrl+<'), _('Insert tag'), syntaxes=('html', 'xml'))
ac.setToolTip(_('<h3>Insert tag</h3>Insert a tag, if some text is selected the tag will be inserted around the selected text'))
ac = reg('trash.png', _('Remove &tag'), ('remove_tag',), 'remove-tag', ('Ctrl+>'), _('Remove tag'), syntaxes=('html', 'xml'))
ac.setToolTip(_('<h3>Remove tag</h3>Remove the currently highlighted tag'))
ac = reg('split.png', _('&Split tag'), ('split_tag',), 'split-tag', ('Ctrl+Alt+>'), _('Split current tag'), syntaxes=('html', 'xml'))
ac.setToolTip(_('<h3>Split tag</h3>Split the current tag at the cursor position'))
editor_toolbar_actions['html']['fix-html-current'] = actions['fix-html-current']
for s in ('xml', 'html', 'css'):
editor_toolbar_actions[s]['pretty-current'] = actions['pretty-current']
editor_toolbar_actions['html']['change-paragraph'] = actions['change-paragraph'] = QAction(
QIcon(I('format-text-heading.png')), _('Change paragraph to heading'), ac.parent())
class Editor(QMainWindow):
has_line_numbers = True
modification_state_changed = pyqtSignal(object)
undo_redo_state_changed = pyqtSignal(object, object)
copy_available_state_changed = pyqtSignal(object)
data_changed = pyqtSignal(object)
cursor_position_changed = pyqtSignal()
word_ignored = pyqtSignal(object, object)
link_clicked = pyqtSignal(object)
class_clicked = pyqtSignal(object)
rename_class = pyqtSignal(object)
smart_highlighting_updated = pyqtSignal()
def __init__(self, syntax, parent=None):
QMainWindow.__init__(self, parent)
if parent is None:
self.setWindowFlags(Qt.WindowType.Widget)
self.is_synced_to_container = False
self.syntax = syntax
self.editor = TextEdit(self)
self.editor.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.editor.customContextMenuRequested.connect(self.show_context_menu)
self.setCentralWidget(self.editor)
self.create_toolbars()
self.undo_available = False
self.redo_available = False
self.copy_available = self.cut_available = False
self.editor.modificationChanged.connect(self._modification_state_changed)
self.editor.undoAvailable.connect(self._undo_available)
self.editor.redoAvailable.connect(self._redo_available)
self.editor.textChanged.connect(self._data_changed)
self.editor.copyAvailable.connect(self._copy_available)
self.editor.cursorPositionChanged.connect(self._cursor_position_changed)
self.editor.link_clicked.connect(self.link_clicked)
self.editor.class_clicked.connect(self.class_clicked)
self.editor.smart_highlighting_updated.connect(self.smart_highlighting_updated)
@property
def current_line(self):
return self.editor.textCursor().blockNumber()
@current_line.setter
def current_line(self, val):
self.editor.go_to_line(val)
@property
def current_editing_state(self):
c = self.editor.textCursor()
return {'cursor':(c.anchor(), c.position())}
@current_editing_state.setter
def current_editing_state(self, val):
anchor, position = val.get('cursor', (None, None))
if anchor is not None and position is not None:
c = self.editor.textCursor()
c.setPosition(anchor), c.setPosition(position, QTextCursor.MoveMode.KeepAnchor)
self.editor.setTextCursor(c)
def current_tag(self, for_position_sync=True):
return self.editor.current_tag(for_position_sync=for_position_sync)
@property
def highlighter(self):
return self.editor.highlighter
@property
def number_of_lines(self):
return self.editor.blockCount()
@property
def data(self):
ans = self.get_raw_data()
ans, changed = replace_encoding_declarations(ans, enc='utf-8', limit=4*1024)
if changed:
self.data = ans
return ans.encode('utf-8')
@data.setter
def data(self, val):
self.editor.load_text(val, syntax=self.syntax, doc_name=editor_name(self))
def init_from_template(self, template):
self.editor.load_text(template, syntax=self.syntax, process_template=True, doc_name=editor_name(self))
def change_document_name(self, newname):
self.editor.change_document_name(newname)
self.editor.completion_doc_name = newname
def get_raw_data(self):
# The EPUB spec requires NFC normalization, see section 1.3.6 of
# http://www.idpf.org/epub/20/spec/OPS_2.0.1_draft.htm
return unicodedata.normalize('NFC', str(self.editor.toPlainText()).rstrip('\0'))
def replace_data(self, raw, only_if_different=True):
if isinstance(raw, bytes):
raw = raw.decode('utf-8')
current = self.get_raw_data() if only_if_different else False
if current != raw:
self.editor.replace_text(raw)
def apply_settings(self, prefs=None, dictionaries_changed=False):
self.editor.apply_settings(prefs=None, dictionaries_changed=dictionaries_changed)
def set_focus(self):
self.editor.setFocus(Qt.FocusReason.OtherFocusReason)
def action_triggered(self, action):
action, args = action[0], action[1:]
func = getattr(self.editor, action)
func(*args)
def insert_image(self, href, fullpage=False, preserve_aspect_ratio=False, width=-1, height=-1):
self.editor.insert_image(href, fullpage=fullpage, preserve_aspect_ratio=preserve_aspect_ratio, width=width, height=height)
def insert_hyperlink(self, href, text, template=None):
self.editor.insert_hyperlink(href, text, template=template)
def _build_insert_tag_button_menu(self):
m = self.insert_tag_menu
m.clear()
names = tprefs['insert_tag_mru']
for name in names:
m.addAction(name, partial(self.insert_tag, name))
m.addSeparator()
m.addAction(_('Add a tag to this menu'), self.add_insert_tag)
if names:
m = m.addMenu(_('Remove from this menu'))
for name in names:
m.addAction(name, partial(self.remove_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 add_insert_tag(self):
name, ok = QInputDialog.getText(self, _('Name of tag to add'), _(
'Enter the name of the tag'))
if ok:
mru = tprefs['insert_tag_mru']
mru.insert(0, name)
tprefs['insert_tag_mru'] = mru
self._build_insert_tag_button_menu()
def remove_insert_tag(self, name):
mru = tprefs['insert_tag_mru']
try:
mru.remove(name)
except ValueError:
pass
tprefs['insert_tag_mru'] = mru
self._build_insert_tag_button_menu()
def set_request_completion(self, callback=None, doc_name=None):
self.editor.request_completion = callback
self.editor.completion_doc_name = doc_name
def handle_completion_result(self, result):
return self.editor.handle_completion_result(result)
def undo(self):
self.editor.undo()
def redo(self):
self.editor.redo()
@property
def selected_text(self):
return self.editor.selected_text
def get_smart_selection(self, update=True):
return self.editor.smarts.get_smart_selection(self.editor, update=update)
# Search and replace {{{
def mark_selected_text(self):
self.editor.mark_selected_text()
def find(self, *args, **kwargs):
return self.editor.find(*args, **kwargs)
def find_text(self, *args, **kwargs):
return self.editor.find_text(*args, **kwargs)
def find_spell_word(self, *args, **kwargs):
return self.editor.find_spell_word(*args, **kwargs)
def replace(self, *args, **kwargs):
return self.editor.replace(*args, **kwargs)
def all_in_marked(self, *args, **kwargs):
return self.editor.all_in_marked(*args, **kwargs)
def go_to_anchor(self, *args, **kwargs):
return self.editor.go_to_anchor(*args, **kwargs)
# }}}
@property
def has_marked_text(self):
return self.editor.current_search_mark is not None
@property
def is_modified(self):
return self.editor.is_modified
@is_modified.setter
def is_modified(self, val):
self.editor.is_modified = val
def create_toolbars(self):
self.action_bar = b = self.addToolBar(_('Edit actions tool bar'))
b.setObjectName('action_bar') # Needed for saveState
self.tools_bar = b = self.addToolBar(_('Editor tools'))
b.setObjectName('tools_bar')
self.bars = [self.action_bar, self.tools_bar]
if self.syntax == 'html':
self.format_bar = b = self.addToolBar(_('Format text'))
b.setObjectName('html_format_bar')
self.bars.append(self.format_bar)
self.insert_tag_menu = QMenu(self)
self.populate_toolbars()
for x in self.bars:
x.setFloatable(False)
x.topLevelChanged.connect(self.toolbar_floated)
x.setIconSize(QSize(tprefs['toolbar_icon_size'], tprefs['toolbar_icon_size']))
def toolbar_floated(self, floating):
if not floating:
self.save_state()
for ed in itervalues(editors):
if ed is not self:
ed.restore_state()
def save_state(self):
for bar in self.bars:
if bar.isFloating():
return
tprefs['%s-editor-state' % self.syntax] = bytearray(self.saveState())
def restore_state(self):
state = tprefs.get('%s-editor-state' % self.syntax, None)
if state is not None:
self.restoreState(state)
for bar in self.bars:
bar.setVisible(len(bar.actions()) > 0)
def populate_toolbars(self):
self.action_bar.clear(), self.tools_bar.clear()
def add_action(name, bar):
if name is None:
bar.addSeparator()
return
try:
ac = actions[name]
except KeyError:
if DEBUG:
prints('Unknown editor tool: %r' % name)
return
bar.addAction(ac)
if name == 'insert-tag':
w = bar.widgetForAction(ac)
if hasattr(w, 'setPopupMode'):
# For some unknown reason this button is occasionally a
# QPushButton instead of a QToolButton
w.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
w.setMenu(self.insert_tag_menu)
w.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
w.customContextMenuRequested.connect(w.showMenu)
self._build_insert_tag_button_menu()
elif name == 'change-paragraph':
m = ac.m = QMenu()
ac.setMenu(m)
ch = bar.widgetForAction(ac)
if hasattr(ch, 'setPopupMode'):
# For some unknown reason this button is occasionally a
# QPushButton instead of a QToolButton
ch.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
for name in tuple('h%d' % d for d in range(1, 7)) + ('p',):
m.addAction(actions['rename-block-tag-%s' % name])
for name in tprefs.get('editor_common_toolbar', ()):
add_action(name, self.action_bar)
for name in tprefs.get('editor_%s_toolbar' % self.syntax, ()):
add_action(name, self.tools_bar)
if self.syntax == 'html':
self.format_bar.clear()
for name in tprefs['editor_format_toolbar']:
add_action(name, self.format_bar)
self.restore_state()
def break_cycles(self):
for x in ('modification_state_changed', 'word_ignored', 'link_clicked', 'class_clicked', 'smart_highlighting_updated'):
try:
getattr(self, x).disconnect()
except TypeError:
pass # in case this signal was never connected
self.undo_redo_state_changed.disconnect()
self.copy_available_state_changed.disconnect()
self.cursor_position_changed.disconnect()
self.data_changed.disconnect()
self.editor.undoAvailable.disconnect()
self.editor.redoAvailable.disconnect()
self.editor.modificationChanged.disconnect()
self.editor.textChanged.disconnect()
self.editor.copyAvailable.disconnect()
self.editor.cursorPositionChanged.disconnect()
self.editor.link_clicked.disconnect()
self.editor.class_clicked.disconnect()
self.editor.smart_highlighting_updated.disconnect()
self.editor.setPlainText('')
self.editor.smarts = None
self.editor.request_completion = None
def _modification_state_changed(self):
self.is_synced_to_container = self.is_modified
self.modification_state_changed.emit(self.is_modified)
def _data_changed(self):
self.is_synced_to_container = False
self.data_changed.emit(self)
def _undo_available(self, available):
self.undo_available = available
self.undo_redo_state_changed.emit(self.undo_available, self.redo_available)
def _redo_available(self, available):
self.redo_available = available
self.undo_redo_state_changed.emit(self.undo_available, self.redo_available)
def _copy_available(self, available):
self.copy_available = self.cut_available = available
self.copy_available_state_changed.emit(available)
def _cursor_position_changed(self, *args):
self.cursor_position_changed.emit()
@property
def cursor_position(self):
c = self.editor.textCursor()
char = ''
col = c.positionInBlock()
if not c.atStart():
c.clearSelection()
c.movePosition(QTextCursor.MoveOperation.PreviousCharacter, QTextCursor.MoveMode.KeepAnchor)
char = str(c.selectedText()).rstrip('\0')
return (c.blockNumber() + 1, col, char)
def cut(self):
self.editor.cut()
def copy(self):
self.editor.copy()
def go_to_line(self, line, col=None):
self.editor.go_to_line(line, col=col)
def paste(self):
if not self.editor.canPaste():
return error_dialog(self, _('No text'), _(
'There is no suitable text in the clipboard to paste.'), show=True)
self.editor.paste()
def contextMenuEvent(self, ev):
ev.ignore()
def fix_html(self):
if self.syntax == 'html':
from calibre.ebooks.oeb.polish.pretty import fix_html
self.editor.replace_text(fix_html(current_container(), str(self.editor.toPlainText())).decode('utf-8'))
return True
return False
def pretty_print(self, name):
from calibre.ebooks.oeb.polish.pretty import (
pretty_css, pretty_html, pretty_xml
)
if self.syntax in {'css', 'html', 'xml'}:
func = {'css':pretty_css, 'xml':pretty_xml}.get(self.syntax, pretty_html)
original_text = str(self.editor.toPlainText())
prettied_text = func(current_container(), name, original_text).decode('utf-8')
if original_text != prettied_text:
self.editor.replace_text(prettied_text)
return True
return False
def show_context_menu(self, pos):
m = QMenu(self)
a = m.addAction
c = self.editor.cursorForPosition(pos)
origc = QTextCursor(c)
current_cursor = self.editor.textCursor()
r = origr = self.editor.syntax_range_for_cursor(c)
if (r is None or not r.format.property(SPELL_PROPERTY)) and c.positionInBlock() > 0 and not current_cursor.hasSelection():
c.setPosition(c.position() - 1)
r = self.editor.syntax_range_for_cursor(c)
if r is not None and r.format.property(SPELL_PROPERTY):
word = self.editor.text_for_range(c.block(), r)
locale = self.editor.spellcheck_locale_for_cursor(c)
orig_pos = c.position()
c.setPosition(orig_pos - utf16_length(word))
found = False
self.editor.setTextCursor(c)
if self.editor.find_spell_word([word], locale.langcode, center_on_cursor=False):
found = True
fc = self.editor.textCursor()
if fc.position() < c.position():
self.editor.find_spell_word([word], locale.langcode, center_on_cursor=False)
spell_cursor = self.editor.textCursor()
if current_cursor.hasSelection():
# Restore the current cursor so that any selection is preserved
# for the change case actions
self.editor.setTextCursor(current_cursor)
if found:
suggestions = dictionaries.suggestions(word, locale)[:7]
if suggestions:
for suggestion in suggestions:
ac = m.addAction(suggestion, partial(self.editor.simple_replace, suggestion, cursor=spell_cursor))
f = ac.font()
f.setBold(True), ac.setFont(f)
m.addSeparator()
m.addAction(actions['spell-next'])
m.addAction(_('Ignore this word'), partial(self._nuke_word, None, word, locale))
dics = dictionaries.active_user_dictionaries
if len(dics) > 0:
if len(dics) == 1:
m.addAction(_('Add this word to the dictionary: {0}').format(dics[0].name), partial(
self._nuke_word, dics[0].name, word, locale))
else:
ac = m.addAction(_('Add this word to the dictionary'))
dmenu = QMenu(m)
ac.setMenu(dmenu)
for dic in dics:
dmenu.addAction(dic.name, partial(self._nuke_word, dic.name, word, locale))
m.addSeparator()
if origr is not None and origr.format.property(LINK_PROPERTY):
href = self.editor.text_for_range(origc.block(), origr)
m.addAction(_('Open %s') % href, partial(self.link_clicked.emit, href))
if origr is not None and origr.format.property(CLASS_ATTRIBUTE_PROPERTY):
cls = self.editor.class_for_position(pos)
if cls:
class_name = cls['class']
m.addAction(_('Rename the class {}').format(class_name), partial(self.rename_class.emit, class_name))
if origr is not None and (origr.format.property(TAG_NAME_PROPERTY) or origr.format.property(CSS_PROPERTY)):
word = self.editor.text_for_range(origc.block(), origr)
item_type = 'tag_name' if origr.format.property(TAG_NAME_PROPERTY) else 'css_property'
url = help_url(word, item_type, self.editor.highlighter.doc_name, extra_data=current_container().opf_version)
if url is not None:
m.addAction(_('Show help for: %s') % word, partial(open_url, url))
for x in ('undo', 'redo'):
ac = actions['editor-%s' % x]
if ac.isEnabled():
a(ac)
m.addSeparator()
for x in ('cut', 'copy', 'paste'):
ac = actions['editor-' + x]
if ac.isEnabled():
a(ac)
m.addSeparator()
m.addAction(_('&Select all'), self.editor.select_all)
if self.selected_text or self.has_marked_text:
update_mark_text_action(self)
m.addAction(actions['mark-selected-text'])
if self.syntax != 'css' and actions['editor-cut'].isEnabled():
cm = QMenu(_('C&hange case'), m)
for ac in 'upper lower swap title capitalize'.split():
cm.addAction(actions['transform-case-' + ac])
m.addMenu(cm)
if self.syntax == 'html':
m.addAction(actions['multisplit'])
m.exec(self.editor.viewport().mapToGlobal(pos))
def goto_sourceline(self, *args, **kwargs):
return self.editor.goto_sourceline(*args, **kwargs)
def goto_css_rule(self, *args, **kwargs):
return self.editor.goto_css_rule(*args, **kwargs)
def get_tag_contents(self, *args, **kwargs):
return self.editor.get_tag_contents(*args, **kwargs)
def _nuke_word(self, dic, word, locale):
if dic is None:
dictionaries.ignore_word(word, locale)
else:
dictionaries.add_to_user_dictionary(dic, word, locale)
self.word_ignored.emit(word, locale)
def launch_editor(path_to_edit, path_is_raw=False, syntax='html', callback=None):
from calibre.gui2 import Application
from calibre.gui2.tweak_book import dictionaries
from calibre.gui2.tweak_book.editor.syntax.html import refresh_spell_check_status
from calibre.gui2.tweak_book.main import option_parser
from calibre.gui2.tweak_book.ui import Main
dictionaries.initialize()
refresh_spell_check_status()
opts = option_parser().parse_args([])
app = Application([])
# Create the actions that are placed into the editors toolbars
main = Main(opts) # noqa
if path_is_raw:
raw = path_to_edit
else:
with open(path_to_edit, 'rb') as f:
raw = f.read().decode('utf-8')
ext = path_to_edit.rpartition('.')[-1].lower()
if ext in ('html', 'htm', 'xhtml', 'xhtm'):
syntax = 'html'
elif ext in ('css',):
syntax = 'css'
t = Editor(syntax)
t.data = raw
if callback is not None:
callback(t)
t.show()
app.exec()