From f6554d74bbee9dcfc3a71bb2de1769b07b17d7e0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 3 Nov 2013 14:01:14 +0530 Subject: [PATCH] Add basic editor actions --- resources/images/edit-undo.png | Bin 1986 -> 3440 bytes src/calibre/gui2/tweak_book/__init__.py | 2 ++ src/calibre/gui2/tweak_book/boss.py | 33 +++++++++++++++++-- src/calibre/gui2/tweak_book/editor/widget.py | 23 +++++++++++++ src/calibre/gui2/tweak_book/ui.py | 21 ++++++++++-- 5 files changed, 74 insertions(+), 5 deletions(-) diff --git a/resources/images/edit-undo.png b/resources/images/edit-undo.png index f6d7e8ba56eeb0e4ea9083e4979a7f7b79448b39..5071aa1dd3c41f3c1ba760256d3e60efc1b46acc 100644 GIT binary patch delta 3379 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 JPDHLkV1m>-W$6F_ delta 1913 zcmV-<2Zs3Y8p02dI|~ih000fw0YWI7c#%dYe*hD7MObuHX>@F508emqX=7n*AVhL$ zc4a;hc2QJZS$1@!GmPV?GmN4lK3E-Z%ZQL9p3TcyY%v9;JK&@Qi%MzCyXsl3`ElxNe@QqrbD3L&&jlcs6Xqc8s0>c8s0>c8=%Fl8d zRlbFi%cYc8Ascywx0MySH2n`DP_c=|AD2>9i*gBko>u-SfL^Z^2KB0dVhd#!XVK`W z6%fL6Z7c1@QvSCOfj$cYzH_`4MEuaJf0pCe7J4Hp1;XKrxzfG-dkTS`iZH&ja9PU0 z`)rIlfpGXe9KHvE?qdjee)<(<==IcryO%zy1j6BeBZlcNM_zl8SL}<%X%o&-tJJ*PD{g=ZZe}zFc zU#R`VQ8A2$!~c&57Z(T3hTpzQj3Xo1{|$8?2;r5&aW`Bb96q1JJ2_}N{I;cHI#apt z@8AE`>L20nbcplB;Xd|vDe-I8YypDeR0IPY{>eneG%oR{aAw<8K1%_XU7 z-vhyAfzL7zzP4FWLoc^XfV->Tt3$>2V?0MU_HT*cpHgmt!<$H{t9}i=f1R1=X`O{0 z!*fx@-&jHix496UvsTWlVbL`pNR+K7Wz&gQ;I*ZrtK~U#=^l?7x{V3J$+W5ZEgafF z84*VzLE->QT&R2*J(iVlH$4v5g@;7Ao1WzFzh=?w>&QAj=YP;``ULLgrz4izW9CuO z*NS7`-xeszq5C4v1A;_pe-0_nD_(%doC=5bVc0eCA{^RB;9&`EY4or{OHVWJ3|=0SAZ zHlwNLEeQLU4q_(Wn@zWuX44CNp1l?FiRfyVp!57xQJLM=^)P9cf1_kAW&P$_sB|@r z-^ZcO9_8jVEv0Xa+T$|U9KvL|3~nTNKzSQwQg!ex@c-c|tKH*c^4MXPCA z#f~X_&s{4jr(Hb>F0&N7Gp|zMqC^o(s9D0HpT{*3i?&+oho#{`^f-^8=sP+xXn{=H zW;pXDyzX*QIabZnFl&>Aa;mQBo>-75%(y5q7ws%z;uBuPf8LY;W5Z|QeJX!XXt{*O z@v$=tSJU3gqQ`h)=Y&oBV!tv3Ivf814Uc@hZ`L(Gl@KI$q_f1qC*U-G4y*QAm`~pw z+`8IQa7cEqe@BM+`H(D~Vz;N!WZ^q`I?XQlmJ<0q%XXwu4sUldial#?(bkka@3QDN zA>jA$1^g9Ee|03n(ef@T3*%9c{u{&Sl_cb8boiib5)Yp|R_l||c=B}|+ByjwGBe_F zrv0(Joy-VwvzbI@8I!~C{VEi^iq!gjMwi!hh==FLxP?hXN5lPm9VVeDYg8bA)c_OO z)$)UcW^9+x?tBU9w|_$$`Gm6vdZ=YmQEa=KCNtC9f9q&AlW0-VGA74dPdm+Zq%f;U z%eWXs!lE7vSL>%x6x<6gJ7Y+njrJ2zvw)ff)Rzg=vp_uy)U!Z62abS4mjLaF=dtZ; zUd*LNSj-KTKnDw0ekIVs0u~lvTnsL!<8&OHE&oD?_C4f(6XTJsh_IX?(>HQuOM^i1oy#R-P93zOQ;e=nA_H!hJ{nwJ)L>QhcST2q>>jfq{l z+BZ;lU>eG{{uQ|^MmWr+U}pOvL0(b|*`Xzoih=n)S4XGRdXjk)!P2wW6bC3x&FanGgzk4>K{ey_>m<86T?si{Wo(M%>U>O#BlQ5*ZWf7DBi`g%}sw#)Uo~ z8|wT0f=AUmG9g%~bz&~#rbvN@U-AtLTxb3dDDuVvfqd)O00000NkvXXu0mjf)0(O0 diff --git a/src/calibre/gui2/tweak_book/__init__.py b/src/calibre/gui2/tweak_book/__init__.py index afb7ca1f3d..ab9390ef53 100644 --- a/src/calibre/gui2/tweak_book/__init__.py +++ b/src/calibre/gui2/tweak_book/__init__.py @@ -22,3 +22,5 @@ def current_container(): def set_current_container(container): global _current_container _current_container = container + +actions = {} diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 134c7973de..62569986d0 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -21,7 +21,7 @@ from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_cont from calibre.ebooks.oeb.polish.replace import rename_files from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog from calibre.gui2.dialogs.confirm_delete import confirm -from calibre.gui2.tweak_book import set_current_container, current_container, tprefs +from calibre.gui2.tweak_book import set_current_container, current_container, tprefs, actions from calibre.gui2.tweak_book.undo import GlobalUndoHistory from calibre.gui2.tweak_book.save import SaveManager from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime @@ -49,6 +49,7 @@ class Boss(QObject): fl.reorder_spine.connect(self.reorder_spine) fl.rename_requested.connect(self.rename_requested) fl.edit_file.connect(self.edit_file_requested) + self.gui.central.current_editor_changed.connect(self.apply_current_editor_state) def mkdtemp(self): self.container_count += 1 @@ -217,11 +218,12 @@ class Boss(QObject): editor = self.editors.get(name, None) if editor is None: editor = self.editors[name] = editor_from_syntax(syntax, self.gui.editor_tabs) + editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed) + editor.modification_state_changed.connect(self.editor_modification_state_changed) self.gui.central.add_editor(name, editor) c = current_container() editor.load_text(c.decode(c.open(name).read())) self.gui.central.show_editor(editor) - self.gui.keyboard.set_mode(syntax) def edit_file_requested(self, name, syntax, mime): if name in self.editors: @@ -234,6 +236,33 @@ class Boss(QObject): _('Editing of files of type %s is not supported' % mime), show=True) self.edit_file(name, syntax) + def do_editor_undo(self): + ed = self.gui.central.current_editor + if ed is not None: + ed.undo() + + def do_editor_redo(self): + ed = self.gui.central.current_editor + if ed is not None: + ed.redo() + + def editor_undo_redo_state_changed(self, *args): + self.apply_current_editor_state(update_keymap=False) + + def editor_modification_state_changed(self, *args): + self.apply_current_editor_state(update_keymap=False) + + def apply_current_editor_state(self, update_keymap=True): + ed = self.gui.central.current_editor + if ed is not None: + actions['editor-undo'].setEnabled(ed.undo_available) + actions['editor-redo'].setEnabled(ed.redo_available) + actions['editor-save'].setEnabled(ed.is_modified) + self.gui.keyboard.set_mode(ed.syntax) + + def do_editor_save(self): + pass # TODO: Implement this + # Shutdown {{{ def quit(self): if not self.confirm_quit(): diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 36e02c755f..72e615a1e7 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -8,11 +8,13 @@ __copyright__ = '2013, Kovid Goyal ' from PyQt4.Qt import QMainWindow, Qt, QApplication, pyqtSignal +from calibre.gui2.tweak_book import actions from calibre.gui2.tweak_book.editor.text import TextEdit class Editor(QMainWindow): modification_state_changed = pyqtSignal(object) + undo_redo_state_changed = pyqtSignal(object, object) def __init__(self, syntax, parent=None): QMainWindow.__init__(self, parent) @@ -23,10 +25,28 @@ class Editor(QMainWindow): self.setCentralWidget(self.editor) self.editor.modificationChanged.connect(self.modification_state_changed.emit) self.create_toolbars() + self.undo_available = False + self.redo_available = False + self.editor.undoAvailable.connect(self._undo_available) + self.editor.redoAvailable.connect(self._redo_available) + + 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 load_text(self, raw): self.editor.load_text(raw, syntax=self.syntax) + def undo(self): + self.editor.undo() + + def redo(self): + self.editor.redo() + @dynamic_property def is_modified(self): def fget(self): @@ -38,6 +58,9 @@ class Editor(QMainWindow): def create_toolbars(self): self.action_bar = b = self.addToolBar(_('Edit 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']) def launch_editor(path_to_edit, path_is_raw=False, syntax='html'): diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index f4c536f254..9bbec5bc39 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -8,11 +8,11 @@ __copyright__ = '2013, Kovid Goyal ' from PyQt4.Qt import ( QDockWidget, Qt, QLabel, QIcon, QAction, QApplication, QWidget, - QVBoxLayout, QStackedWidget, QTabWidget, QImage, QPixmap) + QVBoxLayout, QStackedWidget, QTabWidget, QImage, QPixmap, pyqtSignal) from calibre.constants import __appname__, get_version from calibre.gui2.main_window import MainWindow -from calibre.gui2.tweak_book import current_container, tprefs +from calibre.gui2.tweak_book import current_container, tprefs, actions from calibre.gui2.tweak_book.file_list import FileListWidget from calibre.gui2.tweak_book.job import BlockingJob from calibre.gui2.tweak_book.boss import Boss @@ -21,6 +21,8 @@ from calibre.gui2.tweak_book.keyboard import KeyboardManager class Central(QStackedWidget): ' The central widget, hosts the editors ' + current_editor_changed = pyqtSignal() + def __init__(self, parent=None): QStackedWidget.__init__(self, parent) self.welcome = w = QLabel('

