mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Work on replacing webkit in the comments editor
This commit is contained in:
parent
371e5f9813
commit
25eb65e156
@ -1,151 +1,103 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python2
|
||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
# License: GPLv3 Copyright: 2010, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
import json
|
||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
import os
|
||||||
__docformat__ = 'restructuredtext en'
|
import re
|
||||||
|
import weakref
|
||||||
|
|
||||||
import re, os, json, weakref
|
|
||||||
|
|
||||||
from lxml import html
|
|
||||||
|
|
||||||
from PyQt5.Qt import (QApplication, QFontInfo, QSize, QWidget, QPlainTextEdit,
|
|
||||||
QToolBar, QVBoxLayout, QAction, QIcon, Qt, QTabWidget, QUrl, QFormLayout,
|
|
||||||
QSyntaxHighlighter, QColor, QColorDialog, QMenu, QDialog, QLabel,
|
|
||||||
QHBoxLayout, QKeySequence, QLineEdit, QDialogButtonBox, QPushButton,
|
|
||||||
pyqtSignal, QCheckBox)
|
|
||||||
from PyQt5.QtWebKitWidgets import QWebView, QWebPage
|
|
||||||
try:
|
|
||||||
from PyQt5 import sip
|
|
||||||
except ImportError:
|
|
||||||
import sip
|
|
||||||
|
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
|
||||||
from calibre import xml_replace_entities, prepare_string_for_xml
|
|
||||||
from calibre.gui2 import open_url, error_dialog, choose_files, gprefs, NO_URL_FORMATTING, secure_web_page
|
|
||||||
from calibre.gui2.widgets import LineEditECM
|
|
||||||
from html5_parser import parse
|
from html5_parser import parse
|
||||||
|
from lxml import html
|
||||||
|
from PyQt5.Qt import (
|
||||||
|
QAction, QApplication, QByteArray, QCheckBox, QColor, QColorDialog, QDialog,
|
||||||
|
QDialogButtonBox, QFontInfo, QFormLayout, QHBoxLayout, QIcon, QKeySequence,
|
||||||
|
QLabel, QLineEdit, QMenu, QPlainTextEdit, QPushButton, QSize, QSyntaxHighlighter,
|
||||||
|
Qt, QTabWidget, QTextEdit, QToolBar, QUrl, QVBoxLayout, QWidget, pyqtSignal,
|
||||||
|
pyqtSlot
|
||||||
|
)
|
||||||
|
|
||||||
|
from calibre import prepare_string_for_xml, xml_replace_entities
|
||||||
|
from calibre.ebooks.chardet import xml_to_unicode
|
||||||
|
from calibre.gui2 import NO_URL_FORMATTING, choose_files, error_dialog, gprefs
|
||||||
|
from calibre.gui2.widgets import LineEditECM
|
||||||
from calibre.utils.config import tweaks
|
from calibre.utils.config import tweaks
|
||||||
from calibre.utils.imghdr import what
|
from calibre.utils.imghdr import what
|
||||||
from polyglot.builtins import unicode_type
|
from polyglot.builtins import unicode_type
|
||||||
|
|
||||||
|
|
||||||
class PageAction(QAction): # {{{
|
class EditorWidget(QTextEdit, LineEditECM): # {{{
|
||||||
|
|
||||||
def __init__(self, wac, icon, text, checkable, view):
|
|
||||||
QAction.__init__(self, QIcon(I(icon+'.png')), text, view)
|
|
||||||
self._page_action = getattr(QWebPage, wac)
|
|
||||||
self.setCheckable(checkable)
|
|
||||||
self.triggered.connect(self.trigger_page_action)
|
|
||||||
view.selectionChanged.connect(self.update_state,
|
|
||||||
type=Qt.QueuedConnection)
|
|
||||||
self.page_action.changed.connect(self.update_state,
|
|
||||||
type=Qt.QueuedConnection)
|
|
||||||
self.update_state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def page_action(self):
|
|
||||||
return self.parent().pageAction(self._page_action)
|
|
||||||
|
|
||||||
def trigger_page_action(self, *args):
|
|
||||||
self.page_action.trigger()
|
|
||||||
|
|
||||||
def update_state(self, *args):
|
|
||||||
if sip.isdeleted(self) or sip.isdeleted(self.page_action):
|
|
||||||
return
|
|
||||||
if self.isCheckable():
|
|
||||||
self.setChecked(self.page_action.isChecked())
|
|
||||||
self.setEnabled(self.page_action.isEnabled())
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
class BlockStyleAction(QAction): # {{{
|
|
||||||
|
|
||||||
def __init__(self, text, name, view):
|
|
||||||
QAction.__init__(self, text, view)
|
|
||||||
self._name = name
|
|
||||||
self.triggered.connect(self.apply_style)
|
|
||||||
|
|
||||||
def apply_style(self, *args):
|
|
||||||
self.parent().exec_command('formatBlock', self._name)
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
class EditorWidget(QWebView, LineEditECM): # {{{
|
|
||||||
|
|
||||||
data_changed = pyqtSignal()
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def readonly(self):
|
||||||
|
return self.isReadOnly()
|
||||||
|
|
||||||
|
@readonly.setter
|
||||||
|
def readonly(self, val):
|
||||||
|
self.setReadOnly(bool(val))
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWebView.__init__(self, parent)
|
QTextEdit.__init__(self, parent)
|
||||||
|
self.setTabChangesFocus(True)
|
||||||
|
font = self.font()
|
||||||
|
f = QFontInfo(font)
|
||||||
|
delta = tweaks['change_book_details_font_size_by'] + 1
|
||||||
|
if delta:
|
||||||
|
font.setPixelSize(f.pixelSize() + delta)
|
||||||
|
self.setFont(font)
|
||||||
self.base_url = None
|
self.base_url = None
|
||||||
self._parent = weakref.ref(parent)
|
self._parent = weakref.ref(parent)
|
||||||
self.readonly = False
|
|
||||||
|
|
||||||
self.comments_pat = re.compile(r'<!--.*?-->', re.DOTALL)
|
self.comments_pat = re.compile(r'<!--.*?-->', re.DOTALL)
|
||||||
|
|
||||||
extra_shortcuts = {
|
extra_shortcuts = {
|
||||||
'ToggleBold': 'Bold',
|
'bold': 'Bold',
|
||||||
'ToggleItalic': 'Italic',
|
'italic': 'Italic',
|
||||||
'ToggleUnderline': 'Underline',
|
'underline': 'Underline',
|
||||||
}
|
}
|
||||||
|
|
||||||
for wac, name, icon, text, checkable in [
|
for rec in (
|
||||||
('ToggleBold', 'bold', 'format-text-bold', _('Bold'), True),
|
('bold', 'format-text-bold', _('Bold'), True),
|
||||||
('ToggleItalic', 'italic', 'format-text-italic', _('Italic'),
|
('italic', 'format-text-italic', _('Italic'), True),
|
||||||
True),
|
('underline', 'format-text-underline', _('Underline'), True),
|
||||||
('ToggleUnderline', 'underline', 'format-text-underline',
|
('strikethrough', 'format-text-strikethrough', _('Strikethrough'), True),
|
||||||
_('Underline'), True),
|
('superscript', 'format-text-superscript', _('Superscript'), True),
|
||||||
('ToggleStrikethrough', 'strikethrough', 'format-text-strikethrough',
|
('subscript', 'format-text-subscript', _('Subscript'), True),
|
||||||
_('Strikethrough'), True),
|
('ordered_list', 'format-list-ordered', _('Ordered list'), True),
|
||||||
('ToggleSuperscript', 'superscript', 'format-text-superscript',
|
('unordered_list', 'format-list-unordered', _('Unordered list'), True),
|
||||||
_('Superscript'), True),
|
|
||||||
('ToggleSubscript', 'subscript', 'format-text-subscript',
|
|
||||||
_('Subscript'), True),
|
|
||||||
('InsertOrderedList', 'ordered_list', 'format-list-ordered',
|
|
||||||
_('Ordered list'), True),
|
|
||||||
('InsertUnorderedList', 'unordered_list', 'format-list-unordered',
|
|
||||||
_('Unordered list'), True),
|
|
||||||
|
|
||||||
('AlignLeft', 'align_left', 'format-justify-left',
|
('align_left', 'format-justify-left', _('Align left'), ),
|
||||||
_('Align left'), False),
|
('align_center', 'format-justify-center', _('Align center'), ),
|
||||||
('AlignCenter', 'align_center', 'format-justify-center',
|
('align_right', 'format-justify-right', _('Align right'), ),
|
||||||
_('Align center'), False),
|
('align_justified', 'format-justify-fill', _('Align justified'), ),
|
||||||
('AlignRight', 'align_right', 'format-justify-right',
|
('undo', 'edit-undo', _('Undo'), ),
|
||||||
_('Align right'), False),
|
('redo', 'edit-redo', _('Redo'), ),
|
||||||
('AlignJustified', 'align_justified', 'format-justify-fill',
|
('remove_format', 'edit-clear', _('Remove formatting'), ),
|
||||||
_('Align justified'), False),
|
('copy', 'edit-copy', _('Copy'), ),
|
||||||
('Undo', 'undo', 'edit-undo', _('Undo'), False),
|
('paste', 'edit-paste', _('Paste'), ),
|
||||||
('Redo', 'redo', 'edit-redo', _('Redo'), False),
|
('cut', 'edit-cut', _('Cut'), ),
|
||||||
('RemoveFormat', 'remove_format', 'edit-clear', _('Remove formatting'), False),
|
('indent', 'format-indent-more', _('Increase indentation'), ),
|
||||||
('Copy', 'copy', 'edit-copy', _('Copy'), False),
|
('outdent', 'format-indent-less', _('Decrease indentation'), ),
|
||||||
('Paste', 'paste', 'edit-paste', _('Paste'), False),
|
('select_all', 'edit-select-all', _('Select all'), ),
|
||||||
('Cut', 'cut', 'edit-cut', _('Cut'), False),
|
|
||||||
('Indent', 'indent', 'format-indent-more',
|
('color', 'format-text-color', _('Foreground color')),
|
||||||
_('Increase indentation'), False),
|
('background', 'format-fill-color', _('Background color')),
|
||||||
('Outdent', 'outdent', 'format-indent-less',
|
('insert_link', 'insert-link', _('Insert link or image'),),
|
||||||
_('Decrease indentation'), False),
|
('insert_hr', 'format-text-hr.png', _('Insert separator'),),
|
||||||
('SelectAll', 'select_all', 'edit-select-all',
|
('clear', 'trash.png', _('Clear')),
|
||||||
_('Select all'), False),
|
):
|
||||||
]:
|
name, icon, text = rec[:3]
|
||||||
ac = PageAction(wac, icon, text, checkable, self)
|
checkable = len(rec) == 4
|
||||||
|
ac = QAction(QIcon(I(icon + '.png')), text, self)
|
||||||
|
if checkable:
|
||||||
|
ac.setCheckable(checkable)
|
||||||
setattr(self, 'action_'+name, ac)
|
setattr(self, 'action_'+name, ac)
|
||||||
ss = extra_shortcuts.get(wac, None)
|
ss = extra_shortcuts.get(name)
|
||||||
if ss:
|
if ss is not None:
|
||||||
ac.setShortcut(QKeySequence(getattr(QKeySequence, ss)))
|
ac.setShortcut(QKeySequence(getattr(QKeySequence, ss)))
|
||||||
if wac == 'RemoveFormat':
|
ac.triggered.connect(getattr(self, 'do_' + name))
|
||||||
ac.triggered.connect(self.remove_format_cleanup,
|
|
||||||
type=Qt.QueuedConnection)
|
|
||||||
|
|
||||||
self.action_color = QAction(QIcon(I('format-text-color.png')), _('Foreground color'),
|
|
||||||
self)
|
|
||||||
self.action_color.triggered.connect(self.foreground_color)
|
|
||||||
|
|
||||||
self.action_background = QAction(QIcon(I('format-fill-color.png')),
|
|
||||||
_('Background color'), self)
|
|
||||||
self.action_background.triggered.connect(self.background_color)
|
|
||||||
|
|
||||||
self.action_block_style = QAction(QIcon(I('format-text-heading.png')),
|
self.action_block_style = QAction(QIcon(I('format-text-heading.png')),
|
||||||
_('Style text block'), self)
|
_('Style text block'), self)
|
||||||
@ -153,81 +105,124 @@ class EditorWidget(QWebView, LineEditECM): # {{{
|
|||||||
_('Style the selected text block'))
|
_('Style the selected text block'))
|
||||||
self.block_style_menu = QMenu(self)
|
self.block_style_menu = QMenu(self)
|
||||||
self.action_block_style.setMenu(self.block_style_menu)
|
self.action_block_style.setMenu(self.block_style_menu)
|
||||||
self.block_style_actions = []
|
h = _('Heading {0}')
|
||||||
for text, name in [
|
for text, name in (
|
||||||
(_('Normal'), 'p'),
|
(_('Normal'), 'p'),
|
||||||
(_('Heading') +' 1', 'h1'),
|
(h.format(1), 'h1'),
|
||||||
(_('Heading') +' 2', 'h2'),
|
(h.format(2), 'h2'),
|
||||||
(_('Heading') +' 3', 'h3'),
|
(h.format(3), 'h3'),
|
||||||
(_('Heading') +' 4', 'h4'),
|
(h.format(4), 'h4'),
|
||||||
(_('Heading') +' 5', 'h5'),
|
(h.format(5), 'h5'),
|
||||||
(_('Heading') +' 6', 'h6'),
|
(h.format(6), 'h6'),
|
||||||
(_('Pre-formatted'), 'pre'),
|
(_('Pre-formatted'), 'pre'),
|
||||||
(_('Blockquote'), 'blockquote'),
|
(_('Blockquote'), 'blockquote'),
|
||||||
(_('Address'), 'address'),
|
(_('Address'), 'address'),
|
||||||
]:
|
):
|
||||||
ac = BlockStyleAction(text, name, self)
|
ac = QAction(text, self)
|
||||||
self.block_style_menu.addAction(ac)
|
self.block_style_menu.addAction(ac)
|
||||||
self.block_style_actions.append(ac)
|
connect_lambda(ac.triggered, self, lambda self: self.do_format_block(name))
|
||||||
|
|
||||||
self.action_insert_link = QAction(QIcon(I('insert-link.png')),
|
|
||||||
_('Insert link or image'), self)
|
|
||||||
self.action_insert_hr = QAction(QIcon(I('format-text-hr.png')),
|
|
||||||
_('Insert separator'), self)
|
|
||||||
self.action_insert_link.triggered.connect(self.insert_link)
|
|
||||||
self.action_insert_hr.triggered.connect(self.insert_hr)
|
|
||||||
self.pageAction(QWebPage.ToggleBold).changed.connect(self.update_link_action)
|
|
||||||
self.action_insert_link.setEnabled(False)
|
|
||||||
self.action_insert_hr.setEnabled(False)
|
|
||||||
self.action_clear = QAction(QIcon(I('trash.png')), _('Clear'), self)
|
|
||||||
self.action_clear.triggered.connect(self.clear_text)
|
|
||||||
|
|
||||||
self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
|
|
||||||
self.page().linkClicked.connect(self.link_clicked)
|
|
||||||
secure_web_page(self.page().settings())
|
|
||||||
|
|
||||||
self.setHtml('')
|
self.setHtml('')
|
||||||
self.set_readonly(False)
|
self.textChanged.connect(self.data_changed)
|
||||||
self.page().contentsChanged.connect(self.data_changed)
|
|
||||||
|
|
||||||
def update_link_action(self):
|
|
||||||
wac = self.pageAction(QWebPage.ToggleBold).isEnabled()
|
|
||||||
self.action_insert_link.setEnabled(wac)
|
|
||||||
self.action_insert_hr.setEnabled(wac)
|
|
||||||
|
|
||||||
def set_readonly(self, what):
|
def set_readonly(self, what):
|
||||||
self.readonly = what
|
self.readonly = what
|
||||||
self.page().setContentEditable(not self.readonly)
|
|
||||||
|
|
||||||
def clear_text(self, *args):
|
def do_clear(self, *args):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
us = self.page().undoStack()
|
us = self.page().undoStack()
|
||||||
us.beginMacro('clear all text')
|
us.beginMacro('clear all text')
|
||||||
self.action_select_all.trigger()
|
self.action_select_all.trigger()
|
||||||
self.action_remove_format.trigger()
|
self.action_remove_format.trigger()
|
||||||
self.exec_command('delete')
|
self.exec_command('delete')
|
||||||
us.endMacro()
|
us.endMacro()
|
||||||
self.set_font_style()
|
|
||||||
self.setFocus(Qt.OtherFocusReason)
|
self.setFocus(Qt.OtherFocusReason)
|
||||||
|
clear_text = do_clear
|
||||||
|
|
||||||
def link_clicked(self, url):
|
def do_bold(self):
|
||||||
open_url(url)
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
def foreground_color(self):
|
def do_italic(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_underline(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_strikethrough(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_superscript(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_subscript(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_ordered_list(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_unordered_list(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_align_left(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_align_center(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_align_right(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_align_justified(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_undo(self):
|
||||||
|
self.undo()
|
||||||
|
|
||||||
|
def do_redo(self):
|
||||||
|
self.redo()
|
||||||
|
|
||||||
|
def do_remove_format(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_copy(self):
|
||||||
|
self.copy()
|
||||||
|
|
||||||
|
def do_paste(self):
|
||||||
|
self.paste()
|
||||||
|
|
||||||
|
def do_cut(self):
|
||||||
|
self.cut()
|
||||||
|
|
||||||
|
def do_indent(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_outdent(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_select_all(self):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_format_block(self, name):
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
|
def do_color(self):
|
||||||
col = QColorDialog.getColor(Qt.black, self,
|
col = QColorDialog.getColor(Qt.black, self,
|
||||||
_('Choose foreground color'), QColorDialog.ShowAlphaChannel)
|
_('Choose foreground color'), QColorDialog.ShowAlphaChannel)
|
||||||
if col.isValid():
|
if col.isValid():
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
self.exec_command('foreColor', unicode_type(col.name()))
|
self.exec_command('foreColor', unicode_type(col.name()))
|
||||||
|
|
||||||
def background_color(self):
|
def do_background(self):
|
||||||
col = QColorDialog.getColor(Qt.white, self,
|
col = QColorDialog.getColor(Qt.white, self,
|
||||||
_('Choose background color'), QColorDialog.ShowAlphaChannel)
|
_('Choose background color'), QColorDialog.ShowAlphaChannel)
|
||||||
if col.isValid():
|
if col.isValid():
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
self.exec_command('hiliteColor', unicode_type(col.name()))
|
self.exec_command('hiliteColor', unicode_type(col.name()))
|
||||||
|
|
||||||
def insert_hr(self, *args):
|
def do_insert_hr(self, *args):
|
||||||
self.exec_command('insertHTML', '<hr>')
|
raise NotImplementedError('TODO')
|
||||||
|
|
||||||
def insert_link(self, *args):
|
def do_insert_link(self, *args):
|
||||||
link, name, is_image = self.ask_link()
|
link, name, is_image = self.ask_link()
|
||||||
if not link:
|
if not link:
|
||||||
return
|
return
|
||||||
@ -235,6 +230,7 @@ class EditorWidget(QWebView, LineEditECM): # {{{
|
|||||||
if url.isValid():
|
if url.isValid():
|
||||||
url = unicode_type(url.toString(NO_URL_FORMATTING))
|
url = unicode_type(url.toString(NO_URL_FORMATTING))
|
||||||
self.setFocus(Qt.OtherFocusReason)
|
self.setFocus(Qt.OtherFocusReason)
|
||||||
|
raise NotImplementedError('TODO')
|
||||||
if is_image:
|
if is_image:
|
||||||
self.exec_command('insertHTML',
|
self.exec_command('insertHTML',
|
||||||
'<img src="%s" alt="%s"></img>'%(prepare_string_for_xml(url, True),
|
'<img src="%s" alt="%s"></img>'%(prepare_string_for_xml(url, True),
|
||||||
@ -325,18 +321,6 @@ class EditorWidget(QWebView, LineEditECM): # {{{
|
|||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
return QSize(150, 150)
|
return QSize(150, 150)
|
||||||
|
|
||||||
def exec_command(self, cmd, arg=None):
|
|
||||||
frame = self.page().mainFrame()
|
|
||||||
if arg is not None:
|
|
||||||
js = 'document.execCommand("%s", false, %s);' % (cmd,
|
|
||||||
json.dumps(unicode_type(arg)))
|
|
||||||
else:
|
|
||||||
js = 'document.execCommand("%s", false, null);' % cmd
|
|
||||||
frame.evaluateJavaScript(js)
|
|
||||||
|
|
||||||
def remove_format_cleanup(self):
|
|
||||||
self.html = self.html
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def html(self):
|
def html(self):
|
||||||
ans = ''
|
ans = ''
|
||||||
@ -371,7 +355,7 @@ class EditorWidget(QWebView, LineEditECM): # {{{
|
|||||||
if not ans.startswith('<'):
|
if not ans.startswith('<'):
|
||||||
ans = '<p>%s</p>'%ans
|
ans = '<p>%s</p>'%ans
|
||||||
ans = xml_replace_entities(ans)
|
ans = xml_replace_entities(ans)
|
||||||
except:
|
except Exception:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
@ -379,15 +363,25 @@ class EditorWidget(QWebView, LineEditECM): # {{{
|
|||||||
|
|
||||||
@html.setter
|
@html.setter
|
||||||
def html(self, val):
|
def html(self, val):
|
||||||
if self.base_url is None:
|
self.setHtml(val)
|
||||||
self.setHtml(val)
|
|
||||||
else:
|
|
||||||
self.setHtml(val, self.base_url)
|
|
||||||
self.set_font_style()
|
|
||||||
|
|
||||||
def set_base_url(self, qurl):
|
def set_base_url(self, qurl):
|
||||||
self.base_url = qurl
|
self.base_url = qurl
|
||||||
self.setHtml('', self.base_url)
|
|
||||||
|
@pyqtSlot(int, 'QUrl', result='QVariant')
|
||||||
|
def loadResource(self, rtype, qurl):
|
||||||
|
if self.base_url:
|
||||||
|
if qurl.isRelative():
|
||||||
|
qurl = self.base_url.resolved(qurl)
|
||||||
|
if qurl.isLocalFile():
|
||||||
|
path = qurl.toLocalFile()
|
||||||
|
try:
|
||||||
|
with lopen(path, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
except EnvironmentError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return QByteArray(data)
|
||||||
|
|
||||||
def set_html(self, val, allow_undo=True):
|
def set_html(self, val, allow_undo=True):
|
||||||
if not allow_undo or self.readonly:
|
if not allow_undo or self.readonly:
|
||||||
@ -396,44 +390,22 @@ class EditorWidget(QWebView, LineEditECM): # {{{
|
|||||||
mf = self.page().mainFrame()
|
mf = self.page().mainFrame()
|
||||||
mf.evaluateJavaScript('document.execCommand("selectAll", false, null)')
|
mf.evaluateJavaScript('document.execCommand("selectAll", false, null)')
|
||||||
mf.evaluateJavaScript('document.execCommand("insertHTML", false, %s)' % json.dumps(unicode_type(val)))
|
mf.evaluateJavaScript('document.execCommand("insertHTML", false, %s)' % json.dumps(unicode_type(val)))
|
||||||
self.set_font_style()
|
|
||||||
|
|
||||||
def set_font_style(self):
|
|
||||||
fi = QFontInfo(QApplication.font(self))
|
|
||||||
f = fi.pixelSize() + 1 + int(tweaks['change_book_details_font_size_by'])
|
|
||||||
fam = unicode_type(fi.family()).strip().replace('"', '')
|
|
||||||
if not fam:
|
|
||||||
fam = 'sans-serif'
|
|
||||||
style = 'font-size: %fpx; font-family:"%s",sans-serif;' % (f, fam)
|
|
||||||
|
|
||||||
# toList() is needed because PyQt on Debian is old/broken
|
|
||||||
for body in self.page().mainFrame().documentElement().findAll('body').toList():
|
|
||||||
body.setAttribute('style', style)
|
|
||||||
self.page().setContentEditable(not self.readonly)
|
|
||||||
|
|
||||||
def event(self, ev):
|
|
||||||
if ev.type() in (ev.KeyPress, ev.KeyRelease, ev.ShortcutOverride) and hasattr(ev, 'key') and ev.key() in (
|
|
||||||
Qt.Key_Tab, Qt.Key_Escape, Qt.Key_Backtab):
|
|
||||||
if (ev.key() == Qt.Key_Tab and ev.modifiers() & Qt.ControlModifier and ev.type() == ev.KeyPress):
|
|
||||||
self.exec_command('insertHTML', '<span style="white-space:pre">\t</span>')
|
|
||||||
ev.accept()
|
|
||||||
return True
|
|
||||||
ev.ignore()
|
|
||||||
return False
|
|
||||||
return QWebView.event(self, ev)
|
|
||||||
|
|
||||||
def text(self):
|
def text(self):
|
||||||
return self.page().selectedText()
|
return self.textCursor().selectedText()
|
||||||
|
|
||||||
def setText(self, text):
|
def setText(self, text):
|
||||||
self.exec_command('insertText', text)
|
c = self.textCursor()
|
||||||
|
c.insertText(text)
|
||||||
|
self.setTextCursor(c)
|
||||||
|
|
||||||
def contextMenuEvent(self, ev):
|
def contextMenuEvent(self, ev):
|
||||||
menu = self.page().createStandardContextMenu()
|
menu = self.createStandardContextMenu()
|
||||||
paste = self.pageAction(QWebPage.Paste)
|
# paste = self.pageAction(QWebPage.Paste)
|
||||||
for action in menu.actions():
|
for action in menu.actions():
|
||||||
if action == paste:
|
pass
|
||||||
menu.insertAction(action, self.pageAction(QWebPage.PasteAndMatchStyle))
|
# if action == paste:
|
||||||
|
# menu.insertAction(action, self.pageAction(QWebPage.PasteAndMatchStyle))
|
||||||
st = self.text()
|
st = self.text()
|
||||||
if st and st.strip():
|
if st and st.strip():
|
||||||
self.create_change_case_menu(menu)
|
self.create_change_case_menu(menu)
|
||||||
@ -757,7 +729,7 @@ class Editor(QWidget): # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
self.code_edit.textChanged.connect(self.code_dirtied)
|
self.code_edit.textChanged.connect(self.code_dirtied)
|
||||||
self.editor.page().contentsChanged.connect(self.wyswyg_dirtied)
|
self.editor.data_changed.connect(self.wyswyg_dirtied)
|
||||||
|
|
||||||
def set_minimum_height_for_editor(self, val):
|
def set_minimum_height_for_editor(self, val):
|
||||||
self.editor.setMinimumHeight(val)
|
self.editor.setMinimumHeight(val)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user