mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Start work on the check book tool
This commit is contained in:
parent
ccd2fc524c
commit
c69316ecb5
10
src/calibre/ebooks/oeb/polish/check/__init__.py
Normal file
10
src/calibre/ebooks/oeb/polish/check/__init__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
|
||||||
|
|
56
src/calibre/ebooks/oeb/polish/check/base.py
Normal file
56
src/calibre/ebooks/oeb/polish/check/base.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from multiprocessing.pool import ThreadPool
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from calibre import detect_ncpus as cpu_count
|
||||||
|
|
||||||
|
DEBUG, INFO, WARN, ERROR, CRITICAL = xrange(5)
|
||||||
|
|
||||||
|
class BaseError(object):
|
||||||
|
|
||||||
|
HELP = ''
|
||||||
|
|
||||||
|
def __init__(self, msg, name, line=None, col=None):
|
||||||
|
self.msg, self.line, self.col = msg, line, col
|
||||||
|
self.name = name
|
||||||
|
self.level = ERROR
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '%s:%s (%s, %s):%s' % (self.__class__.__name__, self.name, self.line, self.col, self.msg)
|
||||||
|
|
||||||
|
__repr__ = __str__
|
||||||
|
|
||||||
|
class Worker(object):
|
||||||
|
|
||||||
|
def __init__(self, func):
|
||||||
|
self.func = func
|
||||||
|
self.result = None
|
||||||
|
self.tb = None
|
||||||
|
|
||||||
|
def worker(func, args):
|
||||||
|
try:
|
||||||
|
result = func(*args)
|
||||||
|
tb = None
|
||||||
|
except:
|
||||||
|
result = None
|
||||||
|
import traceback
|
||||||
|
tb = traceback.format_exc()
|
||||||
|
return result, tb
|
||||||
|
|
||||||
|
def run_checkers(func, args_list):
|
||||||
|
num = cpu_count()
|
||||||
|
pool = ThreadPool(num)
|
||||||
|
ans = []
|
||||||
|
for result, tb in pool.map(partial(worker, func), args_list):
|
||||||
|
if tb is not None:
|
||||||
|
raise Exception('Failed to run worker: \n%s' % tb)
|
||||||
|
ans.extend(result)
|
||||||
|
return ans
|
||||||
|
|
35
src/calibre/ebooks/oeb/polish/check/main.py
Normal file
35
src/calibre/ebooks/oeb/polish/check/main.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from future_builtins import map
|
||||||
|
|
||||||
|
from calibre.ebooks.oeb.base import OEB_DOCS
|
||||||
|
from calibre.ebooks.oeb.polish.container import guess_type
|
||||||
|
from calibre.ebooks.oeb.polish.check.base import run_checkers
|
||||||
|
from calibre.ebooks.oeb.polish.check.parsing import check_xml_parsing
|
||||||
|
|
||||||
|
def run_checks(container):
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
# Check parsing
|
||||||
|
XML_TYPES = frozenset(map(guess_type, ('a.xml', 'a.svg', 'a.opf', 'a.ncx')))
|
||||||
|
xml_items, html_items = [], []
|
||||||
|
for name, mt in container.mime_map.iteritems():
|
||||||
|
items = None
|
||||||
|
if mt in XML_TYPES:
|
||||||
|
items = xml_items
|
||||||
|
elif mt in OEB_DOCS:
|
||||||
|
items = html_items
|
||||||
|
if items is not None:
|
||||||
|
items.append((name, mt, container.open(name, 'rb').read()))
|
||||||
|
errors.extend(run_checkers(check_xml_parsing, xml_items))
|
||||||
|
errors.extend(run_checkers(check_xml_parsing, html_items))
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
46
src/calibre/ebooks/oeb/polish/check/parsing.py
Normal file
46
src/calibre/ebooks/oeb/polish/check/parsing.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from lxml.etree import XMLParser, fromstring, XMLSyntaxError
|
||||||
|
|
||||||
|
from calibre.ebooks.oeb.polish.check.base import BaseError
|
||||||
|
from calibre.ebooks.oeb.base import OEB_DOCS
|
||||||
|
|
||||||
|
class XMLParseError(BaseError):
|
||||||
|
|
||||||
|
HELP = _('A parsing error in an XML file means that the XML syntax in the file is incorrect.'
|
||||||
|
' Such a file will most probably not open in an ebook reader. These errors can '
|
||||||
|
' usually be fixed automatically, however, automatic fixing can sometimes '
|
||||||
|
' "do the wrong thing".')
|
||||||
|
|
||||||
|
def __init__(self, msg, *args, **kwargs):
|
||||||
|
BaseError.__init__(self, 'Parsing failed: ' + msg, *args, **kwargs)
|
||||||
|
|
||||||
|
class HTMLParseError(XMLParseError):
|
||||||
|
|
||||||
|
HELP = _('A parsing error in an HTML file means that the HTML syntax is incorrect.'
|
||||||
|
' Most readers will automatically ignore such errors, but they may result in '
|
||||||
|
' incorrect display of content. These errors can usually be fixed automatically,'
|
||||||
|
' however, automatic fixing can sometimes "do the wrong thing".')
|
||||||
|
|
||||||
|
def check_xml_parsing(name, mt, raw):
|
||||||
|
parser = XMLParser(recover=False)
|
||||||
|
errcls = HTMLParseError if mt in OEB_DOCS else XMLParseError
|
||||||
|
|
||||||
|
try:
|
||||||
|
fromstring(raw, parser=parser)
|
||||||
|
except XMLSyntaxError as err:
|
||||||
|
try:
|
||||||
|
line, col = err.position
|
||||||
|
except:
|
||||||
|
line = col = None
|
||||||
|
return [errcls(err.message, name, line, col)]
|
||||||
|
except Exception as err:
|
||||||
|
return [errcls(err.message, name)]
|
||||||
|
return []
|
||||||
|
|
@ -92,6 +92,8 @@ class Boss(QObject):
|
|||||||
self.gui.preview.split_start_requested.connect(self.split_start_requested)
|
self.gui.preview.split_start_requested.connect(self.split_start_requested)
|
||||||
self.gui.preview.split_requested.connect(self.split_requested)
|
self.gui.preview.split_requested.connect(self.split_requested)
|
||||||
self.gui.preview.link_clicked.connect(self.link_clicked)
|
self.gui.preview.link_clicked.connect(self.link_clicked)
|
||||||
|
self.gui.check_book.item_activated.connect(self.check_item_activated)
|
||||||
|
self.gui.check_book.check_requested.connect(self.check_requested)
|
||||||
|
|
||||||
def preferences(self):
|
def preferences(self):
|
||||||
p = Preferences(self.gui)
|
p = Preferences(self.gui)
|
||||||
@ -680,6 +682,26 @@ class Boss(QObject):
|
|||||||
if anchor:
|
if anchor:
|
||||||
editor.go_to_anchor(anchor)
|
editor.go_to_anchor(anchor)
|
||||||
|
|
||||||
|
@in_thread_job
|
||||||
|
def check_item_activated(self, item):
|
||||||
|
name = item.name
|
||||||
|
if name in editors:
|
||||||
|
editor = editors[name]
|
||||||
|
self.gui.central.show_editor(editor)
|
||||||
|
else:
|
||||||
|
syntax = syntax_from_mime(name, current_container().mime_map[name])
|
||||||
|
editor = self.edit_file(name, syntax)
|
||||||
|
if editor.has_line_numbers:
|
||||||
|
editor.go_to_line(item.line, item.col)
|
||||||
|
editor.set_focus()
|
||||||
|
|
||||||
|
@in_thread_job
|
||||||
|
def check_requested(self, *args):
|
||||||
|
c = self.gui.check_book
|
||||||
|
c.parent().show()
|
||||||
|
c.parent().raise_()
|
||||||
|
c.run_checks(current_container())
|
||||||
|
|
||||||
@in_thread_job
|
@in_thread_job
|
||||||
def merge_requested(self, category, names, master):
|
def merge_requested(self, category, names, master):
|
||||||
self.commit_all_editors_to_container()
|
self.commit_all_editors_to_container()
|
||||||
@ -733,6 +755,7 @@ class Boss(QObject):
|
|||||||
editor.data_changed.connect(self.editor_data_changed)
|
editor.data_changed.connect(self.editor_data_changed)
|
||||||
editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed)
|
editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed)
|
||||||
editor.cursor_position_changed.connect(self.sync_preview_to_editor)
|
editor.cursor_position_changed.connect(self.sync_preview_to_editor)
|
||||||
|
editor.cursor_position_changed.connect(self.update_cursor_position)
|
||||||
if data is not None:
|
if data is not None:
|
||||||
if use_template:
|
if use_template:
|
||||||
editor.init_from_template(data)
|
editor.init_from_template(data)
|
||||||
@ -818,6 +841,7 @@ class Boss(QObject):
|
|||||||
|
|
||||||
def apply_current_editor_state(self):
|
def apply_current_editor_state(self):
|
||||||
ed = self.gui.central.current_editor
|
ed = self.gui.central.current_editor
|
||||||
|
self.gui.cursor_position_widget.update_position()
|
||||||
if ed is not None:
|
if ed is not None:
|
||||||
actions['editor-undo'].setEnabled(ed.undo_available)
|
actions['editor-undo'].setEnabled(ed.undo_available)
|
||||||
actions['editor-redo'].setEnabled(ed.redo_available)
|
actions['editor-redo'].setEnabled(ed.redo_available)
|
||||||
@ -832,9 +856,18 @@ class Boss(QObject):
|
|||||||
break
|
break
|
||||||
if name is not None and getattr(ed, 'syntax', None) == 'html':
|
if name is not None and getattr(ed, 'syntax', None) == 'html':
|
||||||
self.gui.preview.show(name)
|
self.gui.preview.show(name)
|
||||||
|
if ed.has_line_numbers:
|
||||||
|
self.gui.cursor_position_widget.update_position(*ed.cursor_position)
|
||||||
else:
|
else:
|
||||||
actions['go-to-line-number'].setEnabled(False)
|
actions['go-to-line-number'].setEnabled(False)
|
||||||
|
|
||||||
|
def update_cursor_position(self):
|
||||||
|
ed = self.gui.central.current_editor
|
||||||
|
if getattr(ed, 'has_line_numbers', False):
|
||||||
|
self.gui.cursor_position_widget.update_position(*ed.cursor_position)
|
||||||
|
else:
|
||||||
|
self.gui.cursor_position_widget.update_position()
|
||||||
|
|
||||||
def editor_close_requested(self, editor):
|
def editor_close_requested(self, editor):
|
||||||
name = None
|
name = None
|
||||||
for n, ed in editors.iteritems():
|
for n, ed in editors.iteritems():
|
||||||
|
126
src/calibre/gui2/tweak_book/check.py
Normal file
126
src/calibre/gui2/tweak_book/check.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt4.Qt import (
|
||||||
|
QIcon, Qt, QHBoxLayout, QListWidget, QTextBrowser, QWidget,
|
||||||
|
QListWidgetItem, pyqtSignal, QApplication)
|
||||||
|
|
||||||
|
from calibre.ebooks.oeb.polish.check.base import WARN, INFO, DEBUG, ERROR, CRITICAL
|
||||||
|
from calibre.ebooks.oeb.polish.check.main import run_checks
|
||||||
|
|
||||||
|
def icon_for_level(level):
|
||||||
|
if level > WARN:
|
||||||
|
icon = 'dialog_error.png'
|
||||||
|
elif level == WARN:
|
||||||
|
icon = 'dialog_warning.png'
|
||||||
|
elif level == INFO:
|
||||||
|
icon = 'dialog_information.png'
|
||||||
|
else:
|
||||||
|
icon = None
|
||||||
|
return QIcon(I(icon)) if icon else QIcon()
|
||||||
|
|
||||||
|
class Check(QWidget):
|
||||||
|
|
||||||
|
item_activated = pyqtSignal(object)
|
||||||
|
check_requested = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
|
||||||
|
self.l = l = QHBoxLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
self.items = i = QListWidget(self)
|
||||||
|
self.items.setSpacing(2)
|
||||||
|
self.items.itemDoubleClicked.connect(self.current_item_activated)
|
||||||
|
self.items.currentItemChanged.connect(self.current_item_changed)
|
||||||
|
l.addWidget(i)
|
||||||
|
self.help = h = QTextBrowser(self)
|
||||||
|
h.anchorClicked.connect(self.link_clicked)
|
||||||
|
h.setOpenLinks(False)
|
||||||
|
l.addWidget(h)
|
||||||
|
h.setMaximumWidth(250)
|
||||||
|
self.clear_help(_('Check has not been run'))
|
||||||
|
|
||||||
|
def clear_help(self, msg):
|
||||||
|
self.help.setText('<h2>%s</h2><p><a href="run:check">%s</a></p>' % (
|
||||||
|
msg, _('Run check')))
|
||||||
|
|
||||||
|
def link_clicked(self, url):
|
||||||
|
url = unicode(url.toString())
|
||||||
|
if url == 'activate:item':
|
||||||
|
self.current_item_activated()
|
||||||
|
elif url == 'run:check':
|
||||||
|
self.check_requested.emit()
|
||||||
|
|
||||||
|
def current_item_activated(self, *args):
|
||||||
|
i = self.items.currentItem()
|
||||||
|
if i is not None:
|
||||||
|
err = i.data(Qt.UserRole).toPyObject()
|
||||||
|
self.item_activated.emit(err)
|
||||||
|
|
||||||
|
def current_item_changed(self, *args):
|
||||||
|
i = self.items.currentItem()
|
||||||
|
self.help.setText('')
|
||||||
|
if i is not None:
|
||||||
|
err = i.data(Qt.UserRole).toPyObject()
|
||||||
|
header = {DEBUG:_('Debug'), INFO:_('Information'), WARN:_('Warning'), ERROR:_('Error'), CRITICAL:_('Error')}[err.level]
|
||||||
|
loc = ''
|
||||||
|
if err.line is not None:
|
||||||
|
loc = _('line: %d') % err.line
|
||||||
|
if err.col is not None:
|
||||||
|
loc += ' column: %d' % err.col
|
||||||
|
if loc:
|
||||||
|
loc = ' (%s)' % loc
|
||||||
|
self.help.setText(
|
||||||
|
'''<h2 style="text-align:center">%s</h2>
|
||||||
|
<p>%s</p>
|
||||||
|
<div><a style="text-decoration:none" href="activate:item">%s %s</a></div>
|
||||||
|
''' % (header, err.msg, err.name, loc))
|
||||||
|
|
||||||
|
def run_checks(self, container):
|
||||||
|
from calibre.gui2.tweak_book.boss import BusyCursor
|
||||||
|
with BusyCursor():
|
||||||
|
self.show_busy()
|
||||||
|
QApplication.processEvents()
|
||||||
|
errors = run_checks(container)
|
||||||
|
self.hide_busy()
|
||||||
|
|
||||||
|
for err in sorted(errors, key=lambda e:(100 - e.level, e.name)):
|
||||||
|
i = QListWidgetItem(err.msg, self.items)
|
||||||
|
i.setData(Qt.UserRole, err)
|
||||||
|
i.setIcon(icon_for_level(err.level))
|
||||||
|
if errors:
|
||||||
|
self.items.item(0).setSelected(True)
|
||||||
|
self.items.setCurrentRow(0)
|
||||||
|
self.current_item_changed()
|
||||||
|
else:
|
||||||
|
self.clear_help(_('No problems found'))
|
||||||
|
|
||||||
|
def show_busy(self, msg=_('Running checks, please wait...')):
|
||||||
|
self.help.setText(msg)
|
||||||
|
self.items.clear()
|
||||||
|
|
||||||
|
def hide_busy(self):
|
||||||
|
self.help.setText('')
|
||||||
|
self.items.clear()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
from calibre.gui2 import Application
|
||||||
|
from calibre.gui2.tweak_book.boss import get_container
|
||||||
|
app = Application([]) # noqa
|
||||||
|
path = sys.argv[-1]
|
||||||
|
container = get_container(path)
|
||||||
|
d = Check()
|
||||||
|
d.run_checks(container)
|
||||||
|
d.show()
|
||||||
|
app.exec_()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -181,7 +181,7 @@ class TextEdit(QPlainTextEdit):
|
|||||||
self.setTextCursor(c)
|
self.setTextCursor(c)
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
def go_to_line(self, lnum):
|
def go_to_line(self, lnum, col=None):
|
||||||
lnum = max(1, min(self.blockCount(), lnum))
|
lnum = max(1, min(self.blockCount(), lnum))
|
||||||
c = self.textCursor()
|
c = self.textCursor()
|
||||||
c.clearSelection()
|
c.clearSelection()
|
||||||
@ -190,10 +190,16 @@ class TextEdit(QPlainTextEdit):
|
|||||||
c.movePosition(c.StartOfLine)
|
c.movePosition(c.StartOfLine)
|
||||||
c.movePosition(c.EndOfLine, c.KeepAnchor)
|
c.movePosition(c.EndOfLine, c.KeepAnchor)
|
||||||
text = unicode(c.selectedText())
|
text = unicode(c.selectedText())
|
||||||
|
if col is None:
|
||||||
c.movePosition(c.StartOfLine)
|
c.movePosition(c.StartOfLine)
|
||||||
lt = text.lstrip()
|
lt = text.lstrip()
|
||||||
if text and lt and lt != text:
|
if text and lt and lt != text:
|
||||||
c.movePosition(c.NextWord)
|
c.movePosition(c.NextWord)
|
||||||
|
else:
|
||||||
|
c.setPosition(c.block().position() + col)
|
||||||
|
if c.blockNumber() + 1 > lnum:
|
||||||
|
c.movePosition(c.PreviousBlock)
|
||||||
|
c.movePosition(c.EndOfBlock)
|
||||||
self.setTextCursor(c)
|
self.setTextCursor(c)
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
|
@ -176,12 +176,20 @@ class Editor(QMainWindow):
|
|||||||
def _cursor_position_changed(self, *args):
|
def _cursor_position_changed(self, *args):
|
||||||
self.cursor_position_changed.emit()
|
self.cursor_position_changed.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cursor_position(self):
|
||||||
|
c = self.editor.textCursor()
|
||||||
|
return (c.blockNumber() + 1, c.positionInBlock())
|
||||||
|
|
||||||
def cut(self):
|
def cut(self):
|
||||||
self.editor.cut()
|
self.editor.cut()
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
self.editor.copy()
|
self.editor.copy()
|
||||||
|
|
||||||
|
def go_to_line(self, line, col=None):
|
||||||
|
self.editor.go_to_line(line, col=col)
|
||||||
|
|
||||||
def paste(self):
|
def paste(self):
|
||||||
if not self.editor.canPaste():
|
if not self.editor.canPaste():
|
||||||
return error_dialog(self, _('No text'), _(
|
return error_dialog(self, _('No text'), _(
|
||||||
|
@ -10,7 +10,8 @@ from functools import partial
|
|||||||
|
|
||||||
from PyQt4.Qt import (
|
from PyQt4.Qt import (
|
||||||
QDockWidget, Qt, QLabel, QIcon, QAction, QApplication, QWidget, QEvent,
|
QDockWidget, Qt, QLabel, QIcon, QAction, QApplication, QWidget, QEvent,
|
||||||
QVBoxLayout, QStackedWidget, QTabWidget, QImage, QPixmap, pyqtSignal, QMenu)
|
QVBoxLayout, QStackedWidget, QTabWidget, QImage, QPixmap, pyqtSignal,
|
||||||
|
QMenu, QHBoxLayout)
|
||||||
|
|
||||||
from calibre.constants import __appname__, get_version
|
from calibre.constants import __appname__, get_version
|
||||||
from calibre.gui2 import elided_text
|
from calibre.gui2 import elided_text
|
||||||
@ -22,6 +23,7 @@ from calibre.gui2.tweak_book.job import BlockingJob
|
|||||||
from calibre.gui2.tweak_book.boss import Boss
|
from calibre.gui2.tweak_book.boss import Boss
|
||||||
from calibre.gui2.tweak_book.preview import Preview
|
from calibre.gui2.tweak_book.preview import Preview
|
||||||
from calibre.gui2.tweak_book.search import SearchPanel
|
from calibre.gui2.tweak_book.search import SearchPanel
|
||||||
|
from calibre.gui2.tweak_book.check import Check
|
||||||
|
|
||||||
class Central(QStackedWidget):
|
class Central(QStackedWidget):
|
||||||
|
|
||||||
@ -155,6 +157,25 @@ class Central(QStackedWidget):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
class CursorPositionWidget(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.l = QHBoxLayout(self)
|
||||||
|
self.setLayout(self.l)
|
||||||
|
self.la = QLabel('')
|
||||||
|
self.l.addWidget(self.la)
|
||||||
|
self.l.setContentsMargins(0, 0, 0, 0)
|
||||||
|
f = self.la.font()
|
||||||
|
f.setBold(False)
|
||||||
|
self.la.setFont(f)
|
||||||
|
|
||||||
|
def update_position(self, line=None, col=None):
|
||||||
|
if line is None:
|
||||||
|
self.la.setText('')
|
||||||
|
else:
|
||||||
|
self.la.setText(_('Line: {0} : {1}').format(line, col))
|
||||||
|
|
||||||
class Main(MainWindow):
|
class Main(MainWindow):
|
||||||
|
|
||||||
APP_NAME = _('Tweak Book')
|
APP_NAME = _('Tweak Book')
|
||||||
@ -182,6 +203,8 @@ class Main(MainWindow):
|
|||||||
|
|
||||||
self.status_bar = self.statusBar()
|
self.status_bar = self.statusBar()
|
||||||
self.status_bar.addPermanentWidget(self.boss.save_manager.status_widget)
|
self.status_bar.addPermanentWidget(self.boss.save_manager.status_widget)
|
||||||
|
self.cursor_position_widget = CursorPositionWidget(self)
|
||||||
|
self.status_bar.addPermanentWidget(self.cursor_position_widget)
|
||||||
self.status_bar.addWidget(QLabel(_('{0} {1} created by {2}').format(__appname__, get_version(), 'Kovid Goyal')))
|
self.status_bar.addWidget(QLabel(_('{0} {1} created by {2}').format(__appname__, get_version(), 'Kovid Goyal')))
|
||||||
f = self.status_bar.font()
|
f = self.status_bar.font()
|
||||||
f.setBold(True)
|
f.setBold(True)
|
||||||
@ -259,6 +282,7 @@ class Main(MainWindow):
|
|||||||
_('Beautify current file'))
|
_('Beautify current file'))
|
||||||
self.action_pretty_all = reg('format-justify-fill.png', _('&Beautify all files'), partial(self.boss.pretty_print, False), 'pretty-all', (),
|
self.action_pretty_all = reg('format-justify-fill.png', _('&Beautify all files'), partial(self.boss.pretty_print, False), 'pretty-all', (),
|
||||||
_('Beautify all files'))
|
_('Beautify all files'))
|
||||||
|
self.action_check_book = reg('debug.png', _('&Check Book'), self.boss.check_requested, 'check-book', ('F7'), _('Check book for errors'))
|
||||||
|
|
||||||
# Polish actions
|
# Polish actions
|
||||||
group = _('Polish Book')
|
group = _('Polish Book')
|
||||||
@ -355,6 +379,7 @@ class Main(MainWindow):
|
|||||||
e.addAction(self.action_smarten_punctuation)
|
e.addAction(self.action_smarten_punctuation)
|
||||||
e.addAction(self.action_fix_html_all)
|
e.addAction(self.action_fix_html_all)
|
||||||
e.addAction(self.action_pretty_all)
|
e.addAction(self.action_pretty_all)
|
||||||
|
e.addAction(self.action_check_book)
|
||||||
|
|
||||||
e = b.addMenu(_('&View'))
|
e = b.addMenu(_('&View'))
|
||||||
t = e.addMenu(_('Tool&bars'))
|
t = e.addMenu(_('Tool&bars'))
|
||||||
@ -403,7 +428,7 @@ class Main(MainWindow):
|
|||||||
return b
|
return b
|
||||||
|
|
||||||
a = create(_('Book tool bar'), 'global').addAction
|
a = create(_('Book tool bar'), 'global').addAction
|
||||||
for x in ('new_file', 'open_book', 'global_undo', 'global_redo', 'save', 'create_checkpoint', 'toc'):
|
for x in ('new_file', 'open_book', 'global_undo', 'global_redo', 'save', 'create_checkpoint', 'toc', 'check_book'):
|
||||||
a(getattr(self, 'action_' + x))
|
a(getattr(self, 'action_' + x))
|
||||||
|
|
||||||
a = create(_('Polish book tool bar'), 'polish').addAction
|
a = create(_('Polish book tool bar'), 'polish').addAction
|
||||||
@ -436,6 +461,13 @@ class Main(MainWindow):
|
|||||||
d.setWidget(self.preview)
|
d.setWidget(self.preview)
|
||||||
self.addDockWidget(Qt.RightDockWidgetArea, d)
|
self.addDockWidget(Qt.RightDockWidgetArea, d)
|
||||||
|
|
||||||
|
d = create(_('Check Book'), 'check-book')
|
||||||
|
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
|
||||||
|
self.check_book = Check(self)
|
||||||
|
d.setWidget(self.check_book)
|
||||||
|
self.addDockWidget(Qt.TopDockWidgetArea, d)
|
||||||
|
d.close() # By default the check window is closed
|
||||||
|
|
||||||
d = create(_('Inspector'), 'inspector')
|
d = create(_('Inspector'), 'inspector')
|
||||||
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
|
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
|
||||||
d.setWidget(self.preview.inspector)
|
d.setWidget(self.preview.inspector)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user