'+_( @@ -47,6 +49,7 @@ class Central(QStackedWidget): self.modified_icon = QIcon(QPixmap.fromImage(i)) else: self.modified_icon = QIcon(I('modified.png')) + self.editor_tabs.currentChanged.connect(self.current_editor_changed) def add_editor(self, name, editor): fname = name.rpartition('/')[2] @@ -65,6 +68,10 @@ class Central(QStackedWidget): modified = getattr(editor, 'is_modified', False) tb.setTabIcon(i, self.modified_icon if modified else QIcon()) + @property + def current_editor(self): + return self.editor_tabs.currentWidget() + class Main(MainWindow): APP_NAME = _('Tweak Book') @@ -113,7 +120,7 @@ class Main(MainWindow): group = _('Global Actions') def reg(icon, text, target, sid, keys, description): - ac = QAction(QIcon(I(icon)), text, self) + ac = actions[sid] = QAction(QIcon(I(icon)), text, self) ac.setObjectName('action-' + sid) ac.triggered.connect(target) if isinstance(keys, type('')): @@ -132,6 +139,14 @@ class Main(MainWindow): self.action_save.setEnabled(False) self.action_quit = reg('quit.png', _('&Quit'), self.boss.quit, 'quit', 'Ctrl+Q', _('Quit')) + # 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', + _('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')) + def create_menubar(self): b = self.menuBar()