From 76b0ba65f6cc0e89a0be63ff5dd0eb9c2404dacf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 7 Nov 2013 17:45:39 +0530 Subject: [PATCH 01/28] ... --- recipes/wprost_rss.recipe | 1 - 1 file changed, 1 deletion(-) diff --git a/recipes/wprost_rss.recipe b/recipes/wprost_rss.recipe index 7cd9d9ce5c..bc57e288d4 100644 --- a/recipes/wprost_rss.recipe +++ b/recipes/wprost_rss.recipe @@ -6,7 +6,6 @@ __copyright__ = '''2010, matek09, matek09@gmail.com Modified 2012, Artur Stachecki ''' from calibre.web.feeds.news import BasicNewsRecipe -import re class Wprost(BasicNewsRecipe): title = u'Wprost (RSS)' From c0deb561e780a252a3335605880c1675e93ab789 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 7 Nov 2013 17:48:04 +0530 Subject: [PATCH 02/28] Clear parse worker cache when new book is opened --- src/calibre/gui2/tweak_book/boss.py | 1 + src/calibre/gui2/tweak_book/preview.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 7269f3934e..b54a86f8be 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -97,6 +97,7 @@ class Boss(QObject): return error_dialog(self.gui, _('Failed to open book'), _('Failed to open book, click Show details for more information.'), det_msg=job.traceback, show=True) + parse_worker.clear() container = job.result set_current_container(container) self.current_metadata = self.gui.current_metadata = container.mi diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index 59efe2fc70..04c81bc9e8 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -117,6 +117,9 @@ class ParseWorker(Thread): def get_data(self, name): return getattr(self.parse_items.get(name, None), 'parsed_data', None) + def clear(self): + self.parse_items.clear() + parse_worker = ParseWorker() # }}} From 72abdfc3e5103af70383364443be0abf995ea1d1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 7 Nov 2013 17:52:20 +0530 Subject: [PATCH 03/28] Close editors and clear preview when opening a new book --- src/calibre/gui2/tweak_book/boss.py | 3 +++ src/calibre/gui2/tweak_book/preview.py | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index b54a86f8be..9b7c983b9a 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -86,6 +86,9 @@ class Boss(QObject): ' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)), show=True) + for name in editors: + self.close_editor(name) + self.gui.preview.clear() self.container_count = -1 if self.tdir: shutil.rmtree(self.tdir, ignore_errors=True) diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index 04c81bc9e8..af93a8f347 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -235,10 +235,11 @@ class WebView(QWebView): settings.setAttribute(settings.DeveloperExtrasEnabled, True) settings.setDefaultTextEncoding('utf-8') - self.setHtml('

') self.page().setNetworkAccessManager(NetworkAccessManager(self)) self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) + self.clear() + def sizeHint(self): return self._size_hint @@ -256,6 +257,9 @@ class WebView(QWebView): mf.setScrollBarValue(Qt.Vertical, val[1]) return property(fget=fget, fset=fset) + def clear(self): + self.setHtml('

