1# This file is part of the Frescobaldi project, http://www.frescobaldi.org/ 2# 3# Copyright (c) 2011 - 2014 by Wilbert Berendsen 4# 5# This program is free software; you can redistribute it and/or 6# modify it under the terms of the GNU General Public License 7# as published by the Free Software Foundation; either version 2 8# of the License, or (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18# See http://www.gnu.org/licenses/ for more information. 19 20""" 21The sidebar in the editor View. 22""" 23 24 25import sys 26 27from PyQt5.QtCore import QSettings 28from PyQt5.QtWidgets import QAction, QApplication 29 30import app 31import actioncollection 32import actioncollectionmanager 33import plugin 34 35 36class SideBarManager(plugin.MainWindowPlugin): 37 def __init__(self, mainwindow): 38 self.actionCollection = ac = Actions() 39 actioncollectionmanager.manager(mainwindow).addActionCollection(ac) 40 ac.view_linenumbers.triggered.connect(self.toggleLineNumbers) 41 ac.folding_enable.triggered.connect(self.toggleFolding) 42 ac.folding_fold_current.triggered.connect(self.foldCurrent) 43 ac.folding_fold_top.triggered.connect(self.foldTop) 44 ac.folding_unfold_current.triggered.connect(self.unfoldCurrent) 45 ac.folding_fold_all.triggered.connect(self.foldAll) 46 ac.folding_unfold_all.triggered.connect(self.unfoldAll) 47 mainwindow.viewManager.activeViewSpaceChanged.connect(self.updateActions) 48 app.viewSpaceCreated.connect(self.newViewSpace) 49 # there is always one ViewSpace, initialize it 50 self.manager().loadSettings() 51 self.updateActions() 52 53 def manager(self, viewspace=None): 54 """Returns the ViewSpaceSideBarManager for the (current) ViewSpace.""" 55 if viewspace is None: 56 viewspace = self.mainwindow().viewManager.activeViewSpace() 57 return ViewSpaceSideBarManager.instance(viewspace) 58 59 def toggleLineNumbers(self): 60 manager = self.manager() 61 manager.setLineNumbersVisible(not manager.lineNumbersVisible()) 62 manager.saveSettings() 63 64 def toggleFolding(self): 65 """Toggle folding in the current view.""" 66 viewspace = self.mainwindow().viewManager.activeViewSpace() 67 manager = self.manager(viewspace) 68 enable = not manager.foldingVisible() 69 document = viewspace.document() 70 71 # do it for all managers that display our document 72 for m in manager.instances(): 73 if m.viewSpace().document() is document: 74 m.setFoldingVisible(enable) 75 # and also update in other windows 76 for i in self.instances(): 77 i.updateActions() 78 manager.saveSettings() 79 # unfold the document if disabled 80 if not enable: 81 self.folder().unfold_all() 82 83 def folder(self): 84 """Get the Folder for the current document.""" 85 import folding 86 return folding.Folder.get(self.mainwindow().currentDocument()) 87 88 def foldCurrent(self): 89 """Fold current region.""" 90 self.folder().fold(self.mainwindow().textCursor().block()) 91 92 def foldTop(self): 93 """Fold current region to toplevel.""" 94 self.folder().fold(self.mainwindow().textCursor().block(), -1) 95 96 def unfoldCurrent(self): 97 """Unfold current region.""" 98 block = self.mainwindow().textCursor().block() 99 folder = self.folder() 100 folder.ensure_visible(block) 101 folder.unfold(block) 102 103 def foldAll(self): 104 """Fold the whole document.""" 105 self.folder().fold_all() 106 107 def unfoldAll(self): 108 """Unfold the whole document.""" 109 self.folder().unfold_all() 110 111 def updateActions(self): 112 manager = self.manager() 113 ac = self.actionCollection 114 ac.view_linenumbers.setChecked(manager.lineNumbersVisible()) 115 folding = manager.foldingVisible() 116 ac.folding_enable.setChecked(folding) 117 ac.folding_fold_current.setEnabled(folding) 118 ac.folding_fold_top.setEnabled(folding) 119 ac.folding_unfold_current.setEnabled(folding) 120 ac.folding_fold_all.setEnabled(folding) 121 ac.folding_unfold_all.setEnabled(folding) 122 123 def newViewSpace(self, viewspace): 124 viewmanager = viewspace.manager() 125 if viewmanager and viewmanager.window() is self.mainwindow(): 126 # let the new viewspace take over the settings of the currently 127 # active viewspace 128 self.manager(viewspace).copySettings(self.manager()) 129 130 131class ViewSpaceSideBarManager(plugin.ViewSpacePlugin): 132 """Manages SideBar settings and behaviour for one ViewSpace.""" 133 def __init__(self, viewspace): 134 self._line_numbers = False 135 self._linenumberarea = None 136 self._folding = False 137 self._foldingarea = None 138 viewspace.viewChanged.connect(self.updateView) 139 140 def loadSettings(self): 141 """Loads the settings from config.""" 142 s = QSettings() 143 s.beginGroup("sidebar") 144 line_numbers = s.value("line_numbers", self._line_numbers, bool) 145 self.setLineNumbersVisible(line_numbers) 146 folding = s.value("folding", self._folding, bool) 147 self.setFoldingVisible(folding) 148 149 def saveSettings(self): 150 """Saves the settings to config.""" 151 s = QSettings() 152 s.beginGroup("sidebar") 153 s.setValue("line_numbers", self.lineNumbersVisible()) 154 s.setValue("folding",self.foldingVisible()) 155 156 def copySettings(self, other): 157 """Takes over the settings from another viewspace's manager.""" 158 self.setLineNumbersVisible(other.lineNumbersVisible()) 159 self.setFoldingVisible(other.foldingVisible()) 160 161 def setLineNumbersVisible(self, visible): 162 """Set whether line numbers are to be shown.""" 163 if visible == self._line_numbers: 164 return 165 self._line_numbers = visible 166 self.updateView() 167 168 def lineNumbersVisible(self): 169 """Returns whether line numbers are shown.""" 170 return self._line_numbers 171 172 def setFoldingVisible(self, visible): 173 """Set whether folding indicators are to be shown.""" 174 if visible == self._folding: 175 return 176 self._folding = visible 177 self.updateView() 178 179 def foldingVisible(self): 180 """Return whether folding indicators are to be shown.""" 181 return self._folding 182 183 def updateView(self): 184 """Adjust the sidebar in the current View in the sidebar.""" 185 view = self.viewSpace().activeView() 186 if not view: 187 return 188 189 def add(widget): 190 """Adds a widget to the side of the view.""" 191 from gadgets import borderlayout 192 if QApplication.isRightToLeft(): 193 side = borderlayout.RIGHT 194 else: 195 side = borderlayout.LEFT 196 borderlayout.BorderLayout.get(view).addWidget(widget, side) 197 198 # add or remove the folding widget 199 if self._folding: 200 if not self._foldingarea: 201 import folding 202 self._foldingarea = folding.FoldingArea() 203 self._foldingarea.setPalette(QApplication.palette()) 204 add(self._foldingarea) 205 self._foldingarea.setTextEdit(view) 206 elif self._foldingarea: 207 self._foldingarea.deleteLater() 208 self._foldingarea = None 209 210 # add of remove the linenumbers widget 211 if self._line_numbers: 212 if not self._linenumberarea: 213 from widgets import linenumberarea 214 self._linenumberarea = linenumberarea.LineNumberArea() 215 add(self._linenumberarea) 216 self._linenumberarea.setTextEdit(view) 217 elif self._linenumberarea: 218 self._linenumberarea.deleteLater() 219 self._linenumberarea = None 220 221 # display horizontal lines where text is collapsed 222 if self._folding: 223 import folding 224 view.viewport().installEventFilter(folding.line_painter) 225 else: 226 # don't import the folding module if it was not loaded anyway 227 folding = sys.modules.get('folding') 228 if folding: 229 view.viewport().removeEventFilter(folding.line_painter) 230 231 232class Actions(actioncollection.ActionCollection): 233 name = "sidebar" 234 def createActions(self, parent): 235 self.view_linenumbers = QAction(parent, checkable=True) 236 self.folding_enable = QAction(parent, checkable=True) 237 self.folding_fold_current = QAction(parent) 238 self.folding_fold_top = QAction(parent) 239 self.folding_unfold_current = QAction(parent) 240 self.folding_fold_all = QAction(parent) 241 self.folding_unfold_all = QAction(parent) 242 243 def translateUI(self): 244 self.view_linenumbers.setText(_("&Line Numbers")) 245 self.folding_enable.setText(_("&Enable Folding")) 246 self.folding_fold_current.setText(_("&Fold Current Region")) 247 self.folding_fold_top.setText(_("Fold &Top Region")) 248 self.folding_unfold_current.setText(_("&Unfold Current Region")) 249 self.folding_fold_all.setText(_("Fold &All")) 250 self.folding_unfold_all.setText(_("U&nfold All")) 251 252 253