') + class Preview(QWidget): def __init__(self, parent=None): @@ -287,3 +291,6 @@ class Preview(QWidget): # Tell webkit to reload all html and associated resources self.view.refresh() + def clear(self): + self.view.clear() + From d4dd0790bf3eb58f1f40729161c5478189677f4b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 7 Nov 2013 20:10:28 +0530 Subject: [PATCH 04/28] ... --- src/calibre/gui2/tweak_book/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index 46d223e61a..ee9da91970 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -191,8 +191,8 @@ class Main(MainWindow): d.setWidget(self.file_list) self.addDockWidget(Qt.LeftDockWidgetArea, d) - self.preview_dock = d = QDockWidget(_('&Book preview'), self) - d.setObjectName('file_list_dock') # Needed for saveState + self.preview_dock = d = QDockWidget(_('File &Preview'), self) + d.setObjectName('file_preview') # Needed for saveState d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.preview = Preview(d) d.setWidget(self.preview) From f6635f6f1f89cdfd9b586db0410c22e5f8206d37 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 7 Nov 2013 21:08:00 +0530 Subject: [PATCH 05/28] Basic editor actions --- src/calibre/gui2/tweak_book/boss.py | 23 +++++++++ src/calibre/gui2/tweak_book/editor/widget.py | 51 ++++++++++++++------ src/calibre/gui2/tweak_book/ui.py | 7 +++ 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 9b7c983b9a..f7a87ba800 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -238,6 +238,7 @@ class Boss(QObject): editor = editors[name] = editor_from_syntax(syntax, self.gui.editor_tabs) editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed) editor.data_changed.connect(self.editor_data_changed) + editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed) c = current_container() with c.open(name) as f: editor.data = c.decode(f.read()) @@ -256,6 +257,7 @@ class Boss(QObject): _('Editing files of type %s is not supported' % mime), show=True) self.edit_file(name, syntax) + # Editor basic controls {{{ def do_editor_undo(self): ed = self.gui.central.current_editor if ed is not None: @@ -266,16 +268,35 @@ class Boss(QObject): if ed is not None: ed.redo() + def do_editor_copy(self): + ed = self.gui.central.current_editor + if ed is not None: + ed.copy() + + def do_editor_cut(self): + ed = self.gui.central.current_editor + if ed is not None: + ed.cut() + + def do_editor_paste(self): + ed = self.gui.central.current_editor + if ed is not None: + ed.paste() + def editor_data_changed(self, editor): self.gui.preview.refresh_timer.start(tprefs['preview_refresh_time'] * 1000) def editor_undo_redo_state_changed(self, *args): self.apply_current_editor_state(update_keymap=False) + def editor_copy_available_state_changed(self, *args): + self.apply_current_editor_state(update_keymap=False) + def editor_modification_state_changed(self, is_modified): self.apply_current_editor_state(update_keymap=False) if is_modified: actions['save-book'].setEnabled(True) + # }}} def apply_current_editor_state(self, update_keymap=True): ed = self.gui.central.current_editor @@ -283,6 +304,8 @@ class Boss(QObject): actions['editor-undo'].setEnabled(ed.undo_available) actions['editor-redo'].setEnabled(ed.redo_available) actions['editor-save'].setEnabled(ed.is_modified) + actions['editor-cut'].setEnabled(ed.copy_available) + actions['editor-copy'].setEnabled(ed.cut_available) self.gui.keyboard.set_mode(ed.syntax) name = None for n, x in editors.iteritems(): diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 13393d72d8..f560aeb41e 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -16,6 +16,7 @@ class Editor(QMainWindow): modification_state_changed = pyqtSignal(object) undo_redo_state_changed = pyqtSignal(object, object) + copy_available_state_changed = pyqtSignal(object) data_changed = pyqtSignal(object) def __init__(self, syntax, parent=None): @@ -29,20 +30,11 @@ class Editor(QMainWindow): self.create_toolbars() self.undo_available = False self.redo_available = False + self.copy_available = self.cut_available = False self.editor.undoAvailable.connect(self._undo_available) self.editor.redoAvailable.connect(self._redo_available) self.editor.textChanged.connect(self._data_changed) - - def _data_changed(self): - 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) + self.editor.copyAvailable.connect(self._copy_available) @dynamic_property def data(self): @@ -73,22 +65,51 @@ class Editor(QMainWindow): return property(fget=fget, fset=fset) def create_toolbars(self): - self.action_bar = b = self.addToolBar(_('Edit actions tool bar')) + self.action_bar = b = self.addToolBar(_('File actions tool bar')) b.setObjectName('action_bar') # Needed for saveState - b.addAction(actions['editor-save']) - b.addAction(actions['editor-undo']) - b.addAction(actions['editor-redo']) + for x in ('save', 'undo', 'redo'): + b.addAction(actions['editor-%s' % x]) + self.edit_bar = b = self.addToolBar(_('Edit actions tool bar')) + for x in ('cut', 'copy', 'paste'): + b.addAction(actions['editor-%s' % x]) def break_cycles(self): self.modification_state_changed.disconnect() self.undo_redo_state_changed.disconnect() + self.copy_available_state_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.setPlainText('') + def _data_changed(self): + 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 cut(self): + self.editor.cut() + + def copy(self): + self.editor.copy() + + def paste(self): + self.editor.paste() + + def launch_editor(path_to_edit, path_is_raw=False, syntax='html'): if path_is_raw: raw = path_to_edit diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index ee9da91970..9ec9608cdd 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -20,6 +20,7 @@ from calibre.gui2.tweak_book.keyboard import KeyboardManager from calibre.gui2.tweak_book.preview import Preview class Central(QStackedWidget): + ' The central widget, hosts the editors ' current_editor_changed = pyqtSignal() @@ -162,6 +163,12 @@ class Main(MainWindow): _('Redo typing')) self.action_editor_save = reg('save.png', _('&Save'), self.boss.do_editor_save, 'editor-save', 'Ctrl+S', _('Save changes to the current file')) + self.action_editor_cut = reg('edit-cut.png', _('C&ut text'), self.boss.do_editor_cut, 'editor-cut', 'Ctrl+X', + _('Cut text')) + self.action_editor_copy = reg('edit-copy.png', _('&Copy text'), self.boss.do_editor_copy, 'editor-copy', 'Ctrl+C', + _('Copy text')) + self.action_editor_paste = reg('edit-paste.png', _('&Paste text'), self.boss.do_editor_paste, 'editor-paste', 'Ctrl+V', + _('Paste text')) def create_menubar(self): b = self.menuBar() From cdfb03a669175385e13962ece3da6763bd88d738 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 09:14:31 +0530 Subject: [PATCH 06/28] version 1.10 --- Changelog.yaml | 38 ++++++++++++++++++++++++++++++++++++++ src/calibre/constants.py | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Changelog.yaml b/Changelog.yaml index 0e250ae534..310967cbbc 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -20,6 +20,44 @@ # new recipes: # - title: +- version: 1.10.0 + date: 2013-11-08 + + new features: + - title: "Conversion: Treat .docm the same as .docx files, ignoring any macros in the file." + tickets: [1247565] + + - title: "EPUB Output: Auto convert CMYK images to RGB. Works around the inability of Adobe Digital Editions to display CMYK images." + tickets: [1246710] + + - title: "Quickview: Add a checkbox to lock the quickview window so that it does not change while moving around in the main book list" + + bug fixes: + - title: "Fix number of marked books shown in the drop down menu of the Mark Books action not being updated when marked books are deleted (as opposed to being unmarked)." + tickets: [1248506] + + - title: "Book list: Preserve the current column when using Ctrl+Home or Ctrl+End shortcuts" + + - title: "Booklist: Fix using Page Up/Down keys moving book list by one row too many." + tickets: [1248109] + + - title: "Metadata download: Do not auto trim downloaded covers as trimming can sometimes have undesirable effects." + + - title: "Template language: Fix zero valued series indices not formatting correctly" + tickets: [1247348] + + - title: "Fix a regression in 1.9 that broke bibtex catalog creation" + + - title: "Quickview: Auto-close the quickview window when changing libraries. You have to open it again in the new library." + + - title: "Linux binary build: Update the bundled version of zlib to fix library compatibility on some newer linux distros that have libpng16 compiled to require newer zlib versions" + tickets: [1247315] + + improved recipes: + - New York Review of Books + - Various Polish news sources + + - version: 1.9.0 date: 2013-11-01 diff --git a/src/calibre/constants.py b/src/calibre/constants.py index a113f4b48a..fb7da67e9f 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -4,7 +4,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = u'calibre' -numeric_version = (1, 9, 0) +numeric_version = (1, 10, 0) __version__ = u'.'.join(map(unicode, numeric_version)) __author__ = u"Kovid Goyal " From 66fac7483416f02b2e43c058e211525f546c733e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 10:00:31 +0530 Subject: [PATCH 07/28] Remove incorrect sRGB profiles --- .../interface_demo/images/icon.png | Bin 5785 -> 4248 bytes manual/resources/logo.png | Bin 10566 -> 7874 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/manual/plugin_examples/interface_demo/images/icon.png b/manual/plugin_examples/interface_demo/images/icon.png index ad823e2ff4d53c376632ccb0e2fe60d96cc063ea..7512b6ef07f7b58594deec4906ac81d63ef742c3 100644 GIT binary patch delta 4224 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 W+%Z}Pz>y&*3IG5}MNUMnLSTZJ;}$Fc delta 5774 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 M07*qoM6N<$f)~IP?*IS* diff --git a/manual/resources/logo.png b/manual/resources/logo.png index 158bc9d1b5113db517136636ec2d58dcb29e446a..c952d762bf3d159c8a2ddb76f9d4beb44876776a 100644 GIT binary patch delta 84 zcmX>WbjWssvK0eMx}&cn1H;CC?mvmF3=9kk$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo n7+xhXFj&oCU=S~uvn$YMau%27<`rDVa-0mFu6{1-oD!M<)@c`L delta 2793 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 From 17f6f40fde3ec4cc98a923af52a7a3b81d4af890 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 10:36:09 +0530 Subject: [PATCH 08/28] Add editor actions to menubar --- src/calibre/gui2/tweak_book/ui.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index 9ec9608cdd..b2ad06d9e3 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -157,6 +157,7 @@ class Main(MainWindow): self.action_quit = reg('quit.png', _('&Quit'), self.boss.quit, 'quit', 'Ctrl+Q', _('Quit')) # Editor actions + group = _('Editor actions') self.action_editor_undo = reg('edit-undo.png', _('&Undo'), self.boss.do_editor_undo, 'editor-undo', 'Ctrl+Z', _('Undo typing')) self.action_editor_redo = reg('edit-redo.png', _('&Redo'), self.boss.do_editor_redo, 'editor-redo', 'Ctrl+Y', @@ -169,6 +170,10 @@ class Main(MainWindow): _('Copy text')) self.action_editor_paste = reg('edit-paste.png', _('&Paste text'), self.boss.do_editor_paste, 'editor-paste', 'Ctrl+V', _('Paste text')) + self.action_editor_cut.setEnabled(False) + self.action_editor_copy.setEnabled(False) + self.action_editor_undo.setEnabled(False) + self.action_editor_redo.setEnabled(False) def create_menubar(self): b = self.menuBar() @@ -181,6 +186,13 @@ class Main(MainWindow): e = b.addMenu(_('&Edit')) e.addAction(self.action_global_undo) e.addAction(self.action_global_redo) + e.addSeparator() + e.addAction(self.action_editor_undo) + e.addAction(self.action_editor_redo) + e.addSeparator() + e.addAction(self.action_editor_cut) + e.addAction(self.action_editor_copy) + e.addAction(self.action_editor_paste) def create_toolbar(self): self.global_bar = b = self.addToolBar(_('Global tool bar')) From 89c33c3713eaa518cab5c6cfd8210463d14ac8f4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 12:39:29 +0530 Subject: [PATCH 09/28] Fix rewind_savepoint() not working --- src/calibre/gui2/tweak_book/undo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/undo.py b/src/calibre/gui2/tweak_book/undo.py index f9bd9ba810..ae388c56ce 100644 --- a/src/calibre/gui2/tweak_book/undo.py +++ b/src/calibre/gui2/tweak_book/undo.py @@ -57,7 +57,7 @@ class GlobalUndoHistory(object): revert to state before creating savepoint. ''' if self.pos > 0 and self.pos == len(self.states) - 1: self.pos -= 1 - cleanup(self.states.pop()) + cleanup([self.states.pop().container]) ans = self.current_container ans.message = None return ans From 5231d7958c182fbc9e930198e69054c5fd4a49a9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 12:39:51 +0530 Subject: [PATCH 10/28] Add ToC Editor to Tweak Book --- src/calibre/gui2/tweak_book/boss.py | 31 ++++++- src/calibre/gui2/tweak_book/editor/text.py | 12 +++ src/calibre/gui2/tweak_book/editor/widget.py | 11 +++ src/calibre/gui2/tweak_book/toc.py | 96 ++++++++++++++++++++ src/calibre/gui2/tweak_book/ui.py | 16 +++- 5 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 src/calibre/gui2/tweak_book/toc.py diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index f7a87ba800..e92e596801 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -25,6 +25,7 @@ from calibre.gui2.tweak_book import set_current_container, current_container, tp from calibre.gui2.tweak_book.undo import GlobalUndoHistory from calibre.gui2.tweak_book.save import SaveManager from calibre.gui2.tweak_book.preview import parse_worker +from calibre.gui2.tweak_book.toc import TOCEditor from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime def get_container(*args, **kwargs): @@ -86,7 +87,7 @@ class Boss(QObject): ' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)), show=True) - for name in editors: + for name in tuple(editors): self.close_editor(name) self.gui.preview.clear() self.container_count = -1 @@ -110,12 +111,20 @@ class Boss(QObject): self.gui.action_save.setEnabled(False) self.update_global_history_actions() + def update_editors_from_container(self, container=None): + c = container or current_container() + for name, ed in tuple(editors.iteritems()): + if c.has_name(name): + ed.replace_data(c.raw_data(name)) + else: + self.close_editor(name) + def apply_container_update_to_gui(self): container = current_container() self.gui.file_list.build(container) self.update_global_history_actions() self.gui.action_save.setEnabled(True) - # TODO: Apply to other GUI elements + self.update_editors_from_container() def delete_requested(self, spine_items, other_items): if not self.check_dirtied(): @@ -130,7 +139,8 @@ class Boss(QObject): for name in list(spine_items) + list(other_items): if name in editors: self.close_editor(name) - # TODO: Update other GUI elements + if not editors: + self.gui.preview.clear() def reorder_spine(self, items): # TODO: If content.opf is dirty in an editor, abort, calling @@ -142,6 +152,16 @@ class Boss(QObject): self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items # TODO: If content.opf is open in an editor, reload it + def edit_toc(self): + if not self.check_dirtied(): + return + self.add_savepoint(_('Edit Table of Contents')) + d = TOCEditor(parent=self.gui) + if d.exec_() != d.Accepted: + self.rewind_savepoint() + return + self.update_editors_from_container() + # Renaming {{{ def rename_requested(self, oldname, newname): if not self.check_dirtied(): @@ -176,7 +196,8 @@ class Boss(QObject): det_msg=job.traceback, show=True) self.gui.file_list.build(current_container()) self.gui.action_save.setEnabled(True) - # TODO: Update the rest of the GUI + # TODO: Update the rest of the GUI. This means renaming open editors and + # then calling update_editors_from_container() # }}} # Global history {{{ @@ -335,6 +356,8 @@ class Boss(QObject): editor = editors.pop(name) self.gui.central.close_editor(editor) editor.break_cycles() + if not editors: + self.gui.preview.clear() def do_editor_save(self): ed = self.gui.central.current_editor diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 91efa90f8e..39845d15d1 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -114,6 +114,18 @@ class TextEdit(QPlainTextEdit): self.highlighter.setDocument(self.document()) self.setPlainText(text) + def replace_text(self, text): + c = self.textCursor() + pos = c.position() + c.beginEditBlock() + c.clearSelection() + c.select(c.Document) + c.insertText(text) + c.endEditBlock() + c.setPosition(pos) + self.setTextCursor(c) + self.ensureCursorVisible() + # Line numbers and cursor line {{{ def highlight_cursor_line(self): sel = QTextEdit.ExtraSelection() diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index f560aeb41e..b959a2d47c 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -9,6 +9,7 @@ __copyright__ = '2013, Kovid Goyal ' from PyQt4.Qt import QMainWindow, Qt, QApplication, pyqtSignal from calibre import xml_replace_entities +from calibre.gui2 import error_dialog from calibre.gui2.tweak_book import actions from calibre.gui2.tweak_book.editor.text import TextEdit @@ -50,6 +51,13 @@ class Editor(QMainWindow): def get_raw_data(self): return unicode(self.editor.toPlainText()) + 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 undo(self): self.editor.undo() @@ -107,6 +115,9 @@ class Editor(QMainWindow): self.editor.copy() 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() diff --git a/src/calibre/gui2/tweak_book/toc.py b/src/calibre/gui2/tweak_book/toc.py new file mode 100644 index 0000000000..fa828a4f0f --- /dev/null +++ b/src/calibre/gui2/tweak_book/toc.py @@ -0,0 +1,96 @@ +#!/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 ' + +from PyQt4.Qt import (QDialog, pyqtSignal, QIcon, QVBoxLayout, QDialogButtonBox, QStackedWidget) + +from calibre.ebooks.oeb.polish.toc import commit_toc +from calibre.gui2 import gprefs, error_dialog +from calibre.gui2.toc.main import TOCView, ItemEdit +from calibre.gui2.tweak_book import current_container + +class TOCEditor(QDialog): + + explode_done = pyqtSignal(object) + writing_done = pyqtSignal(object) + + def __init__(self, title=None, parent=None): + QDialog.__init__(self, parent) + + t = title or current_container().mi.title + self.book_title = t + self.setWindowTitle(_('Edit the ToC in %s')%t) + self.setWindowIcon(QIcon(I('toc.png'))) + + l = self.l = QVBoxLayout() + self.setLayout(l) + + self.stacks = s = QStackedWidget(self) + l.addWidget(s) + self.toc_view = TOCView(self) + self.toc_view.add_new_item.connect(self.add_new_item) + s.addWidget(self.toc_view) + self.item_edit = ItemEdit(self) + s.addWidget(self.item_edit) + + bb = self.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + l.addWidget(bb) + bb.accepted.connect(self.accept) + bb.rejected.connect(self.reject) + + self.read_toc() + + self.resize(950, 630) + geom = gprefs.get('toc_editor_window_geom', None) + if geom is not None: + self.restoreGeometry(bytes(geom)) + + def add_new_item(self, item, where): + self.item_edit(item, where) + self.stacks.setCurrentIndex(1) + + def accept(self): + if self.stacks.currentIndex() == 1: + self.toc_view.update_item(*self.item_edit.result) + gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState()) + self.stacks.setCurrentIndex(0) + elif self.stacks.currentIndex() == 0: + self.write_toc() + super(TOCEditor, self).accept() + + def really_accept(self, tb): + gprefs['toc_editor_window_geom'] = bytearray(self.saveGeometry()) + if tb: + error_dialog(self, _('Failed to write book'), + _('Could not write %s. Click "Show details" for' + ' more information.')%self.book_title, det_msg=tb, show=True) + gprefs['toc_editor_window_geom'] = bytearray(self.saveGeometry()) + super(TOCEditor, self).reject() + return + + super(TOCEditor, self).accept() + + def reject(self): + if not self.bb.isEnabled(): + return + if self.stacks.currentIndex() == 1: + gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState()) + self.stacks.setCurrentIndex(0) + else: + gprefs['toc_editor_window_geom'] = bytearray(self.saveGeometry()) + super(TOCEditor, self).reject() + + def read_toc(self): + self.toc_view(current_container()) + self.item_edit.load(current_container()) + self.stacks.setCurrentIndex(0) + + def write_toc(self): + toc = self.toc_view.create_toc() + commit_toc(current_container(), toc, lang=self.toc_view.toc_lang, + uid=self.toc_view.toc_uid) + diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index b2ad06d9e3..cfaa8391a8 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -164,17 +164,21 @@ class Main(MainWindow): _('Redo typing')) self.action_editor_save = reg('save.png', _('&Save'), self.boss.do_editor_save, 'editor-save', 'Ctrl+S', _('Save changes to the current file')) - self.action_editor_cut = reg('edit-cut.png', _('C&ut text'), self.boss.do_editor_cut, 'editor-cut', 'Ctrl+X', + self.action_editor_cut = reg('edit-cut.png', _('C&ut text'), self.boss.do_editor_cut, 'editor-cut', ('Ctrl+X', 'Shift+Delete', ), _('Cut text')) - self.action_editor_copy = reg('edit-copy.png', _('&Copy text'), self.boss.do_editor_copy, 'editor-copy', 'Ctrl+C', + self.action_editor_copy = reg('edit-copy.png', _('&Copy text'), self.boss.do_editor_copy, 'editor-copy', ('Ctrl+C', 'Ctrl+Insert'), _('Copy text')) - self.action_editor_paste = reg('edit-paste.png', _('&Paste text'), self.boss.do_editor_paste, 'editor-paste', 'Ctrl+V', + self.action_editor_paste = reg('edit-paste.png', _('&Paste text'), self.boss.do_editor_paste, 'editor-paste', ('Ctrl+V', 'Shift+Insert', ), _('Paste text')) self.action_editor_cut.setEnabled(False) self.action_editor_copy.setEnabled(False) self.action_editor_undo.setEnabled(False) self.action_editor_redo.setEnabled(False) + # Tool actions + group = _('Tools') + self.action_toc = reg('toc.png', _('&Edit ToC'), self.boss.edit_toc, 'edit-toc', (), _('Edit Table of Contents')) + def create_menubar(self): b = self.menuBar() @@ -194,13 +198,17 @@ class Main(MainWindow): e.addAction(self.action_editor_copy) e.addAction(self.action_editor_paste) + e = b.addMenu(_('&Tools')) + e.addAction(self.action_toc) + def create_toolbar(self): - self.global_bar = b = self.addToolBar(_('Global tool bar')) + self.global_bar = b = self.addToolBar(_('Book tool bar')) b.setObjectName('global_bar') # Needed for saveState b.addAction(self.action_open_book) b.addAction(self.action_global_undo) b.addAction(self.action_global_redo) b.addAction(self.action_save) + b.addAction(self.action_toc) def create_docks(self): self.file_list_dock = d = QDockWidget(_('&Files Browser'), self) From e00615d89d39075d4d35530afde4bd6fe32f2a72 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 12:46:40 +0530 Subject: [PATCH 11/28] ... --- src/calibre/gui2/tweak_book/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index cfaa8391a8..b808c9b9d3 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -177,7 +177,7 @@ class Main(MainWindow): # Tool actions group = _('Tools') - self.action_toc = reg('toc.png', _('&Edit ToC'), self.boss.edit_toc, 'edit-toc', (), _('Edit Table of Contents')) + self.action_toc = reg('toc.png', _('&Edit Table of Contents'), self.boss.edit_toc, 'edit-toc', (), _('Edit Table of Contents')) def create_menubar(self): b = self.menuBar() From 45edfa442c4ebbd4f30f7ec46557ebef1f9b5417 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 13:52:32 +0530 Subject: [PATCH 12/28] Refactor polishing code so it can be used from within Tweak Book --- src/calibre/ebooks/oeb/polish/main.py | 120 ++++++++++++++------------ 1 file changed, 66 insertions(+), 54 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/main.py b/src/calibre/ebooks/oeb/polish/main.py index c5a7d4db6d..19d3386c84 100644 --- a/src/calibre/ebooks/oeb/polish/main.py +++ b/src/calibre/ebooks/oeb/polish/main.py @@ -119,65 +119,68 @@ def update_metadata(ebook, new_opf): stream.truncate() stream.write(opf.render()) -def polish(file_map, opts, log, report): +def polish_one(ebook, opts, report): rt = lambda x: report('\n### ' + x) + jacket = None + + if opts.subset or opts.embed: + stats = StatsCollector(ebook, do_embed=opts.embed) + + if opts.opf: + rt(_('Updating metadata')) + update_metadata(ebook, opts.opf) + jacket = find_existing_jacket(ebook) + if jacket is not None: + replace_jacket(ebook, jacket) + report(_('Updated metadata jacket')) + report(_('Metadata updated\n')) + + if opts.cover: + rt(_('Setting cover')) + set_cover(ebook, opts.cover, report) + report('') + + if opts.jacket: + rt(_('Inserting metadata jacket')) + if jacket is None: + if add_or_replace_jacket(ebook): + report(_('Existing metadata jacket replaced')) + else: + report(_('Metadata jacket inserted')) + else: + report(_('Existing metadata jacket replaced')) + report('') + + if opts.remove_jacket: + rt(_('Removing metadata jacket')) + if remove_jacket(ebook): + report(_('Metadata jacket removed')) + else: + report(_('No metadata jacket found')) + report('') + + if opts.smarten_punctuation: + rt(_('Smartening punctuation')) + smarten_punctuation(ebook, report) + report('') + + if opts.embed: + rt(_('Embedding referenced fonts')) + embed_all_fonts(ebook, stats, report) + report('') + + if opts.subset: + rt(_('Subsetting embedded fonts')) + subset_all_fonts(ebook, stats.font_stats, report) + report('') + + +def polish(file_map, opts, log, report): st = time.time() for inbook, outbook in file_map.iteritems(): report(_('## Polishing: %s')%(inbook.rpartition('.')[-1].upper())) ebook = get_container(inbook, log) - jacket = None - - if opts.subset or opts.embed: - stats = StatsCollector(ebook, do_embed=opts.embed) - - if opts.opf: - rt(_('Updating metadata')) - update_metadata(ebook, opts.opf) - jacket = find_existing_jacket(ebook) - if jacket is not None: - replace_jacket(ebook, jacket) - report(_('Updated metadata jacket')) - report(_('Metadata updated\n')) - - if opts.cover: - rt(_('Setting cover')) - set_cover(ebook, opts.cover, report) - report('') - - if opts.jacket: - rt(_('Inserting metadata jacket')) - if jacket is None: - if add_or_replace_jacket(ebook): - report(_('Existing metadata jacket replaced')) - else: - report(_('Metadata jacket inserted')) - else: - report(_('Existing metadata jacket replaced')) - report('') - - if opts.remove_jacket: - rt(_('Removing metadata jacket')) - if remove_jacket(ebook): - report(_('Metadata jacket removed')) - else: - report(_('No metadata jacket found')) - report('') - - if opts.smarten_punctuation: - rt(_('Smartening punctuation')) - smarten_punctuation(ebook, report) - report('') - - if opts.embed: - rt(_('Embedding referenced fonts')) - embed_all_fonts(ebook, stats, report) - report('') - - if opts.subset: - rt(_('Subsetting embedded fonts')) - subset_all_fonts(ebook, stats.font_stats, report) - report('') - + polish_one(ebook, opts, report) ebook.commit(outbook) report('-'*70) report(_('Polishing took: %.1f seconds')%(time.time()-st)) @@ -204,6 +207,15 @@ def gui_polish(data): log(msg) return '\n\n'.join(report) +def tweak_polish(container, actions): + opts = ALL_OPTS.copy() + opts.update(actions) + O = namedtuple('Options', ' '.join(ALL_OPTS.iterkeys())) + opts = O(**opts) + report = [] + polish_one(container, opts, report.append) + return report + def option_parser(): from calibre.utils.config import OptionParser USAGE = '%prog [options] input_file [output_file]\n\n' + re.sub( From 862bc734f288e187292d161e8955b6b6f8c50b8c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 15:35:38 +0530 Subject: [PATCH 13/28] Add various polish actions to Tweak Book --- resources/images/embed-fonts.png | Bin 0 -> 13520 bytes resources/images/smarten-punctuation.png | Bin 0 -> 776 bytes resources/images/subset-fonts.png | Bin 0 -> 15321 bytes src/calibre/gui2/tweak_book/boss.py | 31 ++++++++++++++++++++--- src/calibre/gui2/tweak_book/ui.py | 23 +++++++++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 resources/images/embed-fonts.png create mode 100644 resources/images/smarten-punctuation.png create mode 100644 resources/images/subset-fonts.png diff --git a/resources/images/embed-fonts.png b/resources/images/embed-fonts.png new file mode 100644 index 0000000000000000000000000000000000000000..fac5b5022de3f2f499f1efa71aa4f7f266c53532 GIT binary patch literal 13520 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 Q^`|00SzcYPT-GA!f0R^goB#j- literal 0 HcmV?d00001 diff --git a/resources/images/smarten-punctuation.png b/resources/images/smarten-punctuation.png new file mode 100644 index 0000000000000000000000000000000000000000..6f3cb92ed76e3bb37cc9e0726f693f5be064bc16 GIT binary patch literal 776 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 literal 0 HcmV?d00001 diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index e92e596801..a749e65f54 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -11,12 +11,12 @@ from functools import partial from PyQt4.Qt import ( QObject, QApplication, QDialog, QGridLayout, QLabel, QSize, Qt, - QDialogButtonBox, QIcon, QTimer, QPixmap) + QDialogButtonBox, QIcon, QTimer, QPixmap, QTextBrowser, QVBoxLayout) from calibre import prints from calibre.ptempfile import PersistentTemporaryDirectory from calibre.ebooks.oeb.base import urlnormalize -from calibre.ebooks.oeb.polish.main import SUPPORTED +from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type from calibre.ebooks.oeb.polish.replace import rename_files from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog @@ -156,12 +156,37 @@ class Boss(QObject): if not self.check_dirtied(): return self.add_savepoint(_('Edit Table of Contents')) - d = TOCEditor(parent=self.gui) + d = TOCEditor(title=self.current_metadata.title, parent=self.gui) if d.exec_() != d.Accepted: self.rewind_savepoint() return self.update_editors_from_container() + def polish(self, action, name): + if not self.check_dirtied(): + return + self.add_savepoint(name) + try: + report = tweak_polish(current_container(), {action:True}) + except: + self.rewind_savepoint() + raise + self.apply_container_update_to_gui() + from calibre.ebooks.markdown import markdown + report = markdown('# %s\n\n'%self.current_metadata.title + '\n\n'.join(report), output_format='html4') + d = QDialog(self.gui) + d.l = QVBoxLayout() + d.setLayout(d.l) + d.e = QTextBrowser(d) + d.l.addWidget(d.e) + d.e.setHtml(report) + d.bb = QDialogButtonBox(QDialogButtonBox.Close) + d.l.addWidget(d.bb) + d.bb.rejected.connect(d.reject) + d.bb.accepted.connect(d.accept) + d.resize(600, 400) + d.exec_() + # Renaming {{{ def rename_requested(self, oldname, newname): if not self.check_dirtied(): diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index b808c9b9d3..6c80c1d434 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -6,6 +6,8 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' +from functools import partial + from PyQt4.Qt import ( QDockWidget, Qt, QLabel, QIcon, QAction, QApplication, QWidget, QVBoxLayout, QStackedWidget, QTabWidget, QImage, QPixmap, pyqtSignal) @@ -179,6 +181,18 @@ class Main(MainWindow): group = _('Tools') self.action_toc = reg('toc.png', _('&Edit Table of Contents'), self.boss.edit_toc, 'edit-toc', (), _('Edit Table of Contents')) + # Polish actions + group = _('Polish') + self.action_subset_fonts = reg( + 'subset-fonts.png', _('&Subset embedded fonts'), partial( + self.boss.polish, 'subset', _('Subset fonts')), 'subset-fonts', (), _('Subset embedded fonts')) + self.action_embed_fonts = reg( + 'embed-fonts.png', _('&Embed referenced fonts'), partial( + self.boss.polish, 'embed', _('Embed fonts')), 'embed-fonts', (), _('Embed referenced fonts')) + self.action_smarten_punctuation = reg( + 'smarten-punctuation.png', _('&Smarten punctuation'), partial( + self.boss.polish, 'smarten_punctuation', _('Smarten punstuation')), 'smarten-punctuation', (), _('Smarten punctuation')) + def create_menubar(self): b = self.menuBar() @@ -200,6 +214,9 @@ class Main(MainWindow): e = b.addMenu(_('&Tools')) e.addAction(self.action_toc) + e.addAction(self.action_embed_fonts) + e.addAction(self.action_subset_fonts) + e.addAction(self.action_smarten_punctuation) def create_toolbar(self): self.global_bar = b = self.addToolBar(_('Book tool bar')) @@ -210,6 +227,12 @@ class Main(MainWindow): b.addAction(self.action_save) b.addAction(self.action_toc) + self.polish_bar = b = self.addToolBar(_('Polish book tool bar')) + b.setObjectName('polish_bar') # Needed for saveState + b.addAction(self.action_embed_fonts) + b.addAction(self.action_subset_fonts) + b.addAction(self.action_smarten_punctuation) + def create_docks(self): self.file_list_dock = d = QDockWidget(_('&Files Browser'), self) d.setObjectName('file_list_dock') # Needed for saveState From 09312b65b27122f6c087fdabfa597a4db6038751 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 15:38:10 +0530 Subject: [PATCH 14/28] Dont set cursor pos beyond end of text when replacing text --- src/calibre/gui2/tweak_book/editor/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 39845d15d1..2c1980be34 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -122,7 +122,7 @@ class TextEdit(QPlainTextEdit): c.select(c.Document) c.insertText(text) c.endEditBlock() - c.setPosition(pos) + c.setPosition(min(pos, len(text))) self.setTextCursor(c) self.ensureCursorVisible() From d99ff84f9c49fcc9348ca0606a9d879f315faeac Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 15:41:21 +0530 Subject: [PATCH 15/28] Report when no smartenable punctuation is found --- src/calibre/ebooks/oeb/polish/replace.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/calibre/ebooks/oeb/polish/replace.py b/src/calibre/ebooks/oeb/polish/replace.py index a78f7b5ef7..7945787dd3 100644 --- a/src/calibre/ebooks/oeb/polish/replace.py +++ b/src/calibre/ebooks/oeb/polish/replace.py @@ -78,6 +78,7 @@ def replace_links(container, link_map, frag_map=lambda name, frag:frag, replace_ def smarten_punctuation(container, report): from calibre.ebooks.conversion.preprocess import smarten_punctuation + smartened = False for path in container.spine_items: name = container.abspath_to_name(path) changed = False @@ -98,6 +99,9 @@ def smarten_punctuation(container, report): for m in root.xpath('descendant::*[local-name()="meta" and @http-equiv]'): m.getparent().remove(m) container.dirty(name) + smartened = True + if not smartened: + report(_('No punctuation that could be smartened found')) def rename_files(container, file_map): overlap = set(file_map).intersection(set(file_map.itervalues())) From 175d9132998807997c1d3b0a0db91958cbcfbca2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 15:50:29 +0530 Subject: [PATCH 16/28] Fix refresh timer not being stopped on refresh --- src/calibre/gui2/tweak_book/preview.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index af93a8f347..90906c9d45 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -285,6 +285,7 @@ class Preview(QWidget): def refresh(self): if self.current_name: + self.refresh_timer.stop() # This will check if the current html has changed in its editor, # and re-parse it if so parse_worker.add_request(self.current_name) From 07e0ec1d1667eaea5a67cf2b09bcd6d01ff4bf27 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 16:03:32 +0530 Subject: [PATCH 17/28] Fix preview panel not refreshing if container is changed --- src/calibre/gui2/tweak_book/preview.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index 90906c9d45..883fa2ba7f 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -290,7 +290,12 @@ class Preview(QWidget): # and re-parse it if so parse_worker.add_request(self.current_name) # Tell webkit to reload all html and associated resources - self.view.refresh() + current_url = QUrl.fromLocalFile(current_container().name_to_abspath(self.current_name)) + if current_url != self.view.url(): + # The container was changed + self.view.setUrl(current_url) + else: + self.view.refresh() def clear(self): self.view.clear() From d9fa433025138f0b58297952e739d915d18f2ecb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Nov 2013 16:05:59 +0530 Subject: [PATCH 18/28] ... --- src/calibre/gui2/tweak_book/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index 6c80c1d434..34491d1b68 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -191,7 +191,7 @@ class Main(MainWindow): self.boss.polish, 'embed', _('Embed fonts')), 'embed-fonts', (), _('Embed referenced fonts')) self.action_smarten_punctuation = reg( 'smarten-punctuation.png', _('&Smarten punctuation'), partial( - self.boss.polish, 'smarten_punctuation', _('Smarten punstuation')), 'smarten-punctuation', (), _('Smarten punctuation')) + self.boss.polish, 'smarten_punctuation', _('Smarten punctuation')), 'smarten-punctuation', (), _('Smarten punctuation')) def create_menubar(self): b = self.menuBar() From f77fc9493eefe0092e27f0202d8d0f7926bc7313 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 08:34:15 +0530 Subject: [PATCH 19/28] Move refresh timer control into Preview class --- src/calibre/gui2/tweak_book/boss.py | 4 ++-- src/calibre/gui2/tweak_book/preview.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index a749e65f54..556dd90c7f 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -330,7 +330,7 @@ class Boss(QObject): ed.paste() def editor_data_changed(self, editor): - self.gui.preview.refresh_timer.start(tprefs['preview_refresh_time'] * 1000) + self.gui.preview.start_refresh_timer() def editor_undo_redo_state_changed(self, *args): self.apply_current_editor_state(update_keymap=False) @@ -472,7 +472,7 @@ class Boss(QObject): QApplication.instance().quit() def shutdown(self): - self.gui.preview.refresh_timer.stop() + self.gui.preview.stop_refresh_timer() self.save_state() self.save_manager.shutdown() parse_worker.shutdown() diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index 883fa2ba7f..f4a68b287b 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -20,7 +20,7 @@ from calibre.constants import iswindows from calibre.ebooks.oeb.polish.parsing import parse from calibre.ebooks.oeb.base import serialize, OEB_DOCS from calibre.ptempfile import PersistentTemporaryDirectory -from calibre.gui2.tweak_book import current_container, editors +from calibre.gui2.tweak_book import current_container, editors, tprefs from calibre.gui2.viewer.documentview import apply_settings from calibre.gui2.viewer.config import config from calibre.utils.ipc.simple_worker import offload_worker @@ -300,3 +300,8 @@ class Preview(QWidget): def clear(self): self.view.clear() + def start_refresh_timer(self): + self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000) + + def stop_refresh_timer(self): + self.refresh_timer.stop() From 6df507a9627b7b5e35a1fd999cec7cc5d556dac4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 09:20:34 +0530 Subject: [PATCH 20/28] Add a toolbar with buttons to control the preview panel --- imgsrc/view-refresh.svg | 1566 ++++++++++++++++++++++++ resources/images/auto-reload.png | Bin 0 -> 46804 bytes resources/images/view-refresh.png | Bin 0 -> 3743 bytes src/calibre/gui2/__init__.py | 1 + src/calibre/gui2/tweak_book/preview.py | 25 +- src/calibre/gui2/tweak_book/ui.py | 7 +- 6 files changed, 1595 insertions(+), 4 deletions(-) create mode 100644 imgsrc/view-refresh.svg create mode 100644 resources/images/auto-reload.png create mode 100644 resources/images/view-refresh.png diff --git a/imgsrc/view-refresh.svg b/imgsrc/view-refresh.svg new file mode 100644 index 0000000000..4ab18c929b --- /dev/null +++ b/imgsrc/view-refresh.svg @@ -0,0 +1,1566 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/auto-reload.png b/resources/images/auto-reload.png new file mode 100644 index 0000000000000000000000000000000000000000..5590b33953178deb716c1f8500756320e1da41d9 GIT binary patch literal 46804 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

oCx!vm<_xaq6LzzGopZfsVU!KzkZX-J~ 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 JPDHLkV1mXr+o%8l literal 0 HcmV?d00001 diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 67d4b80750..01a4310c82 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -876,6 +876,7 @@ class Application(QApplication): 'MessageBoxWarning': u'dialog_warning.png', 'MessageBoxCritical': u'dialog_error.png', 'MessageBoxQuestion': u'dialog_question.png', + 'BrowserReload': u'view-refresh.png', # These two are used to calculate the sizes for the doc widget # title bar buttons, therefore, they have to exist. The actual # icon is not used. diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index f4a68b287b..bbc6e9b01e 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -12,7 +12,7 @@ from Queue import Queue, Empty from PyQt4.Qt import ( QWidget, QVBoxLayout, QApplication, QSize, QNetworkAccessManager, - QNetworkReply, QTimer, QNetworkRequest, QUrl, Qt, QNetworkDiskCache) + QNetworkReply, QTimer, QNetworkRequest, QUrl, Qt, QNetworkDiskCache, QToolBar) from PyQt4.QtWebKit import QWebView from calibre import prints @@ -20,7 +20,7 @@ from calibre.constants import iswindows from calibre.ebooks.oeb.polish.parsing import parse from calibre.ebooks.oeb.base import serialize, OEB_DOCS from calibre.ptempfile import PersistentTemporaryDirectory -from calibre.gui2.tweak_book import current_container, editors, tprefs +from calibre.gui2.tweak_book import current_container, editors, tprefs, actions from calibre.gui2.viewer.documentview import apply_settings from calibre.gui2.viewer.config import config from calibre.utils.ipc.simple_worker import offload_worker @@ -269,6 +269,19 @@ class Preview(QWidget): l.setContentsMargins(0, 0, 0, 0) self.view = WebView(self) l.addWidget(self.view) + self.bar = QToolBar(self) + l.addWidget(self.bar) + + ac = actions['auto-reload-preview'] + ac.setCheckable(True) + ac.setChecked(True) + ac.toggled.connect(self.auto_reload_toggled) + self.auto_reload_toggled(ac.isChecked()) + self.bar.addAction(ac) + + ac = actions['reload-preview'] + ac.triggered.connect(self.refresh) + self.bar.addAction(ac) self.current_name = None self.last_sync_request = None @@ -301,7 +314,13 @@ class Preview(QWidget): self.view.clear() def start_refresh_timer(self): - self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000) + if actions['auto-reload-preview'].isChecked(): + self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000) def stop_refresh_timer(self): self.refresh_timer.stop() + + def auto_reload_toggled(self, checked): + actions['auto-reload-preview'].setToolTip(_( + 'Auto reload preview when text changes in editor') if not checked else _( + 'Disable auto reload of preview')) diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index 34491d1b68..1ff98cecd4 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -141,7 +141,8 @@ class Main(MainWindow): def reg(icon, text, target, sid, keys, description): ac = actions[sid] = QAction(QIcon(I(icon)), text, self) ac.setObjectName('action-' + sid) - ac.triggered.connect(target) + if target is not None: + ac.triggered.connect(target) if isinstance(keys, type('')): keys = (keys,) self.keyboard.register_shortcut( @@ -193,6 +194,10 @@ class Main(MainWindow): 'smarten-punctuation.png', _('&Smarten punctuation'), partial( self.boss.polish, 'smarten_punctuation', _('Smarten punctuation')), 'smarten-punctuation', (), _('Smarten punctuation')) + # Preview actions + self.action_auto_reload_preview = reg('auto-reload.png', _('Auto reload preview'), None, 'auto-reload-preview', (), _('Auto reload preview')) + self.action_reload_preview = reg('view-refresh.png', _('Refresh preview'), None, 'reload-preview', (), _('Refresh preview')) + def create_menubar(self): b = self.menuBar() From c1e5cddf5fb8cd1cba26d1f9253d1fb771e6a0b6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 10:31:37 +0530 Subject: [PATCH 21/28] Add inspect element and prevent preview from refreshing while it is hidden --- src/calibre/gui2/tweak_book/preview.py | 31 +++++++++++++++++++++++--- src/calibre/gui2/tweak_book/ui.py | 30 ++++++++++++++++++++----- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index bbc6e9b01e..eabf1e22c9 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -11,9 +11,9 @@ from threading import Thread from Queue import Queue, Empty from PyQt4.Qt import ( - QWidget, QVBoxLayout, QApplication, QSize, QNetworkAccessManager, + QWidget, QVBoxLayout, QApplication, QSize, QNetworkAccessManager, QMenu, QIcon, QNetworkReply, QTimer, QNetworkRequest, QUrl, Qt, QNetworkDiskCache, QToolBar) -from PyQt4.QtWebKit import QWebView +from PyQt4.QtWebKit import QWebView, QWebInspector from calibre import prints from calibre.constants import iswindows @@ -221,6 +221,7 @@ class WebView(QWebView): def __init__(self, parent=None): QWebView.__init__(self, parent) + self.inspector = QWebInspector(self) w = QApplication.instance().desktop().availableGeometry(self).width() self._size_hint = QSize(int(w/3), int(w/2)) settings = self.page().settings() @@ -260,6 +261,17 @@ class WebView(QWebView): def clear(self): self.setHtml('

') + def inspect(self): + self.inspector.parent().show() + self.inspector.parent().raise_() + self.pageAction(self.page().InspectElement).trigger() + + def contextMenuEvent(self, ev): + menu = QMenu(self) + menu.addAction(actions['reload-preview']) + menu.addAction(QIcon(I('debug.png')), _('Inspect element'), self.inspect) + menu.exec_(ev.globalPos()) + class Preview(QWidget): def __init__(self, parent=None): @@ -268,6 +280,8 @@ class Preview(QWidget): self.setLayout(l) l.setContentsMargins(0, 0, 0, 0) self.view = WebView(self) + self.inspector = self.view.inspector + self.inspector.setPage(self.view.page()) l.addWidget(self.view) self.bar = QToolBar(self) l.addWidget(self.bar) @@ -283,6 +297,8 @@ class Preview(QWidget): ac.triggered.connect(self.refresh) self.bar.addAction(ac) + actions['preview-dock'].toggled.connect(self.visibility_changed) + self.current_name = None self.last_sync_request = None self.refresh_timer = QTimer(self) @@ -313,8 +329,12 @@ class Preview(QWidget): def clear(self): self.view.clear() + @property + def is_visible(self): + return actions['preview-dock'].isChecked() + def start_refresh_timer(self): - if actions['auto-reload-preview'].isChecked(): + if self.is_visible and actions['auto-reload-preview'].isChecked(): self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000) def stop_refresh_timer(self): @@ -324,3 +344,8 @@ class Preview(QWidget): actions['auto-reload-preview'].setToolTip(_( 'Auto reload preview when text changes in editor') if not checked else _( 'Disable auto reload of preview')) + + def visibility_changed(self, is_visible): + if is_visible: + self.refresh() + diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index 1ff98cecd4..56a58fc95b 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -195,8 +195,9 @@ class Main(MainWindow): self.boss.polish, 'smarten_punctuation', _('Smarten punctuation')), 'smarten-punctuation', (), _('Smarten punctuation')) # Preview actions + group = _('Preview') self.action_auto_reload_preview = reg('auto-reload.png', _('Auto reload preview'), None, 'auto-reload-preview', (), _('Auto reload preview')) - self.action_reload_preview = reg('view-refresh.png', _('Refresh preview'), None, 'reload-preview', (), _('Refresh preview')) + self.action_reload_preview = reg('view-refresh.png', _('Refresh preview'), None, 'reload-preview', ('F5', 'Ctrl+R'), _('Refresh preview')) def create_menubar(self): b = self.menuBar() @@ -239,20 +240,37 @@ class Main(MainWindow): b.addAction(self.action_smarten_punctuation) def create_docks(self): - self.file_list_dock = d = QDockWidget(_('&Files Browser'), self) - d.setObjectName('file_list_dock') # Needed for saveState + + def create(name, oname): + oname += '-dock' + d = QDockWidget(name, self) + d.setObjectName(oname) # Needed for saveState + ac = d.toggleViewAction() + desc = _('Toggle %s') % name.replace('&', '') + self.keyboard.register_shortcut( + oname, desc, description=desc, action=ac, group=_('Windows')) + actions[oname] = ac + setattr(self, oname.replace('-', '_'), d) + return d + + d = create(_('&Files Browser'), 'files-browser') d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.file_list = FileListWidget(d) d.setWidget(self.file_list) self.addDockWidget(Qt.LeftDockWidgetArea, d) - self.preview_dock = d = QDockWidget(_('File &Preview'), self) - d.setObjectName('file_preview') # Needed for saveState + d = create(_('File &Preview'), 'preview') d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.preview = Preview(d) d.setWidget(self.preview) self.addDockWidget(Qt.RightDockWidgetArea, d) + d = create(_('&Inspector'), 'inspector') + d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea) + d.setWidget(self.preview.inspector) + self.preview.inspector.setParent(d) + self.addDockWidget(Qt.BottomDockWidgetArea, d) + def resizeEvent(self, ev): self.blocking_job.resize(ev.size()) return super(Main, self).resizeEvent(ev) @@ -282,3 +300,5 @@ class Main(MainWindow): state = tprefs.get('main_window_state', None) if state is not None: self.restoreState(state, self.STATE_VERSION) + # We never want to start with the inspector showing + self.inspector_dock.close() From aa0d4f1b71662eb8eb65701ed562f6f44f46e703 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 10:44:10 +0530 Subject: [PATCH 22/28] ARG Noticias by Darko Miletic Fixes #1249506 [New recipe for Argentinian news portal ArgNoticias](https://bugs.launchpad.net/calibre/+bug/1249506) --- recipes/argnoticias.recipe | 96 ++++++++++++++++++++++++++++++++++ recipes/icons/argnoticias.png | Bin 0 -> 171 bytes 2 files changed, 96 insertions(+) create mode 100644 recipes/argnoticias.recipe create mode 100644 recipes/icons/argnoticias.png diff --git a/recipes/argnoticias.recipe b/recipes/argnoticias.recipe new file mode 100644 index 0000000000..03e3627064 --- /dev/null +++ b/recipes/argnoticias.recipe @@ -0,0 +1,96 @@ + +__license__ = 'GPL v3' +__copyright__ = '2013, Darko Miletic ' + +''' +www.argnoticias.com +''' + +import time +from calibre import strftime +from calibre.web.feeds.recipes import BasicNewsRecipe + +class ArgNoticias(BasicNewsRecipe): + title = 'ARG Noticias' + __author__ = 'Darko Miletic' + description = 'Ultimas noticias de Argentina' + publisher = 'ARG Noticias' + category = 'news, politics, Argentina' + oldest_article = 2 + max_articles_per_feed = 100 + no_stylesheets = True + encoding = 'utf-8' + use_embedded_content = False + masthead_url = 'http://www.argnoticias.com/images/arg-logo-footer.png' + language = 'es_AR' + publication_type = 'newsportal' + INDEX = 'http://www.argnoticias.com' + extra_css = '' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher': publisher + , 'language' : language + } + + keep_only_tags = [dict(name='div', attrs={'class':['itemHeader','itemBody','itemAuthorBlock']})] + + remove_tags = [ + dict(name=['object','link','base','iframe']), + dict(name='div', attrs={'class':['b2jsocial_parent','itemSocialSharing']}) + ] + + feeds = [ + (u'Politica' , u'http://www.argnoticias.com/index.php/politica' ) + ,(u'Economia' , u'http://www.argnoticias.com/index.php/economia' ) + ,(u'Sociedad' , u'http://www.argnoticias.com/index.php/sociedad' ) + ,(u'Mundo' , u'http://www.argnoticias.com/index.php/mundo' ) + ,(u'Deportes' , u'http://www.argnoticias.com/index.php/deportes' ) + ,(u'Espectaculos', u'http://www.argnoticias.com/index.php/espectaculos') + ,(u'Tendencias' , u'http://www.argnoticias.com/index.php/tendencias' ) + ] + + def parse_index(self): + totalfeeds = [] + lfeeds = self.get_feeds() + checker = [] + for feedobj in lfeeds: + feedtitle, feedurl = feedobj + self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl)) + articles = [] + soup = self.index_to_soup(feedurl) + for item in soup.findAll('div', attrs={'class':'Nota'}): + atag = item.find('a', attrs={'class':'moduleItemTitle'}) + ptag = item.find('div', attrs={'class':'moduleItemIntrotext'}) + url = self.INDEX + atag['href'] + title = self.tag_to_string(atag) + description = self.tag_to_string(ptag) + date = strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime()) + if url not in checker: + checker.append(url) + articles.append({ + 'title' :title + ,'date' :date + ,'url' :url + ,'description':description + }) + + for item in soup.findAll('li'): + atag = item.find('a', attrs={'class':'moduleItemTitle'}) + if atag: + ptag = item.find('div', attrs={'class':'moduleItemIntrotext'}) + url = self.INDEX + atag['href'] + title = self.tag_to_string(atag) + description = self.tag_to_string(ptag) + date = strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime()) + if url not in checker: + checker.append(url) + articles.append({ + 'title' :title + ,'date' :date + ,'url' :url + ,'description':description + }) + totalfeeds.append((feedtitle, articles)) + return totalfeeds diff --git a/recipes/icons/argnoticias.png b/recipes/icons/argnoticias.png new file mode 100644 index 0000000000000000000000000000000000000000..bdc5fc3290cc782820d9813ad28a5a0910ee2fb7 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|MV>B>Ar*6y|NQ@N&&)GtpJn~| zpJ{A&78pH}6P{E2=2pp{Rf-Lq>wZ00n%A^SP^u$@m!(I*e#`m*B{$Ox{EMYB7BNZ{ z*2Dzu5HN3-*d*un Date: Sat, 9 Nov 2013 11:18:56 +0530 Subject: [PATCH 23/28] Create View menu --- src/calibre/gui2/tweak_book/__init__.py | 11 ++++- src/calibre/gui2/tweak_book/editor/widget.py | 2 + src/calibre/gui2/tweak_book/ui.py | 44 +++++++++++++------- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/calibre/gui2/tweak_book/__init__.py b/src/calibre/gui2/tweak_book/__init__.py index aff0f23a5d..dddcc1ca4e 100644 --- a/src/calibre/gui2/tweak_book/__init__.py +++ b/src/calibre/gui2/tweak_book/__init__.py @@ -24,5 +24,12 @@ def set_current_container(container): global _current_container _current_container = container -actions = {} -editors = {} +class NonReplaceDict(dict): + + def __setitem__(self, k, v): + if k in self: + raise ValueError('The key %s is already present' % k) + dict.__setitem__(self, k, v) + +actions = NonReplaceDict() +editors = NonReplaceDict() diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index b959a2d47c..f59c6de608 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -120,6 +120,8 @@ class Editor(QMainWindow): 'There is no suitable text in the clipboard to paste.'), show=True) self.editor.paste() + def contextMenuEvent(self, ev): + ev.ignore() def launch_editor(path_to_edit, path_is_raw=False, syntax='html'): if path_is_raw: diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index 56a58fc95b..d69fbab793 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -109,9 +109,9 @@ class Main(MainWindow): self.keyboard = KeyboardManager() self.create_actions() - self.create_menubar() - self.create_toolbar() + self.create_toolbars() self.create_docks() + self.create_menubar() self.status_bar = self.statusBar() self.status_bar.addPermanentWidget(self.boss.save_manager.status_widget) @@ -224,20 +224,31 @@ class Main(MainWindow): e.addAction(self.action_subset_fonts) e.addAction(self.action_smarten_punctuation) - def create_toolbar(self): - self.global_bar = b = self.addToolBar(_('Book tool bar')) - b.setObjectName('global_bar') # Needed for saveState - b.addAction(self.action_open_book) - b.addAction(self.action_global_undo) - b.addAction(self.action_global_redo) - b.addAction(self.action_save) - b.addAction(self.action_toc) + e = b.addMenu(_('&View')) + t = e.addMenu(_('Tool&bars')) + e.addSeparator() + for name, ac in actions.iteritems(): + if name.endswith('-dock'): + e.addAction(ac) + elif name.endswith('-bar'): + t.addAction(ac) - self.polish_bar = b = self.addToolBar(_('Polish book tool bar')) - b.setObjectName('polish_bar') # Needed for saveState - b.addAction(self.action_embed_fonts) - b.addAction(self.action_subset_fonts) - b.addAction(self.action_smarten_punctuation) + def create_toolbars(self): + def create(text, name): + name += '-bar' + b = self.addToolBar(text) + b.setObjectName(name) # Needed for saveState + setattr(self, name.replace('-', '_'), b) + actions[name] = b.toggleViewAction() + return b + + a = create(_('Book tool bar'), 'global').addAction + for x in ('open_book', 'global_undo', 'global_redo', 'save', 'toc'): + a(getattr(self, 'action_' + x)) + + a = create(_('Polish book tool bar'), 'polish').addAction + for x in ('embed_fonts', 'subset_fonts', 'smarten_punctuation'): + a(getattr(self, 'action_' + x)) def create_docks(self): @@ -302,3 +313,6 @@ class Main(MainWindow): self.restoreState(state, self.STATE_VERSION) # We never want to start with the inspector showing self.inspector_dock.close() + + def contextMenuEvent(self, ev): + ev.ignore() From b984f394f90522683232231c18dc961e55406270 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 11:30:26 +0530 Subject: [PATCH 24/28] Transliterate the Arabic Alef into a Fixes #1249469 [Transliteration of metadata from Arabic to English is not complete](https://bugs.launchpad.net/calibre/+bug/1249469) --- src/calibre/ebooks/unihandecode/unicodepoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/unihandecode/unicodepoints.py b/src/calibre/ebooks/unihandecode/unicodepoints.py index 05cd51cadb..fe1495de73 100644 --- a/src/calibre/ebooks/unihandecode/unicodepoints.py +++ b/src/calibre/ebooks/unihandecode/unicodepoints.py @@ -178,7 +178,7 @@ CODEPOINTS = { 'x06': [ '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ',', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ';', '[?]', '[?]', '[?]', '?', - '[?]', '', 'a', '\'', 'w\'', '', 'y\'', '', 'b', '@', 't', 'th', 'j', 'H', 'kh', 'd', + '[?]', '', 'a', '\'', 'w\'', '', 'y\'', 'a', 'b', '@', 't', 'th', 'j', 'H', 'kh', 'd', 'dh', 'r', 'z', 's', 'sh', 'S', 'D', 'T', 'Z', '`', 'G', '[?]', '[?]', '[?]', '[?]', '[?]', '', 'f', 'q', 'k', 'l', 'm', 'n', 'h', 'w', '~', 'y', 'an', 'un', 'in', 'a', 'u', 'i', 'W', '', '', '\'', '\'', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', From c2f02dd4237788e1b5d962be8d26f38c5fef8e81 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 14:34:38 +0530 Subject: [PATCH 25/28] Add more color themes and auto detect theme based on users GUI color palette --- src/calibre/gui2/tweak_book/editor/text.py | 4 +- src/calibre/gui2/tweak_book/editor/themes.py | 136 ++++++++++++++++--- 2 files changed, 119 insertions(+), 21 deletions(-) diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 2c1980be34..afd3e8c6dc 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -14,7 +14,7 @@ from PyQt4.Qt import ( QTextEdit, QTextFormat, QWidget, QSize, QPainter, Qt, QRect) from calibre.gui2.tweak_book import tprefs -from calibre.gui2.tweak_book.editor.themes import THEMES, DEFAULT_THEME, theme_color +from calibre.gui2.tweak_book.editor.themes import THEMES, default_theme, theme_color from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter from calibre.gui2.tweak_book.editor.syntax.html import HTMLHighlighter, XMLHighlighter from calibre.gui2.tweak_book.editor.syntax.css import CSSHighlighter @@ -74,7 +74,7 @@ class TextEdit(QPlainTextEdit): self.setLineWrapMode(QPlainTextEdit.WidgetWidth if prefs['editor_line_wrap'] else QPlainTextEdit.NoWrap) theme = THEMES.get(prefs['editor_theme'], None) if theme is None: - theme = THEMES[DEFAULT_THEME] + theme = THEMES[default_theme()] self.apply_theme(theme) def apply_theme(self, theme): diff --git a/src/calibre/gui2/tweak_book/editor/themes.py b/src/calibre/gui2/tweak_book/editor/themes.py index 3d3b5fa618..dbdccbc853 100644 --- a/src/calibre/gui2/tweak_book/editor/themes.py +++ b/src/calibre/gui2/tweak_book/editor/themes.py @@ -8,21 +8,67 @@ __copyright__ = '2013, Kovid Goyal ' from collections import namedtuple -from PyQt4.Qt import (QColor, QTextCharFormat, QBrush, QFont) +from PyQt4.Qt import (QColor, QTextCharFormat, QBrush, QFont, QApplication, QPalette) underline_styles = {'single', 'dash', 'dot', 'dash_dot', 'dash_dot_dot', 'wave', 'spell'} -DEFAULT_THEME = 'calibre-dark' +_default_theme = None +def default_theme(): + global _default_theme + if _default_theme is None: + isdark = QApplication.instance().palette().color(QPalette.WindowText).lightness() > 128 + _default_theme = 'wombat-dark' if isdark else 'pyte-light' + return _default_theme + +# The solarized themes {{{ +SLDX = {'base03':'1c1c1c', 'base02':'262626', 'base01':'585858', 'base00':'626262', 'base0':'808080', 'base1':'8a8a8a', 'base2':'e4e4e4', 'base3':'ffffd7', 'yellow':'af8700', 'orange':'d75f00', 'red':'d70000', 'magenta':'af005f', 'violet':'5f5faf', 'blue':'0087ff', 'cyan':'00afaf', 'green':'5f8700'} # noqa +SLD = {'base03':'002b36', 'base02':'073642', 'base01':'586e75', 'base00':'657b83', 'base0':'839496', 'base1':'93a1a1', 'base2':'eee8d5', 'base3':'fdf6e3', 'yellow':'b58900', 'orange':'cb4b16', 'red':'dc322f', 'magenta':'d33682', 'violet':'6c71c4', 'blue':'268bd2', 'cyan':'2aa198', 'green':'859900'} # noqa +m = {'base%d'%n:'base%02d'%n for n in xrange(1, 4)} +m.update({'base%02d'%n:'base%d'%n for n in xrange(1, 4)}) +SLL = {m.get(k, k) : v for k, v in SLD.iteritems()} +SLLX = {m.get(k, k) : v for k, v in SLDX.iteritems()} +SOLARIZED = \ + ''' + CursorLine bg={base02} + CursorColumn bg={base02} + ColorColumn bg={base02} + MatchParen fg={red} bg={base01} bold + Pmenu fg={base0} bg={base02} + PmenuSel fg={base01} bg={base2} + + Cursor fg={base03} bg={base0} + Normal fg={base0} bg={base03} + LineNr fg={base01} bg={base02} + LineNrC fg={magenta} + Visual fg={base01} bg={base03} + + Comment fg={base01} italic + Todo fg={magenta} bold + String fg={cyan} + Constant fg={cyan} + Number fg={cyan} + PreProc fg={orange} + Identifier fg={blue} + Function fg={blue} + Type fg={yellow} + Statement fg={green} bold + Keyword fg={green} + Special fg={red} + + Error us=wave uc={red} + Tooltip fg=black bg=ffffed + ''' +# }}} THEMES = { - 'calibre-dark': # {{{ Based on the wombat color scheme for vim + 'wombat-dark': # {{{ ''' - CursorLine bg=2d2d2d - CursorColumn bg=2d2d2d - ColorColumn bg=2d2d2d + CursorLine bg={cursor_loc} + CursorColumn bg={cursor_loc} + ColorColumn bg={cursor_loc} MatchParen fg=f6f3e8 bg=857b6f bold Pmenu fg=f6f3e8 bg=444444 - PmenuSel fg=yellow bg=cae682 + PmenuSel fg=yellow bg={identifier} Tooltip fg=black bg=ffffed Cursor bg=656565 @@ -31,27 +77,79 @@ THEMES = { LineNrC fg=yellow Visual fg=f6f3e8 bg=444444 - Comment fg=99968b + Comment fg={comment} Todo fg=8f8f8f - String fg=95e454 - Identifier fg=cae682 - Function fg=cae682 - Type fg=cae682 - Statement fg=8ac6f2 - Keyword fg=8ac6f2 - Constant fg=e5786d - PreProc fg=e5786d - Number fg=e5786d + String fg={string} + Constant fg={constant} + Number fg={constant} + PreProc fg={constant} + Identifier fg={identifier} + Function fg={identifier} + Type fg={identifier} + Statement fg={keyword} + Keyword fg={keyword} Special fg=e7f6da Error us=wave uc=red - ''', # }}} + '''.format( + cursor_loc='2d2d2d', + identifier='cae682', + comment='99968b', + string='95e454', + keyword='8ac6f2', + constant='e5786d'), # }}} + + 'pyte-light': # {{{ + ''' + CursorLine bg={cursor_loc} + CursorColumn bg={cursor_loc} + ColorColumn bg={cursor_loc} + MatchParen fg=white bg=80a090 bold + Pmenu fg=white bg=808080 + PmenuSel fg=white bg=808080 + Tooltip fg=black bg=ffffed + + Cursor fg=black bg=b0b4b8 + Normal fg=404850 bg=f0f0f0 + LineNr fg=white bg=a0b0c0 + LineNrC fg=yellow + Visual fg=black bg=lightyellow + + Comment fg={comment} italic + Todo fg={comment} italic bold + String fg={string} + Constant fg={constant} + Number fg={constant} + PreProc fg={constant} + Identifier fg={identifier} + Function fg={identifier} + Type fg={identifier} + Statement fg={keyword} + Keyword fg={keyword} + Special fg=70a0d0 italic + Error us=wave uc=red + + '''.format( + cursor_loc='f6f6f6', + identifier='7b5694', + comment='a0b0c0', + string='4070a0', + keyword='007020', + constant='a07040'), # }}} + + 'solarized-x-dark': SOLARIZED.format(**SLDX), + 'solarized-dark': SOLARIZED.format(**SLD), + 'solarized-light': SOLARIZED.format(**SLL), + 'solarized-x-light': SOLARIZED.format(**SLLX), } def read_color(col): if QColor.isValidColor(col): return QBrush(QColor(col)) + if col.startswith('rgb('): + r, g, b = map(int, (x.strip() for x in col[4:-1].split(','))) + return QBrush(QColor(r, g, b)) try: r, g, b = col[0:2], col[2:4], col[4:6] r, g, b = int(r, 16), int(g, 16), int(b, 16) @@ -122,5 +220,5 @@ def theme_color(theme, name, attr): try: return getattr(theme[name], attr).color() except (KeyError, AttributeError): - return getattr(THEMES[DEFAULT_THEME], attr).color() + return getattr(THEMES[default_theme()], attr).color() From c7ff0215413477180c0068505f3fc286075c6747 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 14:41:15 +0530 Subject: [PATCH 26/28] ... --- src/calibre/gui2/tweak_book/editor/themes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/tweak_book/editor/themes.py b/src/calibre/gui2/tweak_book/editor/themes.py index dbdccbc853..06d74c5528 100644 --- a/src/calibre/gui2/tweak_book/editor/themes.py +++ b/src/calibre/gui2/tweak_book/editor/themes.py @@ -111,9 +111,9 @@ THEMES = { Cursor fg=black bg=b0b4b8 Normal fg=404850 bg=f0f0f0 - LineNr fg=white bg=a0b0c0 + LineNr fg=white bg=8090a0 LineNrC fg=yellow - Visual fg=black bg=lightyellow + Visual fg=white bg=8090a0 Comment fg={comment} italic Todo fg={comment} italic bold From 6cda45e42f4bf508f6206e23d29a4d82ada708f5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 14:44:04 +0530 Subject: [PATCH 27/28] Fix standalone editor launch not working --- src/calibre/gui2/tweak_book/editor/widget.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index f59c6de608..088794b4f0 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -76,10 +76,16 @@ class Editor(QMainWindow): self.action_bar = b = self.addToolBar(_('File actions tool bar')) b.setObjectName('action_bar') # Needed for saveState for x in ('save', 'undo', 'redo'): - b.addAction(actions['editor-%s' % x]) + try: + b.addAction(actions['editor-%s' % x]) + except KeyError: + pass self.edit_bar = b = self.addToolBar(_('Edit actions tool bar')) for x in ('cut', 'copy', 'paste'): - b.addAction(actions['editor-%s' % x]) + try: + b.addAction(actions['editor-%s' % x]) + except KeyError: + pass def break_cycles(self): self.modification_state_changed.disconnect() @@ -136,7 +142,7 @@ def launch_editor(path_to_edit, path_is_raw=False, syntax='html'): syntax = 'css' app = QApplication([]) t = Editor(syntax) - t.load_text(raw, syntax=syntax) + t.data = raw t.show() app.exec_() From 3113191c80de028fa31b81189ed52d427e820d0f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Nov 2013 14:56:41 +0530 Subject: [PATCH 28/28] ... --- src/calibre/gui2/tweak_book/editor/themes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/editor/themes.py b/src/calibre/gui2/tweak_book/editor/themes.py index 06d74c5528..fcd35fdad5 100644 --- a/src/calibre/gui2/tweak_book/editor/themes.py +++ b/src/calibre/gui2/tweak_book/editor/themes.py @@ -130,7 +130,7 @@ THEMES = { Error us=wave uc=red '''.format( - cursor_loc='f6f6f6', + cursor_loc='white', identifier='7b5694', comment='a0b0c0', string='4070a0',