|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the editor component of the eric4 IDE. |
|
8 """ |
|
9 |
|
10 import os |
|
11 import re |
|
12 import types |
|
13 |
|
14 from PyQt4.Qsci import QsciScintilla, QsciMacro |
|
15 from PyQt4.QtCore import * |
|
16 from PyQt4.QtGui import * |
|
17 |
|
18 from E4Gui.E4Application import e4App |
|
19 |
|
20 import Exporters |
|
21 import Lexers |
|
22 import TypingCompleters |
|
23 from QsciScintillaCompat import QsciScintillaCompat, QSCINTILLA_VERSION |
|
24 from SpellChecker import SpellChecker |
|
25 from SpellCheckingDialog import SpellCheckingDialog |
|
26 |
|
27 from Debugger.EditBreakpointDialog import EditBreakpointDialog |
|
28 |
|
29 from DebugClients.Python.coverage import coverage |
|
30 |
|
31 from DataViews.CodeMetricsDialog import CodeMetricsDialog |
|
32 from DataViews.PyCoverageDialog import PyCoverageDialog |
|
33 from DataViews.PyProfileDialog import PyProfileDialog |
|
34 |
|
35 from Printer import Printer |
|
36 |
|
37 import Preferences |
|
38 import Utilities |
|
39 |
|
40 import UI.PixmapCache |
|
41 |
|
42 EditorAutoCompletionListID = 1 |
|
43 TemplateCompletionListID = 2 |
|
44 |
|
45 class Editor(QsciScintillaCompat): |
|
46 """ |
|
47 Class implementing the editor component of the eric4 IDE. |
|
48 |
|
49 @signal modificationStatusChanged(boolean, editor) emitted when the |
|
50 modification status has changed |
|
51 @signal undoAvailable(boolean) emitted to signal the undo availability |
|
52 @signal redoAvailable(boolean) emitted to signal the redo availability |
|
53 @signal cursorChanged(string, int, int) emitted when the cursor position |
|
54 was changed |
|
55 @signal editorAboutToBeSaved(string) emitted before the editor is saved |
|
56 @signal editorSaved(string) emitted after the editor has been saved |
|
57 @signal editorRenamed(string) emitted after the editor got a new name |
|
58 (i.e. after a 'Save As') |
|
59 @signal captionChanged(string, editor) emitted when the caption is |
|
60 updated. Typically due to a readOnly attribute change. |
|
61 @signal breakpointToggled(editor) emitted when a breakpoint is toggled |
|
62 @signal bookmarkToggled(editor) emitted when a bookmark is toggled |
|
63 @signal syntaxerrorToggled(editor) emitted when a syntax error was discovered |
|
64 @signal autoCompletionAPIsAvailable(avail) emitted after the autocompletion |
|
65 function has been configured |
|
66 @signal coverageMarkersShown(boolean) emitted after the coverage markers have been |
|
67 shown or cleared |
|
68 @signal taskMarkersUpdated(editor) emitted when the task markers were updated |
|
69 @signal showMenu(string, QMenu, editor) emitted when a menu is about to be shown. |
|
70 The name of the menu, a reference to the menu and a reference to the |
|
71 editor are given. |
|
72 @signal languageChanged(language) emitted when the editors language was set. The |
|
73 language is passed as a parameter. |
|
74 @signal eolChanged(eol) emitted when the editors eol type was set. The eol string |
|
75 is passed as a parameter. |
|
76 @signal encodingChanged(encoding) emitted when the editors encoding was set. The |
|
77 encoding name is passed as a parameter. |
|
78 """ |
|
79 ClassID = 1 |
|
80 ClassProtectedID = 2 |
|
81 ClassPrivateID = 3 |
|
82 MethodID = 4 |
|
83 MethodProtectedID = 5 |
|
84 MethodPrivateID = 6 |
|
85 AttributeID = 7 |
|
86 AttributeProtectedID = 8 |
|
87 AttributePrivateID = 9 |
|
88 EnumID = 10 |
|
89 |
|
90 FromDocumentID = 99 |
|
91 |
|
92 TemplateImageID = 100 |
|
93 |
|
94 def __init__(self, dbs, fn = None, vm = None, |
|
95 filetype = "", editor = None, tv = None): |
|
96 """ |
|
97 Constructor |
|
98 |
|
99 @param dbs reference to the debug server object |
|
100 @param fn name of the file to be opened (string). If it is None, |
|
101 a new (empty) editor is opened |
|
102 @param vm reference to the view manager object (ViewManager.ViewManager) |
|
103 @param filetype type of the source file (string) |
|
104 @param editor reference to an Editor object, if this is a cloned view |
|
105 @param tv reference to the task viewer object |
|
106 """ |
|
107 QsciScintillaCompat.__init__(self) |
|
108 self.setAttribute(Qt.WA_DeleteOnClose) |
|
109 self.setAttribute(Qt.WA_KeyCompression) |
|
110 self.setUtf8(True) |
|
111 |
|
112 self.pyExtensions = dbs.getExtensions('Python') |
|
113 self.py3Extensions = dbs.getExtensions('Python3') |
|
114 self.rbExtensions = dbs.getExtensions('Ruby') |
|
115 |
|
116 self.dbs = dbs |
|
117 self.taskViewer = tv |
|
118 self.fileName = fn |
|
119 self.vm = vm |
|
120 self.filetype = filetype |
|
121 self.noName = "" |
|
122 |
|
123 # clear some variables |
|
124 self.lastHighlight = None # remember the last highlighted line |
|
125 self.lastErrorMarker = None # remember the last error line |
|
126 self.lastCurrMarker = None # remember the last current line |
|
127 |
|
128 self.breaks = {} # key: marker handle, |
|
129 # value: (lineno, condition, temporary, |
|
130 # enabled, ignorecount) |
|
131 self.bookmarks = [] # bookmarks are just a list of handles to the |
|
132 # bookmark markers |
|
133 self.syntaxerrors = {} # key: marker handle |
|
134 # value: error message |
|
135 self.notcoveredMarkers = [] # just a list of marker handles |
|
136 |
|
137 self.condHistory = [] |
|
138 self.lexer_ = None |
|
139 self.__lexerReset = False |
|
140 self.completer = None |
|
141 self.encoding = Preferences.getEditor("DefaultEncoding") |
|
142 self.apiLanguage = '' |
|
143 self.lastModified = 0 |
|
144 self.line = -1 |
|
145 self.inReopenPrompt = False |
|
146 # true if the prompt to reload a changed source is present |
|
147 self.inFileRenamed = False # true if we are propagating a rename action |
|
148 self.inLanguageChanged = False # true if we are propagating a language change |
|
149 self.inEolChanged = False # true if we are propagating an eol change |
|
150 self.inEncodingChanged = False # true if we are propagating an encoding change |
|
151 self.inDragDrop = False # true if we are in drop mode |
|
152 self.__hasTaskMarkers = False # no task markers present |
|
153 |
|
154 self.macros = {} # list of defined macros |
|
155 self.curMacro = None |
|
156 self.recording = False |
|
157 |
|
158 self.acAPI = False |
|
159 |
|
160 # list of clones |
|
161 self.__clones = [] |
|
162 |
|
163 # clear QScintilla defined keyboard commands |
|
164 # we do our own handling through the view manager |
|
165 self.clearAlternateKeys() |
|
166 self.clearKeys() |
|
167 |
|
168 # initialise the mark occurrences timer |
|
169 self.__markOccurrencesTimer = QTimer(self) |
|
170 self.__markOccurrencesTimer.setSingleShot(True) |
|
171 self.__markOccurrencesTimer.setInterval( |
|
172 Preferences.getEditor("MarkOccurrencesTimeout")) |
|
173 self.connect(self.__markOccurrencesTimer, SIGNAL("timeout()"), |
|
174 self.__markOccurrences) |
|
175 self.__markedText = "" |
|
176 |
|
177 # initialise some spellchecking stuff |
|
178 self.spell = None |
|
179 self.lastLine = 0 |
|
180 self.lastIndex = 0 |
|
181 |
|
182 self.connect(self, SIGNAL('modificationChanged(bool)'), |
|
183 self.__modificationChanged) |
|
184 self.connect(self, SIGNAL('cursorPositionChanged(int,int)'), |
|
185 self.__cursorPositionChanged) |
|
186 self.connect(self, SIGNAL('modificationAttempted()'), |
|
187 self.__modificationReadOnly) |
|
188 self.connect(self, SIGNAL('userListActivated(int, const QString)'), |
|
189 self.__completionListSelected) |
|
190 |
|
191 # margins layout |
|
192 if QSCINTILLA_VERSION() >= 0x020301: |
|
193 self.__unifiedMargins = Preferences.getEditor("UnifiedMargins") |
|
194 else: |
|
195 self.__unifiedMargins = True |
|
196 |
|
197 # define the margins markers |
|
198 self.breakpoint = \ |
|
199 self.markerDefine(UI.PixmapCache.getPixmap("break.png")) |
|
200 self.cbreakpoint = \ |
|
201 self.markerDefine(UI.PixmapCache.getPixmap("cBreak.png")) |
|
202 self.tbreakpoint = \ |
|
203 self.markerDefine(UI.PixmapCache.getPixmap("tBreak.png")) |
|
204 self.tcbreakpoint = \ |
|
205 self.markerDefine(UI.PixmapCache.getPixmap("tCBreak.png")) |
|
206 self.dbreakpoint = \ |
|
207 self.markerDefine(UI.PixmapCache.getPixmap("breakDisabled.png")) |
|
208 self.bookmark = \ |
|
209 self.markerDefine(UI.PixmapCache.getPixmap("bookmark.png")) |
|
210 self.syntaxerror = \ |
|
211 self.markerDefine(UI.PixmapCache.getPixmap("syntaxError.png")) |
|
212 self.notcovered = \ |
|
213 self.markerDefine(UI.PixmapCache.getPixmap("notcovered.png")) |
|
214 self.taskmarker = \ |
|
215 self.markerDefine(UI.PixmapCache.getPixmap("task.png")) |
|
216 |
|
217 # define the line markers |
|
218 self.currentline = self.markerDefine(QsciScintilla.Background) |
|
219 self.errorline = self.markerDefine(QsciScintilla.Background) |
|
220 self.__setLineMarkerColours() |
|
221 |
|
222 self.breakpointMask = (1 << self.breakpoint) | \ |
|
223 (1 << self.cbreakpoint) | \ |
|
224 (1 << self.tbreakpoint) | \ |
|
225 (1 << self.tcbreakpoint) | \ |
|
226 (1 << self.dbreakpoint) |
|
227 |
|
228 # configure the margins |
|
229 self.__setMarginsDisplay() |
|
230 |
|
231 self.connect(self, SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'), |
|
232 self.__marginClicked) |
|
233 |
|
234 # set the eol mode |
|
235 self.__setEolMode() |
|
236 |
|
237 self.isResourcesFile = False |
|
238 if editor is None: |
|
239 if self.fileName is not None: |
|
240 if (QFileInfo(self.fileName).size() / 1024) > \ |
|
241 Preferences.getEditor("WarnFilesize"): |
|
242 res = QMessageBox.warning(None, |
|
243 self.trUtf8("Open File"), |
|
244 self.trUtf8("""<p>The size of the file <b>{0}</b>""" |
|
245 """ is <b>{1} KB</b>.""" |
|
246 """ Do you really want to load it?</p>""")\ |
|
247 .format(self.fileName, |
|
248 QFileInfo(self.fileName).size() / 1024), |
|
249 QMessageBox.StandardButtons(\ |
|
250 QMessageBox.No | \ |
|
251 QMessageBox.Yes), |
|
252 QMessageBox.No) |
|
253 if res == QMessageBox.No or res == QMessageBox.Cancel: |
|
254 raise IOError() |
|
255 line0 = self.readLine0(self.fileName) |
|
256 bindName = self.__bindName(line0) |
|
257 self.__bindLexer(bindName) |
|
258 self.readFile(self.fileName, True) |
|
259 self.__bindLexer(bindName) |
|
260 self.__bindCompleter(bindName) |
|
261 self.__autoSyntaxCheck() |
|
262 self.isResourcesFile = self.fileName.endswith(".qrc") |
|
263 |
|
264 self.recolor() |
|
265 else: |
|
266 # clone the given editor |
|
267 self.setDocument(editor.document()) |
|
268 self.breaks = editor.breaks |
|
269 self.bookmarks = editor.bookmarks |
|
270 self.syntaxerrors = editor.syntaxerrors |
|
271 self.notcoveredMarkers = editor.notcoveredMarkers |
|
272 self.isResourcesFile = editor.isResourcesFile |
|
273 self.lastModified = editor.lastModified |
|
274 |
|
275 self.addClone(editor) |
|
276 editor.addClone(self) |
|
277 |
|
278 self.gotoLine(0) |
|
279 |
|
280 # set the text display |
|
281 self.__setTextDisplay() |
|
282 |
|
283 # set the autocompletion and calltips function |
|
284 self.__acHookFunction = None |
|
285 self.__setAutoCompletion() |
|
286 self.__ctHookFunction = None |
|
287 self.__setCallTips() |
|
288 |
|
289 sh = self.sizeHint() |
|
290 if sh.height() < 300: |
|
291 sh.setHeight(300) |
|
292 self.resize(sh) |
|
293 |
|
294 # Make sure tabbing through a QWorkspace works. |
|
295 self.setFocusPolicy(Qt.StrongFocus) |
|
296 |
|
297 self.__updateReadOnly(True) |
|
298 |
|
299 self.setWhatsThis(self.trUtf8( |
|
300 """<b>A Source Editor Window</b>""" |
|
301 """<p>This window is used to display and edit a source file.""" |
|
302 """ You can open as many of these as you like. The name of the file""" |
|
303 """ is displayed in the window's titlebar.</p>""" |
|
304 """<p>In order to set breakpoints just click in the space between""" |
|
305 """ the line numbers and the fold markers. Via the context menu""" |
|
306 """ of the margins they may be edited.</p>""" |
|
307 """<p>In order to set bookmarks just Shift click in the space between""" |
|
308 """ the line numbers and the fold markers.</p>""" |
|
309 """<p>These actions can be reversed via the context menu.</p>""" |
|
310 """<p>Ctrl clicking on a syntax error marker shows some info""" |
|
311 """ about this error.</p>""" |
|
312 )) |
|
313 |
|
314 # Set the editors size, if it is too big for the view manager. |
|
315 if self.vm is not None: |
|
316 req = self.size() |
|
317 bnd = req.boundedTo(self.vm.size()) |
|
318 |
|
319 if bnd.width() < req.width() or bnd.height() < req.height(): |
|
320 self.resize(bnd) |
|
321 |
|
322 # set the autosave flag |
|
323 self.autosaveEnabled = Preferences.getEditor("AutosaveInterval") > 0 |
|
324 self.autosaveManuallyDisabled = False |
|
325 |
|
326 self.__initContextMenu() |
|
327 self.__initContextMenuMargins() |
|
328 |
|
329 self.__checkEol() |
|
330 if editor is None: |
|
331 self.__checkLanguage() |
|
332 self.__checkEncoding() |
|
333 else: |
|
334 # it's a clone |
|
335 self.languageChanged(editor.apiLanguage, propagate = False) |
|
336 self.__encodingChanged(editor.encoding, propagate = False) |
|
337 |
|
338 self.coverageMarkersShown = False # flag remembering the current status of the |
|
339 # code coverage markers |
|
340 |
|
341 self.setAcceptDrops(True) |
|
342 |
|
343 # breakpoint handling |
|
344 self.breakpointModel = self.dbs.getBreakPointModel() |
|
345 self.__restoreBreakpoints() |
|
346 self.connect(self.breakpointModel, |
|
347 SIGNAL("rowsAboutToBeRemoved(const QModelIndex &, int, int)"), |
|
348 self.__deleteBreakPoints) |
|
349 self.connect(self.breakpointModel, |
|
350 SIGNAL("dataAboutToBeChanged(const QModelIndex &, const QModelIndex &)"), |
|
351 self.__breakPointDataAboutToBeChanged) |
|
352 self.connect(self.breakpointModel, |
|
353 SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), |
|
354 self.__changeBreakPoints) |
|
355 self.connect(self.breakpointModel, |
|
356 SIGNAL("rowsInserted(const QModelIndex &, int, int)"), |
|
357 self.__addBreakPoints) |
|
358 self.connect(self, SIGNAL("linesChanged()"), self.__linesChanged) |
|
359 |
|
360 # establish connection to some ViewManager action groups |
|
361 self.addActions(self.vm.editorActGrp.actions()) |
|
362 self.addActions(self.vm.editActGrp.actions()) |
|
363 self.addActions(self.vm.copyActGrp.actions()) |
|
364 self.addActions(self.vm.viewActGrp.actions()) |
|
365 |
|
366 # register images to be shown in autocompletion lists |
|
367 self.__registerImages() |
|
368 |
|
369 def __registerImages(self): |
|
370 """ |
|
371 Private method to register images for autocompletion lists. |
|
372 """ |
|
373 self.registerImage(self.ClassID, |
|
374 UI.PixmapCache.getPixmap("class.png")) |
|
375 self.registerImage(self.ClassProtectedID, |
|
376 UI.PixmapCache.getPixmap("class_protected.png")) |
|
377 self.registerImage(self.ClassPrivateID, |
|
378 UI.PixmapCache.getPixmap("class_private.png")) |
|
379 self.registerImage(self.MethodID, |
|
380 UI.PixmapCache.getPixmap("method.png")) |
|
381 self.registerImage(self.MethodProtectedID, |
|
382 UI.PixmapCache.getPixmap("method_protected.png")) |
|
383 self.registerImage(self.MethodPrivateID, |
|
384 UI.PixmapCache.getPixmap("method_private.png")) |
|
385 self.registerImage(self.AttributeID, |
|
386 UI.PixmapCache.getPixmap("attribute.png")) |
|
387 self.registerImage(self.AttributeProtectedID, |
|
388 UI.PixmapCache.getPixmap("attribute_protected.png")) |
|
389 self.registerImage(self.AttributePrivateID, |
|
390 UI.PixmapCache.getPixmap("attribute_private.png")) |
|
391 self.registerImage(self.EnumID, |
|
392 UI.PixmapCache.getPixmap("enum.png")) |
|
393 |
|
394 self.registerImage(self.FromDocumentID, |
|
395 UI.PixmapCache.getPixmap("editor.png")) |
|
396 |
|
397 self.registerImage(self.TemplateImageID, |
|
398 UI.PixmapCache.getPixmap("templateViewer.png")) |
|
399 |
|
400 def addClone(self, editor): |
|
401 """ |
|
402 Public method to add a clone to our list. |
|
403 |
|
404 @param clone reference to the cloned editor (Editor) |
|
405 """ |
|
406 self.__clones.append(editor) |
|
407 |
|
408 self.connect(editor, SIGNAL('editorRenamed'), self.fileRenamed) |
|
409 self.connect(editor, SIGNAL('languageChanged'), self.languageChanged) |
|
410 self.connect(editor, SIGNAL('eolChanged'), self.__eolChanged) |
|
411 self.connect(editor, SIGNAL('encodingChanged'), self.__encodingChanged) |
|
412 |
|
413 def removeClone(self, editor): |
|
414 """ |
|
415 Public method to remove a clone from our list. |
|
416 |
|
417 @param clone reference to the cloned editor (Editor) |
|
418 """ |
|
419 if editor in self.__clones: |
|
420 self.disconnect(editor, SIGNAL('editorRenamed'), self.fileRenamed) |
|
421 self.disconnect(editor, SIGNAL('languageChanged'), self.languageChanged) |
|
422 self.disconnect(editor, SIGNAL('eolChanged'), self.__eolChanged) |
|
423 self.disconnect(editor, SIGNAL('encodingChanged'), self.__encodingChanged) |
|
424 self.__clones.remove(editor) |
|
425 |
|
426 def __bindName(self, line0): |
|
427 """ |
|
428 Private method to generate a dummy filename for binding a lexer. |
|
429 |
|
430 @param line0 first line of text to use in the generation process (string) |
|
431 """ |
|
432 bindName = self.fileName |
|
433 |
|
434 if line0.startswith("<?xml"): |
|
435 # override extension for XML files |
|
436 bindName = "dummy.xml" |
|
437 |
|
438 # check filetype |
|
439 if self.filetype == "Python": |
|
440 bindName = "dummy.py" |
|
441 elif self.filetype == "Ruby": |
|
442 bindName = "dummy.rb" |
|
443 elif self.filetype == "D": |
|
444 bindName = "dummy.d" |
|
445 elif self.filetype == "Properties": |
|
446 bindName = "dummy.ini" |
|
447 |
|
448 # #! marker detection |
|
449 if line0.startswith("#!"): |
|
450 if "python3" in line0: |
|
451 bindName = "dummy.py" |
|
452 self.filetype = "Python3" |
|
453 elif "python2" in line0: |
|
454 bindName = "dummy.py" |
|
455 self.filetype = "Python" |
|
456 elif "python" in line0: |
|
457 bindName = "dummy.py" |
|
458 self.filetype = "Python" |
|
459 elif ("/bash" in line0 or "/sh" in line0): |
|
460 bindName = "dummy.sh" |
|
461 elif "ruby" in line0: |
|
462 bindName = "dummy.rb" |
|
463 self.filetype = "Ruby" |
|
464 elif "perl" in line0: |
|
465 bindName = "dummy.pl" |
|
466 elif "lua" in line0: |
|
467 bindName = "dummy.lua" |
|
468 elif "dmd" in line0: |
|
469 bindName = "dummy.d" |
|
470 self.filetype = "D" |
|
471 return bindName |
|
472 |
|
473 def getMenu(self, menuName): |
|
474 """ |
|
475 Public method to get a reference to the main context menu or a submenu. |
|
476 |
|
477 @param menuName name of the menu (string) |
|
478 @return reference to the requested menu (QMenu) or None |
|
479 """ |
|
480 try: |
|
481 return self.__menus[menuName] |
|
482 except KeyError: |
|
483 return None |
|
484 |
|
485 def hasMiniMenu(self): |
|
486 """ |
|
487 Public method to check the miniMenu flag. |
|
488 |
|
489 @return flag indicating a minimized context menu (boolean) |
|
490 """ |
|
491 return self.miniMenu |
|
492 |
|
493 def __initContextMenu(self): |
|
494 """ |
|
495 Private method used to setup the context menu |
|
496 """ |
|
497 self.miniMenu = Preferences.getEditor("MiniContextMenu") |
|
498 |
|
499 self.menuActs = {} |
|
500 self.menu = QMenu() |
|
501 self.__menus = { |
|
502 "Main" : self.menu, |
|
503 } |
|
504 |
|
505 self.languagesMenu = self.__initContextMenuLanguages() |
|
506 self.__menus["Languages"] = self.languagesMenu |
|
507 if self.isResourcesFile: |
|
508 self.resourcesMenu = self.__initContextMenuResources() |
|
509 self.__menus["Resources"] = self.resourcesMenu |
|
510 else: |
|
511 self.checksMenu = self.__initContextMenuChecks() |
|
512 self.showMenu = self.__initContextMenuShow() |
|
513 self.graphicsMenu = self.__initContextMenuGraphics() |
|
514 self.autocompletionMenu = self.__initContextMenuAutocompletion() |
|
515 self.__menus["Checks"] = self.checksMenu |
|
516 self.__menus["Show"] = self.showMenu |
|
517 self.__menus["Graphics"] = self.graphicsMenu |
|
518 self.__menus["Autocompletion"] = self.autocompletionMenu |
|
519 self.exportersMenu = self.__initContextMenuExporters() |
|
520 self.__menus["Exporters"] = self.exportersMenu |
|
521 self.eolMenu = self.__initContextMenuEol() |
|
522 self.__menus["Eol"] = self.eolMenu |
|
523 self.encodingsMenu = self.__initContextMenuEncodings() |
|
524 self.__menus["Encodings"] = self.encodingsMenu |
|
525 |
|
526 self.menuActs["Undo"] = \ |
|
527 self.menu.addAction(UI.PixmapCache.getIcon("editUndo.png"), |
|
528 self.trUtf8('Undo'), self.undo) |
|
529 self.menuActs["Redo"] = \ |
|
530 self.menu.addAction(UI.PixmapCache.getIcon("editRedo.png"), |
|
531 self.trUtf8('Redo'), self.redo) |
|
532 self.menuActs["Revert"] = \ |
|
533 self.menu.addAction(self.trUtf8("Revert to last saved state"), |
|
534 self.revertToUnmodified) |
|
535 self.menu.addSeparator() |
|
536 self.menuActs["Cut"] = \ |
|
537 self.menu.addAction(UI.PixmapCache.getIcon("editCut.png"), |
|
538 self.trUtf8('Cut'), self.cut) |
|
539 self.menuActs["Copy"] = \ |
|
540 self.menu.addAction(UI.PixmapCache.getIcon("editCopy.png"), |
|
541 self.trUtf8('Copy'), self.copy) |
|
542 self.menu.addAction(UI.PixmapCache.getIcon("editPaste.png"), |
|
543 self.trUtf8('Paste'), self.paste) |
|
544 if not self.miniMenu: |
|
545 self.menu.addSeparator() |
|
546 self.menu.addAction(UI.PixmapCache.getIcon("editIndent.png"), |
|
547 self.trUtf8('Indent'), self.indentLineOrSelection) |
|
548 self.menu.addAction(UI.PixmapCache.getIcon("editUnindent.png"), |
|
549 self.trUtf8('Unindent'), self.unindentLineOrSelection) |
|
550 self.menuActs["Comment"] = \ |
|
551 self.menu.addAction(UI.PixmapCache.getIcon("editComment.png"), |
|
552 self.trUtf8('Comment'), self.commentLineOrSelection) |
|
553 self.menuActs["Uncomment"] = \ |
|
554 self.menu.addAction(UI.PixmapCache.getIcon("editUncomment.png"), |
|
555 self.trUtf8('Uncomment'), self.uncommentLineOrSelection) |
|
556 self.menuActs["StreamComment"] = \ |
|
557 self.menu.addAction(self.trUtf8('Stream Comment'), |
|
558 self.streamCommentLineOrSelection) |
|
559 self.menuActs["BoxComment"] = \ |
|
560 self.menu.addAction(self.trUtf8('Box Comment'), |
|
561 self.boxCommentLineOrSelection) |
|
562 self.menu.addSeparator() |
|
563 self.menu.addAction(self.trUtf8('Select to brace'), |
|
564 self.selectToMatchingBrace) |
|
565 self.menu.addAction(self.trUtf8('Select all'), self.__selectAll) |
|
566 self.menu.addAction(self.trUtf8('Deselect all'), self.__deselectAll) |
|
567 self.menu.addSeparator() |
|
568 self.menuActs["SpellCheck"] = \ |
|
569 self.menu.addAction(UI.PixmapCache.getIcon("spellchecking.png"), |
|
570 self.trUtf8('Check spelling...'), self.checkSpelling) |
|
571 self.menuActs["SpellCheckSelection"] = \ |
|
572 self.menu.addAction(UI.PixmapCache.getIcon("spellchecking.png"), |
|
573 self.trUtf8('Check spelling of selection...'), |
|
574 self.__checkSpellingSelection) |
|
575 self.menuActs["SpellCheckRemove"] = \ |
|
576 self.menu.addAction(self.trUtf8("Remove from dictionary"), |
|
577 self.__removeFromSpellingDictionary) |
|
578 self.menu.addSeparator() |
|
579 self.menu.addAction(self.trUtf8('Shorten empty lines'), |
|
580 self.shortenEmptyLines) |
|
581 self.menu.addSeparator() |
|
582 self.menuActs["Languages"] = self.menu.addMenu(self.languagesMenu) |
|
583 self.menuActs["Encodings"] = self.menu.addMenu(self.encodingsMenu) |
|
584 self.menuActs["Eol"] = self.menu.addMenu(self.eolMenu) |
|
585 self.menu.addSeparator() |
|
586 self.menuActs["MonospacedFont"] = \ |
|
587 self.menu.addAction(self.trUtf8("Use Monospaced Font"), |
|
588 self.handleMonospacedEnable) |
|
589 self.menuActs["MonospacedFont"].setCheckable(True) |
|
590 self.menuActs["MonospacedFont"].setChecked(self.useMonospaced) |
|
591 self.menuActs["AutosaveEnable"] = \ |
|
592 self.menu.addAction(self.trUtf8("Autosave enabled"), |
|
593 self.__autosaveEnable) |
|
594 self.menuActs["AutosaveEnable"].setCheckable(True) |
|
595 self.menuActs["AutosaveEnable"].setChecked(self.autosaveEnabled) |
|
596 self.menuActs["TypingAidsEnabled"] = \ |
|
597 self.menu.addAction(self.trUtf8("Typing aids enabled"), |
|
598 self.__toggleTypingAids) |
|
599 self.menuActs["TypingAidsEnabled"].setCheckable(True) |
|
600 self.menuActs["TypingAidsEnabled"].setEnabled(self.completer is not None) |
|
601 self.menuActs["TypingAidsEnabled"].setChecked(\ |
|
602 self.completer is not None and self.completer.isEnabled()) |
|
603 self.menuActs["AutoCompletionEnable"] = \ |
|
604 self.menu.addAction(self.trUtf8("Autocompletion enabled"), |
|
605 self.__toggleAutoCompletionEnable) |
|
606 self.menuActs["AutoCompletionEnable"].setCheckable(True) |
|
607 self.menuActs["AutoCompletionEnable"].setChecked(\ |
|
608 self.autoCompletionThreshold() != -1) |
|
609 if not self.isResourcesFile: |
|
610 self.menu.addMenu(self.autocompletionMenu) |
|
611 self.menu.addSeparator() |
|
612 if self.isResourcesFile: |
|
613 self.menu.addMenu(self.resourcesMenu) |
|
614 else: |
|
615 self.menuActs["Check"] = self.menu.addMenu(self.checksMenu) |
|
616 self.menu.addSeparator() |
|
617 self.menuActs["Show"] = self.menu.addMenu(self.showMenu) |
|
618 self.menu.addSeparator() |
|
619 self.menuActs["Diagrams"] = self.menu.addMenu(self.graphicsMenu) |
|
620 self.menu.addSeparator() |
|
621 self.menu.addAction(self.trUtf8('New view'), self.__newView) |
|
622 act = self.menu.addAction(self.trUtf8('New view (with new split)'), |
|
623 self.__newViewNewSplit) |
|
624 if not self.vm.canSplit(): |
|
625 act.setEnabled(False) |
|
626 self.menu.addAction(UI.PixmapCache.getIcon("close.png"), |
|
627 self.trUtf8('Close'), self.__contextClose) |
|
628 self.menu.addSeparator() |
|
629 self.menuActs["Save"] = \ |
|
630 self.menu.addAction(UI.PixmapCache.getIcon("fileSave.png"), |
|
631 self.trUtf8('Save'), self.__contextSave) |
|
632 self.menu.addAction(UI.PixmapCache.getIcon("fileSaveAs.png"), |
|
633 self.trUtf8('Save As...'), self.__contextSaveAs) |
|
634 if not self.miniMenu: |
|
635 self.menu.addMenu(self.exportersMenu) |
|
636 self.menu.addSeparator() |
|
637 self.menu.addAction(UI.PixmapCache.getIcon("printPreview.png"), |
|
638 self.trUtf8("Print Preview"), self.printPreviewFile) |
|
639 self.menu.addAction(UI.PixmapCache.getIcon("print.png"), |
|
640 self.trUtf8('Print'), self.printFile) |
|
641 |
|
642 self.connect(self.menu, SIGNAL('aboutToShow()'), self.__showContextMenu) |
|
643 |
|
644 self.spellingMenu = QMenu() |
|
645 self.__menus["Spelling"] = self.spellingMenu |
|
646 |
|
647 self.connect(self.spellingMenu, SIGNAL('aboutToShow()'), |
|
648 self.__showContextMenuSpelling) |
|
649 self.connect(self.spellingMenu, SIGNAL('triggered(QAction *)'), |
|
650 self.__contextMenuSpellingTriggered) |
|
651 |
|
652 def __initContextMenuAutocompletion(self): |
|
653 """ |
|
654 Private method used to setup the Checks context sub menu. |
|
655 """ |
|
656 menu = QMenu(self.trUtf8('Autocomplete')) |
|
657 |
|
658 self.menuActs["acDynamic"] = \ |
|
659 menu.addAction(self.trUtf8('dynamic'), |
|
660 self.autoComplete) |
|
661 menu.addSeparator() |
|
662 menu.addAction(self.trUtf8('from Document'), |
|
663 self.autoCompleteFromDocument) |
|
664 self.menuActs["acAPI"] = \ |
|
665 menu.addAction(self.trUtf8('from APIs'), |
|
666 self.autoCompleteFromAPIs) |
|
667 self.menuActs["acAPIDocument"] = \ |
|
668 menu.addAction(self.trUtf8('from Document and APIs'), |
|
669 self.autoCompleteFromAll) |
|
670 menu.addSeparator() |
|
671 self.menuActs["calltip"] = \ |
|
672 menu.addAction(self.trUtf8('Calltip'), self.callTip) |
|
673 |
|
674 self.connect(menu, SIGNAL('aboutToShow()'), self.__showContextMenuAutocompletion) |
|
675 |
|
676 return menu |
|
677 |
|
678 def __initContextMenuChecks(self): |
|
679 """ |
|
680 Private method used to setup the Checks context sub menu. |
|
681 """ |
|
682 menu = QMenu(self.trUtf8('Check')) |
|
683 self.connect(menu, SIGNAL("aboutToShow()"), self.__showContextMenuChecks) |
|
684 return menu |
|
685 |
|
686 def __initContextMenuShow(self): |
|
687 """ |
|
688 Private method used to setup the Show context sub menu. |
|
689 """ |
|
690 menu = QMenu(self.trUtf8('Show')) |
|
691 |
|
692 menu.addAction(self.trUtf8('Code metrics...'), self.__showCodeMetrics) |
|
693 self.coverageMenuAct = \ |
|
694 menu.addAction(self.trUtf8('Code coverage...'), self.__showCodeCoverage) |
|
695 self.coverageShowAnnotationMenuAct = \ |
|
696 menu.addAction(self.trUtf8('Show code coverage annotations'), |
|
697 self.__codeCoverageShowAnnotations) |
|
698 self.coverageHideAnnotationMenuAct = \ |
|
699 menu.addAction(self.trUtf8('Hide code coverage annotations'), |
|
700 self.__codeCoverageHideAnnotations) |
|
701 self.profileMenuAct = \ |
|
702 menu.addAction(self.trUtf8('Profile data...'), self.__showProfileData) |
|
703 |
|
704 self.connect(menu, SIGNAL('aboutToShow()'), self.__showContextMenuShow) |
|
705 |
|
706 return menu |
|
707 |
|
708 def __initContextMenuGraphics(self): |
|
709 """ |
|
710 Private method used to setup the diagrams context sub menu. |
|
711 """ |
|
712 menu = QMenu(self.trUtf8('Diagrams')) |
|
713 |
|
714 menu.addAction(self.trUtf8('Class Diagram...'), |
|
715 self.__showClassDiagram) |
|
716 menu.addAction(self.trUtf8('Package Diagram...'), |
|
717 self.__showPackageDiagram) |
|
718 menu.addAction(self.trUtf8('Imports Diagram...'), |
|
719 self.__showImportsDiagram) |
|
720 self.applicationDiagramMenuAct = \ |
|
721 menu.addAction(self.trUtf8('Application Diagram...'), |
|
722 self.__showApplicationDiagram) |
|
723 |
|
724 self.connect(menu, SIGNAL('aboutToShow()'), self.__showContextMenuGraphics) |
|
725 |
|
726 return menu |
|
727 |
|
728 def __initContextMenuLanguages(self): |
|
729 """ |
|
730 Private method used to setup the Languages context sub menu. |
|
731 """ |
|
732 menu = QMenu(self.trUtf8("Languages")) |
|
733 |
|
734 self.languagesActGrp = QActionGroup(self) |
|
735 self.noLanguageAct = menu.addAction(self.trUtf8("No Language")) |
|
736 self.noLanguageAct.setCheckable(True) |
|
737 self.noLanguageAct.setData(QVariant("None")) |
|
738 self.languagesActGrp.addAction(self.noLanguageAct) |
|
739 menu.addSeparator() |
|
740 |
|
741 self.supportedLanguages = {} |
|
742 supportedLanguages = Lexers.getSupportedLanguages() |
|
743 languages = supportedLanguages.keys() |
|
744 languages.sort() |
|
745 for language in languages: |
|
746 if language != "Guessed": |
|
747 self.supportedLanguages[language] = supportedLanguages[language][:] |
|
748 act = menu.addAction(self.supportedLanguages[language][0]) |
|
749 act.setCheckable(True) |
|
750 act.setData(QVariant(language)) |
|
751 self.supportedLanguages[language].append(act) |
|
752 self.languagesActGrp.addAction(act) |
|
753 |
|
754 menu.addSeparator() |
|
755 self.pygmentsAct = menu.addAction(self.trUtf8("Guessed")) |
|
756 self.pygmentsAct.setCheckable(True) |
|
757 self.pygmentsAct.setData(QVariant("Guessed")) |
|
758 self.languagesActGrp.addAction(self.pygmentsAct) |
|
759 self.pygmentsSelAct = menu.addAction(self.trUtf8("Alternatives")) |
|
760 self.pygmentsSelAct.setData(QVariant("Alternatives")) |
|
761 |
|
762 self.connect(menu, SIGNAL('triggered(QAction *)'), self.__languageMenuTriggered) |
|
763 self.connect(menu, SIGNAL('aboutToShow()'), self.__showContextMenuLanguages) |
|
764 |
|
765 return menu |
|
766 |
|
767 def __initContextMenuEncodings(self): |
|
768 """ |
|
769 Private method used to setup the Encodings context sub menu. |
|
770 """ |
|
771 self.supportedEncodings = {} |
|
772 |
|
773 menu = QMenu(self.trUtf8("Encodings")) |
|
774 |
|
775 self.encodingsActGrp = QActionGroup(self) |
|
776 |
|
777 for encoding in sorted(Utilities.supportedCodecs): |
|
778 act = menu.addAction(encoding) |
|
779 act.setCheckable(True) |
|
780 act.setData(QVariant(encoding)) |
|
781 self.supportedEncodings[encoding] = act |
|
782 self.encodingsActGrp.addAction(act) |
|
783 |
|
784 self.connect(menu, SIGNAL('triggered(QAction *)'), self.__encodingsMenuTriggered) |
|
785 self.connect(menu, SIGNAL('aboutToShow()'), self.__showContextMenuEncodings) |
|
786 |
|
787 return menu |
|
788 |
|
789 def __initContextMenuEol(self): |
|
790 """ |
|
791 Private method to setup the eol context sub menu. |
|
792 """ |
|
793 self.supportedEols = {} |
|
794 |
|
795 menu = QMenu(self.trUtf8("End-of-Line Type")) |
|
796 |
|
797 self.eolActGrp = QActionGroup(self) |
|
798 |
|
799 act = menu.addAction(self.trUtf8("Unix")) |
|
800 act.setCheckable(True) |
|
801 act.setData(QVariant('\n')) |
|
802 self.supportedEols['\n'] = act |
|
803 self.eolActGrp.addAction(act) |
|
804 |
|
805 act = menu.addAction(self.trUtf8("Windows")) |
|
806 act.setCheckable(True) |
|
807 act.setData(QVariant('\r\n')) |
|
808 self.supportedEols['\r\n'] = act |
|
809 self.eolActGrp.addAction(act) |
|
810 |
|
811 act = menu.addAction(self.trUtf8("Macintosh")) |
|
812 act.setCheckable(True) |
|
813 act.setData(QVariant('\r')) |
|
814 self.supportedEols['\r'] = act |
|
815 self.eolActGrp.addAction(act) |
|
816 |
|
817 self.connect(menu, SIGNAL('triggered(QAction *)'), self.__eolMenuTriggered) |
|
818 self.connect(menu, SIGNAL('aboutToShow()'), self.__showContextMenuEol) |
|
819 |
|
820 return menu |
|
821 |
|
822 def __initContextMenuExporters(self): |
|
823 """ |
|
824 Private method used to setup the Exporters context sub menu. |
|
825 """ |
|
826 menu = QMenu(self.trUtf8("Export as")) |
|
827 |
|
828 supportedExporters = Exporters.getSupportedFormats() |
|
829 exporters = supportedExporters.keys() |
|
830 exporters.sort() |
|
831 for exporter in exporters: |
|
832 act = menu.addAction(supportedExporters[exporter]) |
|
833 act.setData(QVariant(exporter)) |
|
834 |
|
835 self.connect(menu, SIGNAL('triggered(QAction *)'), self.__exportMenuTriggered) |
|
836 |
|
837 return menu |
|
838 |
|
839 def __initContextMenuMargins(self): |
|
840 """ |
|
841 Private method used to setup the context menu for the margins |
|
842 """ |
|
843 self.marginMenuActs = {} |
|
844 |
|
845 if self.__unifiedMargins: |
|
846 self.__initContextMenuUnifiedMargins() |
|
847 else: |
|
848 self.__initContextMenuSeparateMargins() |
|
849 |
|
850 def __initContextMenuSeparateMargins(self): |
|
851 """ |
|
852 Private method used to setup the context menu for the separated margins |
|
853 """ |
|
854 # bookmark margin |
|
855 self.bmMarginMenu = QMenu() |
|
856 |
|
857 self.bmMarginMenu.addAction(self.trUtf8('Toggle bookmark'), |
|
858 self.menuToggleBookmark) |
|
859 self.marginMenuActs["NextBookmark"] = \ |
|
860 self.bmMarginMenu.addAction(self.trUtf8('Next bookmark'), |
|
861 self.nextBookmark) |
|
862 self.marginMenuActs["PreviousBookmark"] = \ |
|
863 self.bmMarginMenu.addAction(self.trUtf8('Previous bookmark'), |
|
864 self.previousBookmark) |
|
865 self.marginMenuActs["ClearBookmark"] = \ |
|
866 self.bmMarginMenu.addAction(self.trUtf8('Clear all bookmarks'), |
|
867 self.clearBookmarks) |
|
868 |
|
869 self.connect(self.bmMarginMenu, SIGNAL('aboutToShow()'), |
|
870 self.__showContextMenuMargin) |
|
871 |
|
872 # breakpoint margin |
|
873 self.bpMarginMenu = QMenu() |
|
874 |
|
875 self.marginMenuActs["Breakpoint"] = \ |
|
876 self.bpMarginMenu.addAction(self.trUtf8('Toggle breakpoint'), |
|
877 self.menuToggleBreakpoint) |
|
878 self.marginMenuActs["TempBreakpoint"] = \ |
|
879 self.bpMarginMenu.addAction(self.trUtf8('Toggle temporary breakpoint'), |
|
880 self.__menuToggleTemporaryBreakpoint) |
|
881 self.marginMenuActs["EditBreakpoint"] = \ |
|
882 self.bpMarginMenu.addAction(self.trUtf8('Edit breakpoint...'), |
|
883 self.menuEditBreakpoint) |
|
884 self.marginMenuActs["EnableBreakpoint"] = \ |
|
885 self.bpMarginMenu.addAction(self.trUtf8('Enable breakpoint'), |
|
886 self.__menuToggleBreakpointEnabled) |
|
887 self.marginMenuActs["NextBreakpoint"] = \ |
|
888 self.bpMarginMenu.addAction(self.trUtf8('Next breakpoint'), |
|
889 self.menuNextBreakpoint) |
|
890 self.marginMenuActs["PreviousBreakpoint"] = \ |
|
891 self.bpMarginMenu.addAction(self.trUtf8('Previous breakpoint'), |
|
892 self.menuPreviousBreakpoint) |
|
893 self.marginMenuActs["ClearBreakpoint"] = \ |
|
894 self.bpMarginMenu.addAction(self.trUtf8('Clear all breakpoints'), |
|
895 self.__menuClearBreakpoints) |
|
896 |
|
897 self.connect(self.bpMarginMenu, SIGNAL('aboutToShow()'), |
|
898 self.__showContextMenuMargin) |
|
899 |
|
900 # indicator margin |
|
901 self.indicMarginMenu = QMenu() |
|
902 |
|
903 self.marginMenuActs["GotoSyntaxError"] = \ |
|
904 self.indicMarginMenu.addAction(self.trUtf8('Goto syntax error'), |
|
905 self.gotoSyntaxError) |
|
906 self.marginMenuActs["ShowSyntaxError"] = \ |
|
907 self.indicMarginMenu.addAction(self.trUtf8('Show syntax error message'), |
|
908 self.__showSyntaxError) |
|
909 self.marginMenuActs["ClearSyntaxError"] = \ |
|
910 self.indicMarginMenu.addAction(self.trUtf8('Clear syntax error'), |
|
911 self.clearSyntaxError) |
|
912 self.indicMarginMenu.addSeparator() |
|
913 self.marginMenuActs["NextCoverageMarker"] = \ |
|
914 self.indicMarginMenu.addAction(self.trUtf8('Next uncovered line'), |
|
915 self.nextUncovered) |
|
916 self.marginMenuActs["PreviousCoverageMarker"] = \ |
|
917 self.indicMarginMenu.addAction(self.trUtf8('Previous uncovered line'), |
|
918 self.previousUncovered) |
|
919 self.indicMarginMenu.addSeparator() |
|
920 self.marginMenuActs["NextTaskMarker"] = \ |
|
921 self.indicMarginMenu.addAction(self.trUtf8('Next task'), |
|
922 self.nextTask) |
|
923 self.marginMenuActs["PreviousTaskMarker"] = \ |
|
924 self.indicMarginMenu.addAction(self.trUtf8('Previous task'), |
|
925 self.previousTask) |
|
926 |
|
927 self.connect(self.indicMarginMenu, SIGNAL('aboutToShow()'), |
|
928 self.__showContextMenuMargin) |
|
929 |
|
930 def __initContextMenuUnifiedMargins(self): |
|
931 """ |
|
932 Private method used to setup the context menu for the unified margins |
|
933 """ |
|
934 self.marginMenu = QMenu() |
|
935 |
|
936 self.marginMenu.addAction(self.trUtf8('Toggle bookmark'), |
|
937 self.menuToggleBookmark) |
|
938 self.marginMenuActs["NextBookmark"] = \ |
|
939 self.marginMenu.addAction(self.trUtf8('Next bookmark'), |
|
940 self.nextBookmark) |
|
941 self.marginMenuActs["PreviousBookmark"] = \ |
|
942 self.marginMenu.addAction(self.trUtf8('Previous bookmark'), |
|
943 self.previousBookmark) |
|
944 self.marginMenuActs["ClearBookmark"] = \ |
|
945 self.marginMenu.addAction(self.trUtf8('Clear all bookmarks'), |
|
946 self.clearBookmarks) |
|
947 self.marginMenu.addSeparator() |
|
948 self.marginMenuActs["GotoSyntaxError"] = \ |
|
949 self.marginMenu.addAction(self.trUtf8('Goto syntax error'), |
|
950 self.gotoSyntaxError) |
|
951 self.marginMenuActs["ShowSyntaxError"] = \ |
|
952 self.marginMenu.addAction(self.trUtf8('Show syntax error message'), |
|
953 self.__showSyntaxError) |
|
954 self.marginMenuActs["ClearSyntaxError"] = \ |
|
955 self.marginMenu.addAction(self.trUtf8('Clear syntax error'), |
|
956 self.clearSyntaxError) |
|
957 self.marginMenu.addSeparator() |
|
958 self.marginMenuActs["Breakpoint"] = \ |
|
959 self.marginMenu.addAction(self.trUtf8('Toggle breakpoint'), |
|
960 self.menuToggleBreakpoint) |
|
961 self.marginMenuActs["TempBreakpoint"] = \ |
|
962 self.marginMenu.addAction(self.trUtf8('Toggle temporary breakpoint'), |
|
963 self.__menuToggleTemporaryBreakpoint) |
|
964 self.marginMenuActs["EditBreakpoint"] = \ |
|
965 self.marginMenu.addAction(self.trUtf8('Edit breakpoint...'), |
|
966 self.menuEditBreakpoint) |
|
967 self.marginMenuActs["EnableBreakpoint"] = \ |
|
968 self.marginMenu.addAction(self.trUtf8('Enable breakpoint'), |
|
969 self.__menuToggleBreakpointEnabled) |
|
970 self.marginMenuActs["NextBreakpoint"] = \ |
|
971 self.marginMenu.addAction(self.trUtf8('Next breakpoint'), |
|
972 self.menuNextBreakpoint) |
|
973 self.marginMenuActs["PreviousBreakpoint"] = \ |
|
974 self.marginMenu.addAction(self.trUtf8('Previous breakpoint'), |
|
975 self.menuPreviousBreakpoint) |
|
976 self.marginMenuActs["ClearBreakpoint"] = \ |
|
977 self.marginMenu.addAction(self.trUtf8('Clear all breakpoints'), |
|
978 self.__menuClearBreakpoints) |
|
979 self.marginMenu.addSeparator() |
|
980 self.marginMenuActs["NextCoverageMarker"] = \ |
|
981 self.marginMenu.addAction(self.trUtf8('Next uncovered line'), |
|
982 self.nextUncovered) |
|
983 self.marginMenuActs["PreviousCoverageMarker"] = \ |
|
984 self.marginMenu.addAction(self.trUtf8('Previous uncovered line'), |
|
985 self.previousUncovered) |
|
986 self.marginMenu.addSeparator() |
|
987 self.marginMenuActs["NextTaskMarker"] = \ |
|
988 self.marginMenu.addAction(self.trUtf8('Next task'), |
|
989 self.nextTask) |
|
990 self.marginMenuActs["PreviousTaskMarker"] = \ |
|
991 self.marginMenu.addAction(self.trUtf8('Previous task'), |
|
992 self.previousTask) |
|
993 self.marginMenu.addSeparator() |
|
994 self.marginMenuActs["LMBbookmarks"] = \ |
|
995 self.marginMenu.addAction(self.trUtf8('LMB toggles bookmarks'), |
|
996 self.__lmBbookmarks) |
|
997 self.marginMenuActs["LMBbookmarks"].setCheckable(True) |
|
998 self.marginMenuActs["LMBbookmarks"].setChecked(False) |
|
999 self.marginMenuActs["LMBbreakpoints"] = \ |
|
1000 self.marginMenu.addAction(self.trUtf8('LMB toggles breakpoints'), |
|
1001 self.__lmBbreakpoints) |
|
1002 self.marginMenuActs["LMBbreakpoints"].setCheckable(True) |
|
1003 self.marginMenuActs["LMBbreakpoints"].setChecked(True) |
|
1004 |
|
1005 self.connect(self.marginMenu, SIGNAL('aboutToShow()'), |
|
1006 self.__showContextMenuMargin) |
|
1007 |
|
1008 def __exportMenuTriggered(self, act): |
|
1009 """ |
|
1010 Private method to handle the selection of an export format. |
|
1011 |
|
1012 @param act reference to the action that was triggered (QAction) |
|
1013 """ |
|
1014 exporterFormat = act.data().toString() |
|
1015 self.exportFile(exporterFormat) |
|
1016 |
|
1017 def exportFile(self, exporterFormat): |
|
1018 """ |
|
1019 Public method to export the file. |
|
1020 |
|
1021 @param exporterFormat format the file should be exported into (string) |
|
1022 """ |
|
1023 if exporterFormat: |
|
1024 exporter = Exporters.getExporter(exporterFormat, self) |
|
1025 if exporter: |
|
1026 exporter.exportSource() |
|
1027 else: |
|
1028 QMessageBox.critical(self, |
|
1029 self.trUtf8("Export source"), |
|
1030 self.trUtf8("""<p>No exporter available for the """ |
|
1031 """export format <b>{0}</b>. Aborting...</p>""")\ |
|
1032 .format(exporterFormat), |
|
1033 QMessageBox.StandardButtons(\ |
|
1034 QMessageBox.Ok)) |
|
1035 else: |
|
1036 QMessageBox.critical(self, |
|
1037 self.trUtf8("Export source"), |
|
1038 self.trUtf8("""No export format given. Aborting..."""), |
|
1039 QMessageBox.StandardButtons(\ |
|
1040 QMessageBox.Ok)) |
|
1041 |
|
1042 def __showContextMenuLanguages(self): |
|
1043 """ |
|
1044 Private slot handling the aboutToShow signal of the languages context menu. |
|
1045 """ |
|
1046 if self.apiLanguage.startswith("Pygments|"): |
|
1047 self.pygmentsSelAct.setText( |
|
1048 self.trUtf8("Alternatives ({0})").format(self.getLanguage())) |
|
1049 else: |
|
1050 self.pygmentsSelAct.setText(self.trUtf8("Alternatives")) |
|
1051 self.emit(SIGNAL("showMenu"), "Languages", self.languagesMenu, self) |
|
1052 |
|
1053 def __selectPygmentsLexer(self): |
|
1054 """ |
|
1055 Private method to select a specific pygments lexer. |
|
1056 |
|
1057 @return name of the selected pygments lexer (string) |
|
1058 """ |
|
1059 from pygments.lexers import get_all_lexers |
|
1060 lexerList = sorted([l[0] for l in get_all_lexers()]) |
|
1061 try: |
|
1062 lexerSel = lexerList.index(self.getLanguage()) |
|
1063 except ValueError: |
|
1064 lexerSel = 0 |
|
1065 lexerName, ok = QInputDialog.getItem(\ |
|
1066 self, |
|
1067 self.trUtf8("Pygments Lexer"), |
|
1068 self.trUtf8("Select the Pygments lexer to apply."), |
|
1069 lexerList, |
|
1070 lexerSel, |
|
1071 False) |
|
1072 if ok and lexerName: |
|
1073 return lexerName |
|
1074 else: |
|
1075 return "" |
|
1076 |
|
1077 def __languageMenuTriggered(self, act): |
|
1078 """ |
|
1079 Private method to handle the selection of a lexer language. |
|
1080 |
|
1081 @param act reference to the action that was triggered (QAction) |
|
1082 """ |
|
1083 if act == self.noLanguageAct: |
|
1084 self.__resetLanguage() |
|
1085 elif act == self.pygmentsAct: |
|
1086 self.setLanguage("dummy.pygments") |
|
1087 elif act == self.pygmentsSelAct: |
|
1088 language = self.__selectPygmentsLexer() |
|
1089 if language: |
|
1090 self.setLanguage("dummy.pygments", pyname = language) |
|
1091 else: |
|
1092 language = act.data().toString() |
|
1093 if language: |
|
1094 self.setLanguage(self.supportedLanguages[language][1]) |
|
1095 |
|
1096 def languageChanged(self, language, propagate = True): |
|
1097 """ |
|
1098 Public slot handling a change of a connected editor's language. |
|
1099 |
|
1100 @param language language to be set (string) |
|
1101 @keyparam propagate flag indicating to propagate the change (boolean) |
|
1102 """ |
|
1103 if language == '': |
|
1104 self.__resetLanguage(propagate = propagate) |
|
1105 elif language == "Guessed": |
|
1106 self.setLanguage("dummy.pygments") |
|
1107 elif language.startswith("Pygments|"): |
|
1108 pyname = language.split("|", 1)[1] |
|
1109 self.setLanguage("dummy.pygments", pyname = pyname) |
|
1110 else: |
|
1111 self.setLanguage(self.supportedLanguages[language][1], propagate = propagate) |
|
1112 |
|
1113 def __resetLanguage(self, propagate = True): |
|
1114 """ |
|
1115 Private method used to reset the language selection. |
|
1116 |
|
1117 @keyparam propagate flag indicating to propagate the change (boolean) |
|
1118 """ |
|
1119 if self.lexer_ is not None and \ |
|
1120 (self.lexer_.lexer() == "container" or self.lexer_.lexer() is None): |
|
1121 self.disconnect(self, SIGNAL("SCN_STYLENEEDED(int)"), self.__styleNeeded) |
|
1122 |
|
1123 self.apiLanguage = "" |
|
1124 self.lexer_ = None |
|
1125 self.__lexerReset = True |
|
1126 self.setLexer() |
|
1127 self.setMonospaced(self.useMonospaced) |
|
1128 if self.completer is not None: |
|
1129 self.completer.setEnabled(False) |
|
1130 self.completer = None |
|
1131 self.__setTextDisplay() |
|
1132 |
|
1133 if not self.inLanguageChanged and propagate: |
|
1134 self.inLanguageChanged = True |
|
1135 self.emit(SIGNAL('languageChanged'), self.apiLanguage) |
|
1136 self.inLanguageChanged = False |
|
1137 |
|
1138 def setLanguage(self, filename, initTextDisplay = True, propagate = True, |
|
1139 pyname = ""): |
|
1140 """ |
|
1141 Public method to set a lexer language. |
|
1142 |
|
1143 @param filename filename used to determine the associated lexer language (string) |
|
1144 @param initTextDisplay flag indicating an initialization of the text display |
|
1145 is required as well (boolean) |
|
1146 @keyparam propagate flag indicating to propagate the change (boolean) |
|
1147 @keyparam pyname name of the pygments lexer to use (string) |
|
1148 """ |
|
1149 self.__lexerReset = False |
|
1150 self.__bindLexer(filename, pyname = pyname) |
|
1151 self.__bindCompleter(filename) |
|
1152 self.recolor() |
|
1153 self.__checkLanguage() |
|
1154 |
|
1155 # set the text display |
|
1156 if initTextDisplay: |
|
1157 self.__setTextDisplay() |
|
1158 |
|
1159 # set the autocompletion and calltips function |
|
1160 self.__setAutoCompletion() |
|
1161 self.__setCallTips() |
|
1162 |
|
1163 if not self.inLanguageChanged and propagate: |
|
1164 self.inLanguageChanged = True |
|
1165 self.emit(SIGNAL('languageChanged'), self.apiLanguage) |
|
1166 self.inLanguageChanged = False |
|
1167 |
|
1168 def __checkLanguage(self): |
|
1169 """ |
|
1170 Private method to check the selected language of the language submenu. |
|
1171 """ |
|
1172 if self.apiLanguage == "": |
|
1173 self.noLanguageAct.setChecked(True) |
|
1174 elif self.apiLanguage == "Guessed": |
|
1175 self.pygmentsAct.setChecked(True) |
|
1176 elif self.apiLanguage.startswith("Pygments|"): |
|
1177 act = self.languagesActGrp.checkedAction() |
|
1178 if act: |
|
1179 act.setChecked(False) |
|
1180 else: |
|
1181 self.supportedLanguages[self.apiLanguage][2].setChecked(True) |
|
1182 |
|
1183 def projectLexerAssociationsChanged(self): |
|
1184 """ |
|
1185 Public slot to handle changes of the project lexer associations. |
|
1186 """ |
|
1187 self.setLanguage(self.fileName) |
|
1188 |
|
1189 def __showContextMenuEncodings(self): |
|
1190 """ |
|
1191 Private slot handling the aboutToShow signal of the encodings context menu. |
|
1192 """ |
|
1193 self.emit(SIGNAL("showMenu"), "Encodings", self.encodingsMenu, self) |
|
1194 |
|
1195 def __encodingsMenuTriggered(self, act): |
|
1196 """ |
|
1197 Private method to handle the selection of an encoding. |
|
1198 |
|
1199 @param act reference to the action that was triggered (QAction) |
|
1200 """ |
|
1201 encoding = act.data().toString() |
|
1202 self.__encodingChanged("%s-selected" % encoding) |
|
1203 |
|
1204 def __checkEncoding(self): |
|
1205 """ |
|
1206 Private method to check the selected encoding of the encodings submenu. |
|
1207 """ |
|
1208 try: |
|
1209 self.supportedEncodings[self.__normalizedEncoding()].setChecked(True) |
|
1210 except (AttributeError, KeyError): |
|
1211 pass |
|
1212 |
|
1213 def __encodingChanged(self, encoding, propagate = True): |
|
1214 """ |
|
1215 Private slot to handle a change of the encoding. |
|
1216 |
|
1217 @keyparam propagate flag indicating to propagate the change (boolean) |
|
1218 """ |
|
1219 self.encoding = encoding |
|
1220 self.__checkEncoding() |
|
1221 |
|
1222 if not self.inEncodingChanged and propagate: |
|
1223 self.inEncodingChanged = True |
|
1224 self.emit(SIGNAL("encodingChanged"), self.encoding) |
|
1225 self.inEncodingChanged = False |
|
1226 |
|
1227 def __normalizedEncoding(self): |
|
1228 """ |
|
1229 Private method to calculate the normalized encoding string. |
|
1230 |
|
1231 @return normalized encoding (string) |
|
1232 """ |
|
1233 return self.encoding.replace("-default", "")\ |
|
1234 .replace("-guessed", "")\ |
|
1235 .replace("-selected", "") |
|
1236 |
|
1237 def __showContextMenuEol(self): |
|
1238 """ |
|
1239 Private slot handling the aboutToShow signal of the eol context menu. |
|
1240 """ |
|
1241 self.emit(SIGNAL("showMenu"), "Eol", self.eolMenu, self) |
|
1242 |
|
1243 def __eolMenuTriggered(self, act): |
|
1244 """ |
|
1245 Private method to handle the selection of an eol type. |
|
1246 |
|
1247 @param act reference to the action that was triggered (QAction) |
|
1248 """ |
|
1249 eol = act.data().toString() |
|
1250 self.setEolModeByEolString(eol) |
|
1251 self.convertEols(self.eolMode()) |
|
1252 |
|
1253 def __checkEol(self): |
|
1254 """ |
|
1255 Private method to check the selected eol type of the eol submenu. |
|
1256 """ |
|
1257 try: |
|
1258 self.supportedEols[self.getLineSeparator()].setChecked(True) |
|
1259 except AttributeError: |
|
1260 pass |
|
1261 |
|
1262 def __eolChanged(self): |
|
1263 """ |
|
1264 Private slot to handle a change of the eol mode. |
|
1265 """ |
|
1266 self.__checkEol() |
|
1267 |
|
1268 if not self.inEolChanged: |
|
1269 self.inEolChanged = True |
|
1270 eol = self.getLineSeparator() |
|
1271 self.emit(SIGNAL("eolChanged"), eol) |
|
1272 self.inEolChanged = False |
|
1273 |
|
1274 def __bindLexer(self, filename, pyname = ""): |
|
1275 """ |
|
1276 Private slot to set the correct lexer depending on language. |
|
1277 |
|
1278 @param filename filename used to determine the associated lexer language (string) |
|
1279 @keyparam pyname name of the pygments lexer to use (string) |
|
1280 """ |
|
1281 if self.lexer_ is not None and \ |
|
1282 (self.lexer_.lexer() == "container" or self.lexer_.lexer() is None): |
|
1283 self.disconnect(self, SIGNAL("SCN_STYLENEEDED(int)"), self.__styleNeeded) |
|
1284 |
|
1285 language = "" |
|
1286 project = e4App().getObject("Project") |
|
1287 if project.isOpen() and project.isProjectFile(filename): |
|
1288 language = project.getEditorLexerAssoc(os.path.basename(filename)) |
|
1289 if not language: |
|
1290 filename = os.path.basename(filename) |
|
1291 language = Preferences.getEditorLexerAssoc(filename) |
|
1292 if language.startswith("Pygments|"): |
|
1293 pyname = language.split("|", 1)[1] |
|
1294 language = "" |
|
1295 |
|
1296 self.lexer_ = Lexers.getLexer(language, self, pyname = pyname) |
|
1297 if self.lexer_ is None: |
|
1298 self.setLexer() |
|
1299 self.apiLanguage = "" |
|
1300 return |
|
1301 |
|
1302 if pyname: |
|
1303 self.apiLanguage = "Pygments|%s" % pyname |
|
1304 else: |
|
1305 self.apiLanguage = self.lexer_.language() |
|
1306 if self.apiLanguage == "POV": |
|
1307 self.apiLanguage = "Povray" |
|
1308 self.setLexer(self.lexer_) |
|
1309 self.__setMarginsDisplay() |
|
1310 if self.lexer_.lexer() == "container" or self.lexer_.lexer() is None: |
|
1311 self.setStyleBits(self.lexer_.styleBitsNeeded()) |
|
1312 self.connect(self, SIGNAL("SCN_STYLENEEDED(int)"), self.__styleNeeded) |
|
1313 |
|
1314 # get the font for style 0 and set it as the default font |
|
1315 key = 'Scintilla/%s/style0/font' % self.lexer_.language() |
|
1316 fontVariant = Preferences.Prefs.settings.value(key) |
|
1317 if fontVariant.isValid(): |
|
1318 fdesc = fontVariant.toStringList() |
|
1319 font = QFont(fdesc[0], int(fdesc[1])) |
|
1320 self.lexer_.setDefaultFont(font) |
|
1321 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
|
1322 |
|
1323 # now set the lexer properties |
|
1324 self.lexer_.initProperties() |
|
1325 |
|
1326 # initialize the auto indent style of the lexer |
|
1327 ais = self.lexer_.autoIndentStyle() |
|
1328 |
|
1329 # initialize the lexer APIs settings |
|
1330 api = self.vm.getAPIsManager().getAPIs(self.apiLanguage) |
|
1331 if api is not None: |
|
1332 self.lexer_.setAPIs(api.getQsciAPIs()) |
|
1333 self.acAPI = True |
|
1334 else: |
|
1335 self.acAPI = False |
|
1336 self.emit(SIGNAL("autoCompletionAPIsAvailable"), self.acAPI) |
|
1337 |
|
1338 def __styleNeeded(self, position): |
|
1339 """ |
|
1340 Private slot to handle the need for more styling. |
|
1341 |
|
1342 @param position end position, that needs styling (integer) |
|
1343 """ |
|
1344 self.lexer_.styleText(self.getEndStyled(), position) |
|
1345 |
|
1346 def getLexer(self): |
|
1347 """ |
|
1348 Public method to retrieve a reference to the lexer object. |
|
1349 |
|
1350 @return the lexer object (Lexer) |
|
1351 """ |
|
1352 return self.lexer_ |
|
1353 |
|
1354 def getLanguage(self): |
|
1355 """ |
|
1356 Public method to retrieve the language of the editor. |
|
1357 |
|
1358 @return language of the editor (string) |
|
1359 """ |
|
1360 if self.apiLanguage == "Guessed" or self.apiLanguage.startswith("Pygments|"): |
|
1361 return self.lexer_.name() |
|
1362 else: |
|
1363 return self.apiLanguage |
|
1364 |
|
1365 def __bindCompleter(self, filename): |
|
1366 """ |
|
1367 Private slot to set the correct typing completer depending on language. |
|
1368 |
|
1369 @param filename filename used to determine the associated typing |
|
1370 completer language (string) |
|
1371 """ |
|
1372 if self.completer is not None: |
|
1373 self.completer.setEnabled(False) |
|
1374 self.completer = None |
|
1375 |
|
1376 filename = os.path.basename(filename) |
|
1377 apiLanguage = Preferences.getEditorLexerAssoc(filename) |
|
1378 |
|
1379 self.completer = TypingCompleters.getCompleter(apiLanguage, self) |
|
1380 |
|
1381 def getCompleter(self): |
|
1382 """ |
|
1383 Public method to retrieve a reference to the completer object. |
|
1384 |
|
1385 @return the completer object (CompleterBase) |
|
1386 """ |
|
1387 return self.completer |
|
1388 |
|
1389 def __modificationChanged(self, m): |
|
1390 """ |
|
1391 Private slot to handle the modificationChanged signal. |
|
1392 |
|
1393 It emits the signal modificationStatusChanged with parameters |
|
1394 m and self. |
|
1395 |
|
1396 @param m modification status |
|
1397 """ |
|
1398 if not m and self.fileName is not None: |
|
1399 self.lastModified = QFileInfo(self.fileName).lastModified() |
|
1400 if Preferences.getEditor("AutoCheckSyntax"): |
|
1401 self.clearSyntaxError() |
|
1402 self.emit(SIGNAL('modificationStatusChanged'), m, self) |
|
1403 self.emit(SIGNAL('undoAvailable'), self.isUndoAvailable()) |
|
1404 self.emit(SIGNAL('redoAvailable'), self.isRedoAvailable()) |
|
1405 |
|
1406 def __cursorPositionChanged(self, line, index): |
|
1407 """ |
|
1408 Private slot to handle the cursorPositionChanged signal. |
|
1409 |
|
1410 It emits the signal cursorChanged with parameters fileName, |
|
1411 line and pos. |
|
1412 |
|
1413 @param line line number of the cursor |
|
1414 @param index position in line of the cursor |
|
1415 """ |
|
1416 self.emit(SIGNAL('cursorChanged'), self.fileName, line+1, index) |
|
1417 |
|
1418 if Preferences.getEditor("MarkOccurrencesEnabled"): |
|
1419 self.__markOccurrencesTimer.stop() |
|
1420 self.__markOccurrencesTimer.start() |
|
1421 |
|
1422 if self.spell is not None: |
|
1423 # do spell checking |
|
1424 doSpelling = True |
|
1425 if self.lastLine == line: |
|
1426 start, end = self.getWordBoundaries(line, index, useWordChars = False) |
|
1427 if start <= self.lastIndex and self.lastIndex <= end: |
|
1428 doSpelling = False |
|
1429 if doSpelling: |
|
1430 pos = self.positionFromLineIndex(self.lastLine, self.lastIndex) |
|
1431 self.spell.checkWord(pos) |
|
1432 |
|
1433 self.lastLine = line |
|
1434 self.lastIndex = index |
|
1435 |
|
1436 def __modificationReadOnly(self): |
|
1437 """ |
|
1438 Private slot to handle the modificationAttempted signal. |
|
1439 """ |
|
1440 QMessageBox.warning(None, |
|
1441 self.trUtf8("Modification of Read Only file"), |
|
1442 self.trUtf8("""You are attempting to change a read only file. """ |
|
1443 """Please save to a different file first.""")) |
|
1444 |
|
1445 def setNoName(self, noName): |
|
1446 """ |
|
1447 Public method to set the display string for an unnamed editor. |
|
1448 |
|
1449 @param noName display string for this unnamed editor (string) |
|
1450 """ |
|
1451 self.noName = noName |
|
1452 |
|
1453 def getNoName(self): |
|
1454 """ |
|
1455 Public method to get the display string for an unnamed editor. |
|
1456 |
|
1457 @return display string for this unnamed editor (string) |
|
1458 """ |
|
1459 return self.noName |
|
1460 |
|
1461 def getFileName(self): |
|
1462 """ |
|
1463 Public method to return the name of the file being displayed. |
|
1464 |
|
1465 @return filename of the displayed file (string) |
|
1466 """ |
|
1467 return self.fileName |
|
1468 |
|
1469 def getFileType(self): |
|
1470 """ |
|
1471 Public method to return the type of the file being displayed. |
|
1472 |
|
1473 @return type of the displayed file (string) |
|
1474 """ |
|
1475 return self.filetype |
|
1476 |
|
1477 def getEncoding(self): |
|
1478 """ |
|
1479 Public method to return the current encoding. |
|
1480 |
|
1481 @return current encoding (string) |
|
1482 """ |
|
1483 return self.encoding |
|
1484 |
|
1485 def getFolds(self): |
|
1486 """ |
|
1487 Public method to get a list line numbers of collapsed folds. |
|
1488 |
|
1489 @return list of line numbers of folded lines (list of integer) |
|
1490 """ |
|
1491 line = 0 |
|
1492 folds = [] |
|
1493 maxline = self.lines() |
|
1494 while line < maxline: |
|
1495 if self.foldHeaderAt(line) and not self.foldExpandedAt(line): |
|
1496 folds.append(line) |
|
1497 line += 1 |
|
1498 return folds |
|
1499 |
|
1500 def isPyFile(self): |
|
1501 """ |
|
1502 Public method to return a flag indicating a Python file. |
|
1503 |
|
1504 @return flag indicating a Python file (boolean) |
|
1505 """ |
|
1506 return self.filetype == "Python" or \ |
|
1507 (self.fileName is not None and \ |
|
1508 os.path.splitext(self.fileName)[1] in self.pyExtensions) |
|
1509 |
|
1510 def isPy3File(self): |
|
1511 """ |
|
1512 Public method to return a flag indicating a Python3 file. |
|
1513 |
|
1514 @return flag indicating a Python3 file (boolean) |
|
1515 """ |
|
1516 return self.filetype == "Python3" or \ |
|
1517 (self.fileName is not None and \ |
|
1518 os.path.splitext(self.fileName)[1] in self.py3Extensions) |
|
1519 |
|
1520 def isRubyFile(self): |
|
1521 """ |
|
1522 Public method to return a flag indicating a Ruby file. |
|
1523 |
|
1524 @return flag indicating a Ruby file (boolean) |
|
1525 """ |
|
1526 return self.filetype == "Ruby" or \ |
|
1527 (self.fileName is not None and \ |
|
1528 os.path.splitext(self.fileName)[1] in self.rbExtensions) |
|
1529 |
|
1530 def highlightVisible(self): |
|
1531 """ |
|
1532 Public method to make sure that the highlight is visible. |
|
1533 """ |
|
1534 if self.lastHighlight is not None: |
|
1535 lineno = self.markerLine(self.lastHighlight) |
|
1536 self.ensureVisible(lineno+1) |
|
1537 |
|
1538 def highlight(self, line = None, error = False, syntaxError = False): |
|
1539 """ |
|
1540 Public method to highlight (or de-highlight) a particular line. |
|
1541 |
|
1542 @param line line number to highlight (integer) |
|
1543 @param error flag indicating whether the error highlight should be used (boolean) |
|
1544 @param syntaxError flag indicating a syntax error (boolean) |
|
1545 """ |
|
1546 if line is None: |
|
1547 self.lastHighlight = None |
|
1548 if self.lastErrorMarker is not None: |
|
1549 self.markerDeleteHandle(self.lastErrorMarker) |
|
1550 self.lastErrorMarker = None |
|
1551 if self.lastCurrMarker is not None: |
|
1552 self.markerDeleteHandle(self.lastCurrMarker) |
|
1553 self.lastCurrMarker = None |
|
1554 else: |
|
1555 if error: |
|
1556 if self.lastErrorMarker is not None: |
|
1557 self.markerDeleteHandle(self.lastErrorMarker) |
|
1558 self.lastErrorMarker = self.markerAdd(line-1, self.errorline) |
|
1559 self.lastHighlight = self.lastErrorMarker |
|
1560 else: |
|
1561 if self.lastCurrMarker is not None: |
|
1562 self.markerDeleteHandle(self.lastCurrMarker) |
|
1563 self.lastCurrMarker = self.markerAdd(line-1, self.currentline) |
|
1564 self.lastHighlight = self.lastCurrMarker |
|
1565 self.setCursorPosition(line-1, 0) |
|
1566 |
|
1567 def getHighlightPosition(self): |
|
1568 """ |
|
1569 Public method to return the position of the highlight bar. |
|
1570 |
|
1571 @return line number of the highlight bar (integer) |
|
1572 """ |
|
1573 if self.lastHighlight is not None: |
|
1574 return self.markerLine(self.lastHighlight) |
|
1575 else: |
|
1576 return 1 |
|
1577 |
|
1578 ############################################################################ |
|
1579 ## Breakpoint handling methods below |
|
1580 ############################################################################ |
|
1581 |
|
1582 def __linesChanged(self): |
|
1583 """ |
|
1584 Private method to track text changes. |
|
1585 |
|
1586 This method checks, if lines have been inserted or removed in order to |
|
1587 update the breakpoints. |
|
1588 """ |
|
1589 if self.breaks: |
|
1590 bps = [] # list of breakpoints |
|
1591 for handle, (ln, cond, temp, enabled, ignorecount) in self.breaks.items(): |
|
1592 line = self.markerLine(handle) + 1 |
|
1593 bps.append((ln, line, (cond, temp, enabled, ignorecount))) |
|
1594 self.markerDeleteHandle(handle) |
|
1595 self.breaks = {} |
|
1596 for bp in bps: |
|
1597 index = self.breakpointModel.getBreakPointIndex(self.fileName, bp[0]) |
|
1598 self.breakpointModel.setBreakPointByIndex(index, |
|
1599 self.fileName, bp[1], bp[2]) |
|
1600 |
|
1601 def __restoreBreakpoints(self): |
|
1602 """ |
|
1603 Private method to restore the breakpoints. |
|
1604 """ |
|
1605 for handle in self.breaks.keys(): |
|
1606 self.markerDeleteHandle(handle) |
|
1607 self.__addBreakPoints(QModelIndex(), 0, self.breakpointModel.rowCount() - 1) |
|
1608 |
|
1609 def __deleteBreakPoints(self, parentIndex, start, end): |
|
1610 """ |
|
1611 Private slot to delete breakpoints. |
|
1612 |
|
1613 @param parentIndex index of parent item (QModelIndex) |
|
1614 @param start start row (integer) |
|
1615 @param end end row (integer) |
|
1616 """ |
|
1617 for row in range(start, end + 1): |
|
1618 index = self.breakpointModel.index(row, 0, parentIndex) |
|
1619 fn, lineno = self.breakpointModel.getBreakPointByIndex(index)[0:2] |
|
1620 if fn == self.fileName: |
|
1621 self.clearBreakpoint(lineno) |
|
1622 |
|
1623 def __changeBreakPoints(self, startIndex, endIndex): |
|
1624 """ |
|
1625 Private slot to set changed breakpoints. |
|
1626 |
|
1627 @param indexes indexes of changed breakpoints. |
|
1628 """ |
|
1629 self.__addBreakPoints(QModelIndex(), startIndex.row(), endIndex.row()) |
|
1630 |
|
1631 def __breakPointDataAboutToBeChanged(self, startIndex, endIndex): |
|
1632 """ |
|
1633 Private slot to handle the dataAboutToBeChanged signal of the breakpoint model. |
|
1634 |
|
1635 @param startIndex start index of the rows to be changed (QModelIndex) |
|
1636 @param endIndex end index of the rows to be changed (QModelIndex) |
|
1637 """ |
|
1638 self.__deleteBreakPoints(QModelIndex(), startIndex.row(), endIndex.row()) |
|
1639 |
|
1640 def __addBreakPoints(self, parentIndex, start, end): |
|
1641 """ |
|
1642 Private slot to add breakpoints. |
|
1643 |
|
1644 @param parentIndex index of parent item (QModelIndex) |
|
1645 @param start start row (integer) |
|
1646 @param end end row (integer) |
|
1647 """ |
|
1648 for row in range(start, end + 1): |
|
1649 index = self.breakpointModel.index(row, 0, parentIndex) |
|
1650 fn, line, cond, temp, enabled, ignorecount = \ |
|
1651 self.breakpointModel.getBreakPointByIndex(index)[:6] |
|
1652 if fn == self.fileName: |
|
1653 self.newBreakpointWithProperties(line, (cond, temp, enabled, ignorecount)) |
|
1654 |
|
1655 def clearBreakpoint(self, line): |
|
1656 """ |
|
1657 Public method to clear a breakpoint. |
|
1658 |
|
1659 Note: This doesn't clear the breakpoint in the debugger, |
|
1660 it just deletes it from the editor internal list of breakpoints. |
|
1661 |
|
1662 @param line linenumber of the breakpoint (integer) |
|
1663 """ |
|
1664 for handle, (ln, _, _, _, _) in self.breaks.items(): |
|
1665 if self.markerLine(handle) == line-1: |
|
1666 break |
|
1667 else: |
|
1668 # not found, simply ignore it |
|
1669 return |
|
1670 |
|
1671 del self.breaks[handle] |
|
1672 self.markerDeleteHandle(handle) |
|
1673 |
|
1674 def newBreakpointWithProperties(self, line, properties): |
|
1675 """ |
|
1676 Private method to set a new breakpoint and its properties. |
|
1677 |
|
1678 @param line line number of the breakpoint (integer) |
|
1679 @param properties properties for the breakpoint (tuple) |
|
1680 (condition, temporary flag, enabled flag, ignore count) |
|
1681 """ |
|
1682 if not properties[2]: |
|
1683 marker = self.dbreakpoint |
|
1684 elif properties[0]: |
|
1685 marker = properties[1] and self.tcbreakpoint or self.cbreakpoint |
|
1686 else: |
|
1687 marker = properties[1] and self.tbreakpoint or self.breakpoint |
|
1688 |
|
1689 handle = self.markerAdd(line-1, marker) |
|
1690 self.breaks[handle] = (line,) + properties |
|
1691 self.emit(SIGNAL('breakpointToggled'), self) |
|
1692 |
|
1693 def __toggleBreakpoint(self, line, temporary = False): |
|
1694 """ |
|
1695 Private method to toggle a breakpoint. |
|
1696 |
|
1697 @param line line number of the breakpoint (integer) |
|
1698 @param temporary flag indicating a temporary breakpoint (boolean) |
|
1699 """ |
|
1700 for handle, (ln, _, _, _, _) in self.breaks.items(): |
|
1701 if self.markerLine(handle) == line - 1: |
|
1702 # delete breakpoint or toggle it to the next state |
|
1703 index = self.breakpointModel.getBreakPointIndex(self.fileName, line) |
|
1704 if Preferences.getDebugger("ThreeStateBreakPoints") and \ |
|
1705 not self.breakpointModel.isBreakPointTemporaryByIndex(index): |
|
1706 self.breakpointModel.deleteBreakPointByIndex(index) |
|
1707 self.__addBreakPoint(line, True) |
|
1708 else: |
|
1709 self.breakpointModel.deleteBreakPointByIndex(index) |
|
1710 self.emit(SIGNAL('breakpointToggled'), self) |
|
1711 break |
|
1712 else: |
|
1713 self.__addBreakPoint(line, temporary) |
|
1714 |
|
1715 def __addBreakPoint(self, line, temporary): |
|
1716 """ |
|
1717 Private method to add a new breakpoint. |
|
1718 |
|
1719 @param line line number of the breakpoint (integer) |
|
1720 @param temporary flag indicating a temporary breakpoint (boolean) |
|
1721 """ |
|
1722 if self.fileName and \ |
|
1723 (self.isPyFile() or self.isPy3File() or self.isRubyFile()): |
|
1724 self.breakpointModel.addBreakPoint(self.fileName, line, |
|
1725 ('', temporary, True, 0)) |
|
1726 self.emit(SIGNAL('breakpointToggled'), self) |
|
1727 |
|
1728 def __toggleBreakpointEnabled(self, line): |
|
1729 """ |
|
1730 Private method to toggle a breakpoints enabled status. |
|
1731 |
|
1732 @param line line number of the breakpoint (integer) |
|
1733 """ |
|
1734 for handle, (ln, cond, temp, enabled, ignorecount) in self.breaks.items(): |
|
1735 if self.markerLine(handle) == line - 1: |
|
1736 break |
|
1737 else: |
|
1738 # no breakpoint found on that line |
|
1739 return |
|
1740 |
|
1741 index = self.breakpointModel.getBreakPointIndex(self.fileName, line) |
|
1742 self.breakpointModel.setBreakPointEnabledByIndex(index, not enabled) |
|
1743 |
|
1744 def curLineHasBreakpoint(self): |
|
1745 """ |
|
1746 Public method to check for the presence of a breakpoint at the current line. |
|
1747 |
|
1748 @return flag indicating the presence of a breakpoint (boolean) |
|
1749 """ |
|
1750 line, _ = self.getCursorPosition() |
|
1751 return self.markersAtLine(line) & self.breakpointMask != 0 |
|
1752 |
|
1753 def hasBreakpoints(self): |
|
1754 """ |
|
1755 Public method to check for the presence of breakpoints. |
|
1756 |
|
1757 @return flag indicating the presence of breakpoints (boolean) |
|
1758 """ |
|
1759 return len(self.breaks) > 0 |
|
1760 |
|
1761 def __menuToggleTemporaryBreakpoint(self): |
|
1762 """ |
|
1763 Private slot to handle the 'Toggle temporary breakpoint' context menu action. |
|
1764 """ |
|
1765 if self.line < 0: |
|
1766 self.line, index = self.getCursorPosition() |
|
1767 self.line += 1 |
|
1768 self.__toggleBreakpoint(self.line, 1) |
|
1769 self.line = -1 |
|
1770 |
|
1771 def menuToggleBreakpoint(self): |
|
1772 """ |
|
1773 Public slot to handle the 'Toggle breakpoint' context menu action. |
|
1774 """ |
|
1775 if self.line < 0: |
|
1776 self.line, index = self.getCursorPosition() |
|
1777 self.line += 1 |
|
1778 self.__toggleBreakpoint(self.line) |
|
1779 self.line = -1 |
|
1780 |
|
1781 def __menuToggleBreakpointEnabled(self): |
|
1782 """ |
|
1783 Private slot to handle the 'Enable/Disable breakpoint' context menu action. |
|
1784 """ |
|
1785 if self.line < 0: |
|
1786 self.line, index = self.getCursorPosition() |
|
1787 self.line += 1 |
|
1788 self.__toggleBreakpointEnabled(self.line) |
|
1789 self.line = -1 |
|
1790 |
|
1791 def menuEditBreakpoint(self, line = None): |
|
1792 """ |
|
1793 Public slot to handle the 'Edit breakpoint' context menu action. |
|
1794 |
|
1795 @param line linenumber of the breakpoint to edit |
|
1796 """ |
|
1797 if line is not None: |
|
1798 self.line = line - 1 |
|
1799 if self.line < 0: |
|
1800 self.line, index = self.getCursorPosition() |
|
1801 found = False |
|
1802 for handle, (ln, cond, temp, enabled, ignorecount) in self.breaks.items(): |
|
1803 if self.markerLine(handle) == self.line: |
|
1804 found = True |
|
1805 break |
|
1806 |
|
1807 if found: |
|
1808 index = self.breakpointModel.getBreakPointIndex(self.fileName, ln) |
|
1809 if not index.isValid(): |
|
1810 return |
|
1811 |
|
1812 dlg = EditBreakpointDialog((self.fileName, ln), |
|
1813 (cond, temp, enabled, ignorecount), |
|
1814 self.condHistory, self, modal = True) |
|
1815 if dlg.exec_() == QDialog.Accepted: |
|
1816 cond, temp, enabled, ignorecount = dlg.getData() |
|
1817 self.breakpointModel.setBreakPointByIndex(index, |
|
1818 self.fileName, ln, (cond, temp, enabled, ignorecount)) |
|
1819 |
|
1820 self.line = -1 |
|
1821 |
|
1822 def menuNextBreakpoint(self): |
|
1823 """ |
|
1824 Public slot to handle the 'Next breakpoint' context menu action. |
|
1825 """ |
|
1826 line, index = self.getCursorPosition() |
|
1827 if line == self.lines()-1: |
|
1828 line = 0 |
|
1829 else: |
|
1830 line += 1 |
|
1831 bpline = self.markerFindNext(line, self.breakpointMask) |
|
1832 if bpline < 0: |
|
1833 # wrap around |
|
1834 bpline = self.markerFindNext(0, self.breakpointMask) |
|
1835 if bpline >= 0: |
|
1836 self.setCursorPosition(bpline, 0) |
|
1837 self.ensureLineVisible(bpline) |
|
1838 |
|
1839 def menuPreviousBreakpoint(self): |
|
1840 """ |
|
1841 Public slot to handle the 'Previous breakpoint' context menu action. |
|
1842 """ |
|
1843 line, index = self.getCursorPosition() |
|
1844 if line == 0: |
|
1845 line = self.lines()-1 |
|
1846 else: |
|
1847 line -= 1 |
|
1848 bpline = self.markerFindPrevious(line, self.breakpointMask) |
|
1849 if bpline < 0: |
|
1850 # wrap around |
|
1851 bpline = self.markerFindPrevious(self.lines()-1, self.breakpointMask) |
|
1852 if bpline >= 0: |
|
1853 self.setCursorPosition(bpline, 0) |
|
1854 self.ensureLineVisible(bpline) |
|
1855 |
|
1856 def __menuClearBreakpoints(self): |
|
1857 """ |
|
1858 Private slot to handle the 'Clear all breakpoints' context menu action. |
|
1859 """ |
|
1860 self.__clearBreakpoints(self.fileName) |
|
1861 |
|
1862 def __clearBreakpoints(self, fileName): |
|
1863 """ |
|
1864 Private slot to clear all breakpoints. |
|
1865 """ |
|
1866 idxList = [] |
|
1867 for handle, (ln, _, _, _, _) in self.breaks.items(): |
|
1868 index = self.breakpointModel.getBreakPointIndex(fileName, ln) |
|
1869 if index.isValid(): |
|
1870 idxList.append(index) |
|
1871 if idxList: |
|
1872 self.breakpointModel.deleteBreakPoints(idxList) |
|
1873 |
|
1874 ############################################################################ |
|
1875 ## Bookmark handling methods below |
|
1876 ############################################################################ |
|
1877 |
|
1878 def toggleBookmark(self, line): |
|
1879 """ |
|
1880 Public method to toggle a bookmark. |
|
1881 |
|
1882 @param line line number of the bookmark (integer) |
|
1883 """ |
|
1884 for handle in self.bookmarks: |
|
1885 if self.markerLine(handle) == line - 1: |
|
1886 break |
|
1887 else: |
|
1888 # set a new bookmark |
|
1889 handle = self.markerAdd(line - 1, self.bookmark) |
|
1890 self.bookmarks.append(handle) |
|
1891 self.emit(SIGNAL('bookmarkToggled'), self) |
|
1892 return |
|
1893 |
|
1894 self.bookmarks.remove(handle) |
|
1895 self.markerDeleteHandle(handle) |
|
1896 self.emit(SIGNAL('bookmarkToggled'), self) |
|
1897 |
|
1898 def getBookmarks(self): |
|
1899 """ |
|
1900 Public method to retrieve the bookmarks. |
|
1901 |
|
1902 @return sorted list of all lines containing a bookmark |
|
1903 (list of integer) |
|
1904 """ |
|
1905 bmlist = [] |
|
1906 for handle in self.bookmarks: |
|
1907 bmlist.append(self.markerLine(handle) + 1) |
|
1908 |
|
1909 bmlist.sort() |
|
1910 return bmlist |
|
1911 |
|
1912 def hasBookmarks(self): |
|
1913 """ |
|
1914 Public method to check for the presence of bookmarks. |
|
1915 |
|
1916 @return flag indicating the presence of bookmarks (boolean) |
|
1917 """ |
|
1918 return len(self.bookmarks) > 0 |
|
1919 |
|
1920 def menuToggleBookmark(self): |
|
1921 """ |
|
1922 Public slot to handle the 'Toggle bookmark' context menu action. |
|
1923 """ |
|
1924 if self.line < 0: |
|
1925 self.line, index = self.getCursorPosition() |
|
1926 self.line += 1 |
|
1927 self.toggleBookmark(self.line) |
|
1928 self.line = -1 |
|
1929 |
|
1930 def nextBookmark(self): |
|
1931 """ |
|
1932 Public slot to handle the 'Next bookmark' context menu action. |
|
1933 """ |
|
1934 line, index = self.getCursorPosition() |
|
1935 if line == self.lines()-1: |
|
1936 line = 0 |
|
1937 else: |
|
1938 line += 1 |
|
1939 bmline = self.markerFindNext(line, 1 << self.bookmark) |
|
1940 if bmline < 0: |
|
1941 # wrap around |
|
1942 bmline = self.markerFindNext(0, 1 << self.bookmark) |
|
1943 if bmline >= 0: |
|
1944 self.setCursorPosition(bmline, 0) |
|
1945 self.ensureLineVisible(bmline) |
|
1946 |
|
1947 def previousBookmark(self): |
|
1948 """ |
|
1949 Public slot to handle the 'Previous bookmark' context menu action. |
|
1950 """ |
|
1951 line, index = self.getCursorPosition() |
|
1952 if line == 0: |
|
1953 line = self.lines()-1 |
|
1954 else: |
|
1955 line -= 1 |
|
1956 bmline = self.markerFindPrevious(line, 1 << self.bookmark) |
|
1957 if bmline < 0: |
|
1958 # wrap around |
|
1959 bmline = self.markerFindPrevious(self.lines() - 1, 1 << self.bookmark) |
|
1960 if bmline >= 0: |
|
1961 self.setCursorPosition(bmline, 0) |
|
1962 self.ensureLineVisible(bmline) |
|
1963 |
|
1964 def clearBookmarks(self): |
|
1965 """ |
|
1966 Public slot to handle the 'Clear all bookmarks' context menu action. |
|
1967 """ |
|
1968 for handle in self.bookmarks: |
|
1969 self.markerDeleteHandle(handle) |
|
1970 self.bookmarks = [] |
|
1971 self.emit(SIGNAL('bookmarkToggled'), self) |
|
1972 |
|
1973 ############################################################################ |
|
1974 ## Printing methods below |
|
1975 ############################################################################ |
|
1976 |
|
1977 def printFile(self): |
|
1978 """ |
|
1979 Public slot to print the text. |
|
1980 """ |
|
1981 printer = Printer(mode = QPrinter.HighResolution) |
|
1982 sb = e4App().getObject("UserInterface").statusBar() |
|
1983 printDialog = QPrintDialog(printer, self) |
|
1984 if self.hasSelectedText(): |
|
1985 printDialog.addEnabledOption(QAbstractPrintDialog.PrintSelection) |
|
1986 if printDialog.exec_() == QDialog.Accepted: |
|
1987 sb.showMessage(self.trUtf8('Printing...')) |
|
1988 QApplication.processEvents() |
|
1989 fn = self.getFileName() |
|
1990 if fn is not None: |
|
1991 printer.setDocName(os.path.basename(fn)) |
|
1992 else: |
|
1993 printer.setDocName(self.noName) |
|
1994 if printDialog.printRange() == QAbstractPrintDialog.Selection: |
|
1995 # get the selection |
|
1996 fromLine, fromIndex, toLine, toIndex = self.getSelection() |
|
1997 if toIndex == 0: |
|
1998 toLine -= 1 |
|
1999 # Qscintilla seems to print one line more than told |
|
2000 res = printer.printRange(self, fromLine, toLine-1) |
|
2001 else: |
|
2002 res = printer.printRange(self) |
|
2003 if res: |
|
2004 sb.showMessage(self.trUtf8('Printing completed'), 2000) |
|
2005 else: |
|
2006 sb.showMessage(self.trUtf8('Error while printing'), 2000) |
|
2007 QApplication.processEvents() |
|
2008 else: |
|
2009 sb.showMessage(self.trUtf8('Printing aborted'), 2000) |
|
2010 QApplication.processEvents() |
|
2011 |
|
2012 def printPreviewFile(self): |
|
2013 """ |
|
2014 Public slot to show a print preview of the text. |
|
2015 """ |
|
2016 from PyQt4.QtGui import QPrintPreviewDialog |
|
2017 |
|
2018 printer = Printer(mode = QPrinter.HighResolution) |
|
2019 fn = self.getFileName() |
|
2020 if fn is not None: |
|
2021 printer.setDocName(os.path.basename(fn)) |
|
2022 else: |
|
2023 printer.setDocName(self.noName) |
|
2024 preview = QPrintPreviewDialog(printer, self) |
|
2025 self.connect(preview, SIGNAL("paintRequested(QPrinter*)"), self.__printPreview) |
|
2026 preview.exec_() |
|
2027 |
|
2028 def __printPreview(self, printer): |
|
2029 """ |
|
2030 Private slot to generate a print preview. |
|
2031 |
|
2032 @param printer reference to the printer object (QScintilla.Printer.Printer) |
|
2033 """ |
|
2034 printer.printRange(self) |
|
2035 |
|
2036 ############################################################################ |
|
2037 ## Task handling methods below |
|
2038 ############################################################################ |
|
2039 |
|
2040 def hasTaskMarkers(self): |
|
2041 """ |
|
2042 Public method to determine, if this editor contains any task markers. |
|
2043 |
|
2044 @return flag indicating the presence of task markers (boolean) |
|
2045 """ |
|
2046 return self.__hasTaskMarkers |
|
2047 |
|
2048 def nextTask(self): |
|
2049 """ |
|
2050 Public slot to handle the 'Next task' context menu action. |
|
2051 """ |
|
2052 line, index = self.getCursorPosition() |
|
2053 if line == self.lines()-1: |
|
2054 line = 0 |
|
2055 else: |
|
2056 line += 1 |
|
2057 taskline = self.markerFindNext(line, 1 << self.taskmarker) |
|
2058 if taskline < 0: |
|
2059 # wrap around |
|
2060 taskline = self.markerFindNext(0, 1 << self.taskmarker) |
|
2061 if taskline >= 0: |
|
2062 self.setCursorPosition(taskline, 0) |
|
2063 self.ensureLineVisible(taskline) |
|
2064 |
|
2065 def previousTask(self): |
|
2066 """ |
|
2067 Public slot to handle the 'Previous task' context menu action. |
|
2068 """ |
|
2069 line, index = self.getCursorPosition() |
|
2070 if line == 0: |
|
2071 line = self.lines()-1 |
|
2072 else: |
|
2073 line -= 1 |
|
2074 taskline = self.markerFindPrevious(line, 1 << self.taskmarker) |
|
2075 if taskline < 0: |
|
2076 # wrap around |
|
2077 taskline = self.markerFindPrevious(self.lines() - 1, 1 << self.taskmarker) |
|
2078 if taskline >= 0: |
|
2079 self.setCursorPosition(taskline, 0) |
|
2080 self.ensureLineVisible(taskline) |
|
2081 |
|
2082 def extractTasks(self): |
|
2083 """ |
|
2084 Public slot to extract all tasks. |
|
2085 """ |
|
2086 todoMarkers = Preferences.getTasks("TasksMarkers").split() |
|
2087 bugfixMarkers = Preferences.getTasks("TasksMarkersBugfix").split() |
|
2088 txtList = self.text().split(self.getLineSeparator()) |
|
2089 |
|
2090 # clear all task markers and tasks |
|
2091 self.markerDeleteAll(self.taskmarker) |
|
2092 self.taskViewer.clearFileTasks(self.fileName) |
|
2093 self.__hasTaskMarkers = False |
|
2094 |
|
2095 # now search tasks and record them |
|
2096 lineIndex = -1 |
|
2097 for line in txtList: |
|
2098 lineIndex += 1 |
|
2099 shouldContinue = False |
|
2100 # normal tasks first |
|
2101 for tasksMarker in todoMarkers: |
|
2102 index = line.find(tasksMarker) |
|
2103 if index > -1: |
|
2104 task = line[index:] |
|
2105 self.markerAdd(lineIndex, self.taskmarker) |
|
2106 self.taskViewer.addFileTask(task, self.fileName, lineIndex + 1, False) |
|
2107 self.__hasTaskMarkers = True |
|
2108 shouldContinue = True |
|
2109 break |
|
2110 if shouldContinue: |
|
2111 continue |
|
2112 |
|
2113 # bugfix tasks second |
|
2114 for tasksMarker in bugfixMarkers: |
|
2115 index = line.find(tasksMarker) |
|
2116 if index > -1: |
|
2117 task = line[index:] |
|
2118 self.markerAdd(lineIndex, self.taskmarker) |
|
2119 self.taskViewer.addFileTask(task, self.fileName, lineIndex+1, True) |
|
2120 self.__hasTaskMarkers = True |
|
2121 break |
|
2122 self.emit(SIGNAL('taskMarkersUpdated'), self) |
|
2123 |
|
2124 ############################################################################ |
|
2125 ## File handling methods below |
|
2126 ############################################################################ |
|
2127 |
|
2128 def checkDirty(self): |
|
2129 """ |
|
2130 Public method to check dirty status and open a message window. |
|
2131 |
|
2132 @return flag indicating successful reset of the dirty flag (boolean) |
|
2133 """ |
|
2134 if self.isModified(): |
|
2135 fn = self.fileName |
|
2136 if fn is None: |
|
2137 fn = self.noName |
|
2138 res = QMessageBox.warning(self.vm, |
|
2139 self.trUtf8("File Modified"), |
|
2140 self.trUtf8("<p>The file <b>{0}</b> has unsaved changes.</p>") |
|
2141 .format(fn), |
|
2142 QMessageBox.StandardButtons(\ |
|
2143 QMessageBox.Abort | \ |
|
2144 QMessageBox.Discard | \ |
|
2145 QMessageBox.Save), |
|
2146 QMessageBox.Save) |
|
2147 if res == QMessageBox.Save: |
|
2148 ok, newName = self.saveFile() |
|
2149 if ok: |
|
2150 self.vm.setEditorName(self, newName) |
|
2151 return ok |
|
2152 elif res == QMessageBox.Abort or res == QMessageBox.Cancel: |
|
2153 return False |
|
2154 |
|
2155 return True |
|
2156 |
|
2157 def revertToUnmodified(self): |
|
2158 """ |
|
2159 Public method to revert back to the last saved state. |
|
2160 """ |
|
2161 undo_ = True |
|
2162 while self.isModified(): |
|
2163 if undo_: |
|
2164 # try undo first |
|
2165 if self.isUndoAvailable(): |
|
2166 self.undo() |
|
2167 else: |
|
2168 undo_ = False |
|
2169 else: |
|
2170 # try redo next |
|
2171 if self.isRedoAvailable(): |
|
2172 self.redo() |
|
2173 else: |
|
2174 break |
|
2175 # Couldn't find the unmodified state |
|
2176 |
|
2177 def readLine0(self, fn, createIt = False): |
|
2178 """ |
|
2179 Public slot to read the first line from a file. |
|
2180 |
|
2181 @param fn filename to read from (string) |
|
2182 @param createIt flag indicating the creation of a new file, if the given |
|
2183 one doesn't exist (boolean) |
|
2184 @return first line of the file (string) |
|
2185 """ |
|
2186 try: |
|
2187 if createIt and not os.path.exists(fn): |
|
2188 f = open(fn, "wb") |
|
2189 f.close() |
|
2190 f = open(fn, 'rb') |
|
2191 except IOError: |
|
2192 QMessageBox.critical(self.vm, self.trUtf8('Open File'), |
|
2193 self.trUtf8('<p>The file <b>{0}</b> could not be opened.</p>') |
|
2194 .format(fn)) |
|
2195 raise |
|
2196 |
|
2197 txt = f.readline() |
|
2198 f.close() |
|
2199 return txt |
|
2200 |
|
2201 def readFile(self, fn, createIt = False): |
|
2202 """ |
|
2203 Public slot to read the text from a file. |
|
2204 |
|
2205 @param fn filename to read from (string) |
|
2206 @param createIt flag indicating the creation of a new file, if the given |
|
2207 one doesn't exist (boolean) |
|
2208 """ |
|
2209 try: |
|
2210 if createIt and not os.path.exists(fn): |
|
2211 f = open(fn, "wb") |
|
2212 f.close() |
|
2213 f = open(fn, 'rb') |
|
2214 except IOError: |
|
2215 QMessageBox.critical(self.vm, self.trUtf8('Open File'), |
|
2216 self.trUtf8('<p>The file <b>{0}</b> could not be opened.</p>') |
|
2217 .format(fn)) |
|
2218 raise |
|
2219 |
|
2220 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2221 |
|
2222 if fn.endswith('.ts') or fn.endswith('.ui'): |
|
2223 # special treatment for Qt-Linguist and Qt-Designer files |
|
2224 txt = f.read() |
|
2225 self.encoding = 'latin-1' |
|
2226 else: |
|
2227 txt, self.encoding = Utilities.decode(f.read()) |
|
2228 f.close() |
|
2229 fileEol = self.detectEolString(txt) |
|
2230 |
|
2231 modified = False |
|
2232 if (not Preferences.getEditor("TabForIndentation")) and \ |
|
2233 Preferences.getEditor("ConvertTabsOnLoad") and \ |
|
2234 not (self.lexer_ and \ |
|
2235 self.lexer_.alwaysKeepTabs()): |
|
2236 txtExpanded = txt.expandtabs(Preferences.getEditor("TabWidth")) |
|
2237 if txtExpanded != txt: |
|
2238 modified = True |
|
2239 txt = txtExpanded |
|
2240 del txtExpanded |
|
2241 |
|
2242 self.setText(txt) |
|
2243 |
|
2244 # perform automatic eol conversion |
|
2245 if Preferences.getEditor("AutomaticEOLConversion"): |
|
2246 self.convertEols(self.eolMode()) |
|
2247 else: |
|
2248 self.setEolModeByEolString(fileEol) |
|
2249 |
|
2250 self.extractTasks() |
|
2251 |
|
2252 QApplication.restoreOverrideCursor() |
|
2253 |
|
2254 self.setModified(modified) |
|
2255 self.lastModified = QFileInfo(self.fileName).lastModified() |
|
2256 |
|
2257 def setEolModeByEolString(self, eolStr): |
|
2258 """ |
|
2259 Public method to set the eol mode given the eol string. |
|
2260 |
|
2261 @param eolStr eol string (string) |
|
2262 """ |
|
2263 if eolStr == '\r\n': |
|
2264 self.setEolMode(QsciScintilla.EolMode(QsciScintilla.EolWindows)) |
|
2265 elif eolStr == '\n': |
|
2266 self.setEolMode(QsciScintilla.EolMode(QsciScintilla.EolUnix)) |
|
2267 elif eolStr == '\r': |
|
2268 self.setEolMode(QsciScintilla.EolMode(QsciScintilla.EolMac)) |
|
2269 self.__eolChanged() |
|
2270 |
|
2271 def __removeTrailingWhitespace(self): |
|
2272 """ |
|
2273 Private method to remove trailing whitespace. |
|
2274 """ |
|
2275 searchRE = r"[ \t]+$" # whitespace at the end of a line |
|
2276 |
|
2277 ok = self.findFirstTarget(searchRE, True, False, False, 0, 0) |
|
2278 self.beginUndoAction() |
|
2279 while ok: |
|
2280 self.replaceTarget("") |
|
2281 ok = self.findNextTarget() |
|
2282 self.endUndoAction() |
|
2283 |
|
2284 def writeFile(self, fn): |
|
2285 """ |
|
2286 Public slot to write the text to a file. |
|
2287 |
|
2288 @param fn filename to write to (string) |
|
2289 @return flag indicating success |
|
2290 """ |
|
2291 if Preferences.getEditor("StripTrailingWhitespace"): |
|
2292 self.__removeTrailingWhitespace() |
|
2293 |
|
2294 txt = self.text() |
|
2295 # work around glitch in scintilla: always make sure, |
|
2296 # that the last line is terminated properly |
|
2297 eol = self.getLineSeparator() |
|
2298 if eol: |
|
2299 if len(txt) >= len(eol): |
|
2300 if txt[-len(eol):] != eol: |
|
2301 txt += eol |
|
2302 else: |
|
2303 txt += eol |
|
2304 try: |
|
2305 txt, self.encoding = Utilities.encode(txt, self.encoding) |
|
2306 except Utilities.CodingError, e: |
|
2307 QMessageBox.critical(self, self.trUtf8('Save File'), |
|
2308 self.trUtf8('<p>The file <b>{0}</b> could not be saved.<br/>' |
|
2309 'Reason: {1}</p>') |
|
2310 .format(fn, unicode(e))) |
|
2311 return False |
|
2312 |
|
2313 # create a backup file, if the option is set |
|
2314 createBackup = Preferences.getEditor("CreateBackupFile") |
|
2315 if createBackup: |
|
2316 if os.path.islink(fn): |
|
2317 fn = os.path.realpath(fn) |
|
2318 bfn = '%s~' % fn |
|
2319 try: |
|
2320 permissions = os.stat(fn).st_mode |
|
2321 perms_valid = True |
|
2322 except EnvironmentError: |
|
2323 # if there was an error, ignore it |
|
2324 perms_valid = False |
|
2325 try: |
|
2326 os.remove(bfn) |
|
2327 except EnvironmentError: |
|
2328 # if there was an error, ignore it |
|
2329 pass |
|
2330 try: |
|
2331 os.rename(fn, bfn) |
|
2332 except EnvironmentError: |
|
2333 # if there was an error, ignore it |
|
2334 pass |
|
2335 |
|
2336 # now write text to the file fn |
|
2337 try: |
|
2338 f = open(fn, 'wb') |
|
2339 f.write(txt) |
|
2340 f.close() |
|
2341 if createBackup and perms_valid: |
|
2342 os.chmod(fn, permissions) |
|
2343 return True |
|
2344 except IOError, why: |
|
2345 QMessageBox.critical(self, self.trUtf8('Save File'), |
|
2346 self.trUtf8('<p>The file <b>{0}</b> could not be saved.<br/>' |
|
2347 'Reason: {1}</p>') |
|
2348 .format(fn, unicode(why))) |
|
2349 return False |
|
2350 |
|
2351 def saveFile(self, saveas = False, path = None): |
|
2352 """ |
|
2353 Public slot to save the text to a file. |
|
2354 |
|
2355 @param saveas flag indicating a 'save as' action (boolean) |
|
2356 @param path directory to save the file in (string) |
|
2357 @return tuple of two values (boolean, string) giving a success indicator and |
|
2358 the name of the saved file |
|
2359 """ |
|
2360 if not saveas and not self.isModified(): |
|
2361 return (False, None) # do nothing if text wasn't changed |
|
2362 |
|
2363 newName = None |
|
2364 if saveas or self.fileName is None: |
|
2365 saveas = True |
|
2366 if not path and self.fileName is not None: |
|
2367 path = os.path.dirname(self.fileName) |
|
2368 if path is None: |
|
2369 path = "" |
|
2370 defaultFilter = Preferences.getEditor("DefaultSaveFilter") |
|
2371 fn, selectedFilter = QFileDialog.getSaveFileNameAndFilter( |
|
2372 self, |
|
2373 self.trUtf8("Save File"), |
|
2374 path, |
|
2375 Lexers.getSaveFileFiltersList(True, True), |
|
2376 defaultFilter, |
|
2377 QFileDialog.Options(QFileDialog.DontConfirmOverwrite)) |
|
2378 |
|
2379 if fn: |
|
2380 ext = QFileInfo(fn).suffix() |
|
2381 if not ext: |
|
2382 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
2383 if ex: |
|
2384 fn += ex |
|
2385 if QFileInfo(fn).exists(): |
|
2386 res = QMessageBox.warning(self, |
|
2387 self.trUtf8("Save File"), |
|
2388 self.trUtf8("<p>The file <b>{0}</b> already exists.</p>") |
|
2389 .format(fn), |
|
2390 QMessageBox.StandardButtons(\ |
|
2391 QMessageBox.Abort | \ |
|
2392 QMessageBox.Save), |
|
2393 QMessageBox.Abort) |
|
2394 if res == QMessageBox.Abort or res == QMessageBox.Cancel: |
|
2395 return (False, None) |
|
2396 fn = Utilities.toNativeSeparators(fn) |
|
2397 newName = fn |
|
2398 else: |
|
2399 return (False, None) |
|
2400 else: |
|
2401 fn = self.fileName |
|
2402 |
|
2403 self.emit(SIGNAL('editorAboutToBeSaved'), self.fileName) |
|
2404 if self.writeFile(fn): |
|
2405 if saveas: |
|
2406 self.__clearBreakpoints(self.fileName) |
|
2407 self.fileName = fn |
|
2408 self.setModified(False) |
|
2409 self.setReadOnly(False) |
|
2410 self.setWindowTitle(self.fileName) |
|
2411 if self.lexer_ is None and not self.__lexerReset: |
|
2412 self.setLanguage(self.fileName) |
|
2413 |
|
2414 if saveas: |
|
2415 self.isResourcesFile = self.fileName.endswith(".qrc") |
|
2416 self.__initContextMenu() |
|
2417 self.emit(SIGNAL('editorRenamed'), self.fileName) |
|
2418 self.lastModified = QFileInfo(self.fileName).lastModified() |
|
2419 if newName is not None: |
|
2420 self.vm.addToRecentList(newName) |
|
2421 self.emit(SIGNAL('editorSaved'), self.fileName) |
|
2422 self.__autoSyntaxCheck() |
|
2423 self.extractTasks() |
|
2424 return (True, self.fileName) |
|
2425 else: |
|
2426 self.lastModified = QFileInfo(fn).lastModified() |
|
2427 return (False, None) |
|
2428 |
|
2429 def saveFileAs(self, path = None): |
|
2430 """ |
|
2431 Public slot to save a file with a new name. |
|
2432 |
|
2433 @param path directory to save the file in (string) |
|
2434 @return tuple of two values (boolean, string) giving a success indicator and |
|
2435 the name of the saved file |
|
2436 """ |
|
2437 return self.saveFile(True, path) |
|
2438 |
|
2439 def handleRenamed(self, fn): |
|
2440 """ |
|
2441 Public slot to handle the editorRenamed signal. |
|
2442 |
|
2443 @param fn filename to be set for the editor (string). |
|
2444 """ |
|
2445 self.__clearBreakpoints(fn) |
|
2446 |
|
2447 self.fileName = fn |
|
2448 self.setWindowTitle(self.fileName) |
|
2449 |
|
2450 if self.lexer_ is None: |
|
2451 self.setLanguage(self.fileName) |
|
2452 |
|
2453 self.lastModified = QFileInfo(self.fileName).lastModified() |
|
2454 self.vm.setEditorName(self, self.fileName) |
|
2455 self.__updateReadOnly(True) |
|
2456 |
|
2457 def fileRenamed(self, fn): |
|
2458 """ |
|
2459 Public slot to handle the editorRenamed signal. |
|
2460 |
|
2461 @param fn filename to be set for the editor (string). |
|
2462 """ |
|
2463 self.handleRenamed(fn) |
|
2464 if not self.inFileRenamed: |
|
2465 self.inFileRenamed = True |
|
2466 self.emit(SIGNAL('editorRenamed'), self.fileName) |
|
2467 self.inFileRenamed = False |
|
2468 |
|
2469 ############################################################################ |
|
2470 ## Utility methods below |
|
2471 ############################################################################ |
|
2472 |
|
2473 def ensureVisible(self, line): |
|
2474 """ |
|
2475 Public slot to ensure, that the specified line is visible. |
|
2476 |
|
2477 @param line line number to make visible |
|
2478 """ |
|
2479 self.ensureLineVisible(line-1) |
|
2480 |
|
2481 def ensureVisibleTop(self, line): |
|
2482 """ |
|
2483 Public slot to ensure, that the specified line is visible at the top |
|
2484 of the editor. |
|
2485 |
|
2486 @param line line number to make visible |
|
2487 """ |
|
2488 topLine = self.firstVisibleLine() |
|
2489 linesToScroll = line - topLine |
|
2490 self.scrollVertical(linesToScroll) |
|
2491 |
|
2492 def __marginClicked(self, margin, line, modifiers): |
|
2493 """ |
|
2494 Private slot to handle the marginClicked signal. |
|
2495 |
|
2496 @param margin id of the clicked margin (integer) |
|
2497 @param line line number of the click (integer) |
|
2498 @param modifiers keyboard modifiers (Qt.KeyboardModifiers) |
|
2499 """ |
|
2500 if self.__unifiedMargins: |
|
2501 if margin == 1: |
|
2502 if modifiers & Qt.KeyboardModifiers(Qt.ShiftModifier): |
|
2503 if self.marginMenuActs["LMBbreakpoints"].isChecked(): |
|
2504 self.toggleBookmark(line + 1) |
|
2505 else: |
|
2506 self.__toggleBreakpoint(line + 1) |
|
2507 elif modifiers & Qt.KeyboardModifiers(Qt.ControlModifier): |
|
2508 if self.markersAtLine(line) & (1 << self.syntaxerror): |
|
2509 self.__showSyntaxError(line) |
|
2510 else: |
|
2511 if self.marginMenuActs["LMBbreakpoints"].isChecked(): |
|
2512 self.__toggleBreakpoint(line + 1) |
|
2513 else: |
|
2514 self.toggleBookmark(line + 1) |
|
2515 else: |
|
2516 if margin == self.__bmMargin: |
|
2517 self.toggleBookmark(line + 1) |
|
2518 elif margin == self.__bpMargin: |
|
2519 self.__toggleBreakpoint(line + 1) |
|
2520 elif margin == self.__indicMargin: |
|
2521 if self.markersAtLine(line) & (1 << self.syntaxerror): |
|
2522 self.__showSyntaxError(line) |
|
2523 |
|
2524 def handleMonospacedEnable(self): |
|
2525 """ |
|
2526 Private slot to handle the Use Monospaced Font context menu entry. |
|
2527 """ |
|
2528 if self.menuActs["MonospacedFont"].isChecked(): |
|
2529 self.setMonospaced(True) |
|
2530 else: |
|
2531 if self.lexer_: |
|
2532 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
|
2533 self.lexer_.initProperties() |
|
2534 self.setMonospaced(False) |
|
2535 self.__setMarginsDisplay() |
|
2536 |
|
2537 def getWordBoundaries(self, line, index, useWordChars = True): |
|
2538 """ |
|
2539 Public method to get the word boundaries at a position. |
|
2540 |
|
2541 @param line number of line to look at (int) |
|
2542 @param index position to look at (int) |
|
2543 @keyparam useWordChars flag indicating to use the wordCharacters |
|
2544 method (boolean) |
|
2545 @return tuple with start and end indices of the word at the position |
|
2546 (integer, integer) |
|
2547 """ |
|
2548 text = self.text(line) |
|
2549 if self.caseSensitive(): |
|
2550 cs = Qt.CaseSensitive |
|
2551 else: |
|
2552 cs = Qt.CaseInsensitive |
|
2553 wc = self.wordCharacters() |
|
2554 if wc is None or not useWordChars: |
|
2555 regExp = QRegExp('[^\w_]', cs) |
|
2556 else: |
|
2557 regExp = QRegExp('[^%s]' % re.escape(wc), cs) |
|
2558 start = regExp.lastIndexIn(text, index) + 1 |
|
2559 end = regExp.indexIn(text, index) |
|
2560 if start == end + 1 and index > 0: |
|
2561 # we are on a word boundary, try again |
|
2562 start = regExp.lastIndexIn(text, index - 1) + 1 |
|
2563 if start == -1: |
|
2564 start = 0 |
|
2565 if end == -1: |
|
2566 end = len(text) |
|
2567 |
|
2568 return (start, end) |
|
2569 |
|
2570 def getWord(self, line, index, direction = 0, useWordChars = True): |
|
2571 """ |
|
2572 Public method to get the word at a position. |
|
2573 |
|
2574 @param line number of line to look at (int) |
|
2575 @param index position to look at (int) |
|
2576 @param direction direction to look in (0 = whole word, 1 = left, 2 = right) |
|
2577 @keyparam useWordChars flag indicating to use the wordCharacters |
|
2578 method (boolean) |
|
2579 @return the word at that position (string) |
|
2580 """ |
|
2581 start, end = self.getWordBoundaries(line, index, useWordChars) |
|
2582 if direction == 1: |
|
2583 end = index |
|
2584 elif direction == 2: |
|
2585 start = index |
|
2586 if end > start: |
|
2587 text = self.text(line) |
|
2588 word = text[start:end] |
|
2589 else: |
|
2590 word = '' |
|
2591 return word |
|
2592 |
|
2593 def getWordLeft(self, line, index): |
|
2594 """ |
|
2595 Public method to get the word to the left of a position. |
|
2596 |
|
2597 @param line number of line to look at (int) |
|
2598 @param index position to look at (int) |
|
2599 @return the word to the left of that position (string) |
|
2600 """ |
|
2601 return self.getWord(line, index, 1) |
|
2602 |
|
2603 def getWordRight(self, line, index): |
|
2604 """ |
|
2605 Public method to get the word to the right of a position. |
|
2606 |
|
2607 @param line number of line to look at (int) |
|
2608 @param index position to look at (int) |
|
2609 @return the word to the right of that position (string) |
|
2610 """ |
|
2611 return self.getWord(line, index, 2) |
|
2612 |
|
2613 def getCurrentWord(self): |
|
2614 """ |
|
2615 Public method to get the word at the current position. |
|
2616 |
|
2617 @return the word at that current position (string) |
|
2618 """ |
|
2619 line, index = self.getCursorPosition() |
|
2620 return self.getWord(line, index) |
|
2621 |
|
2622 def selectWord(self, line, index): |
|
2623 """ |
|
2624 Public method to select the word at a position. |
|
2625 |
|
2626 @param line number of line to look at (int) |
|
2627 @param index position to look at (int) |
|
2628 """ |
|
2629 start, end = self.getWordBoundaries(line, index) |
|
2630 self.setSelection(line, start, line, end) |
|
2631 |
|
2632 def selectCurrentWord(self): |
|
2633 """ |
|
2634 Public method to select the current word. |
|
2635 """ |
|
2636 line, index = self.getCursorPosition() |
|
2637 self.selectWord(line, index) |
|
2638 |
|
2639 def __getCharacter(self, pos): |
|
2640 """ |
|
2641 Private method to get the character to the left of the current position |
|
2642 in the current line. |
|
2643 |
|
2644 @param pos position to get character at (integer) |
|
2645 @return requested character or "", if there are no more (string) and |
|
2646 the next position (i.e. pos - 1) |
|
2647 """ |
|
2648 if pos <= 0: |
|
2649 return "", pos |
|
2650 |
|
2651 pos = self.positionBefore(pos) |
|
2652 ch = self.charAt(pos) |
|
2653 |
|
2654 # Don't go past the end of the previous line |
|
2655 if ch == '\n' or ch == '\r': |
|
2656 return "", pos |
|
2657 |
|
2658 return ch, pos |
|
2659 |
|
2660 def getSearchText(self, selectionOnly = False): |
|
2661 """ |
|
2662 Public method to determine the selection or the current word for the next |
|
2663 search operation. |
|
2664 |
|
2665 @param selectionOnly flag indicating that only selected text should be |
|
2666 returned (boolean) |
|
2667 @return selection or current word (string) |
|
2668 """ |
|
2669 if self.hasSelectedText(): |
|
2670 text = self.selectedText() |
|
2671 if '\r' in text or '\n' in text: |
|
2672 # the selection contains at least a newline, it is |
|
2673 # unlikely to be the expression to search for |
|
2674 return '' |
|
2675 |
|
2676 return text |
|
2677 |
|
2678 if not selectionOnly: |
|
2679 # no selected text, determine the word at the current position |
|
2680 return self.getCurrentWord() |
|
2681 |
|
2682 return '' |
|
2683 |
|
2684 def setSearchIndicator(self, startPos, indicLength): |
|
2685 """ |
|
2686 Public method to set a search indicator for the given range. |
|
2687 |
|
2688 @param startPos start position of the indicator (integer) |
|
2689 @param indicLength length of the indicator (integer) |
|
2690 """ |
|
2691 self.setIndicatorRange(self.searchIndicator, startPos, indicLength) |
|
2692 |
|
2693 def clearSearchIndicators(self): |
|
2694 """ |
|
2695 Public method to clear all search indicators. |
|
2696 """ |
|
2697 self.clearAllIndicators(self.searchIndicator) |
|
2698 self.__markedText = "" |
|
2699 |
|
2700 def __markOccurrences(self): |
|
2701 """ |
|
2702 Private method to mark all occurrences of the current word. |
|
2703 """ |
|
2704 word = self.getCurrentWord() |
|
2705 if not word: |
|
2706 self.clearSearchIndicators() |
|
2707 return |
|
2708 |
|
2709 if self.__markedText == word: |
|
2710 return |
|
2711 |
|
2712 self.clearSearchIndicators() |
|
2713 ok = self.findFirstTarget(word, False, self.caseSensitive(), True, 0, 0) |
|
2714 while ok: |
|
2715 tgtPos, tgtLen = self.getFoundTarget() |
|
2716 self.setSearchIndicator(tgtPos, tgtLen) |
|
2717 ok = self.findNextTarget() |
|
2718 self.__markedText = word |
|
2719 |
|
2720 ############################################################################ |
|
2721 ## Comment handling methods below |
|
2722 ############################################################################ |
|
2723 |
|
2724 def commentLine(self): |
|
2725 """ |
|
2726 Public slot to comment the current line. |
|
2727 """ |
|
2728 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
|
2729 return |
|
2730 |
|
2731 line, index = self.getCursorPosition() |
|
2732 self.beginUndoAction() |
|
2733 if Preferences.getEditor("CommentColumn0"): |
|
2734 self.insertAt(self.lexer_.commentStr(), line, 0) |
|
2735 else: |
|
2736 self.insertAt(self.lexer_.commentStr(), line, self.indentation(line)) |
|
2737 self.endUndoAction() |
|
2738 |
|
2739 def uncommentLine(self): |
|
2740 """ |
|
2741 Public slot to uncomment the current line. |
|
2742 """ |
|
2743 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
|
2744 return |
|
2745 |
|
2746 commentStr = self.lexer_.commentStr() |
|
2747 line, index = self.getCursorPosition() |
|
2748 |
|
2749 # check if line starts with our comment string (i.e. was commented |
|
2750 # by our comment...() slots |
|
2751 if not self.text(line).strip().startswith(commentStr): |
|
2752 return |
|
2753 |
|
2754 # now remove the comment string |
|
2755 self.beginUndoAction() |
|
2756 if Preferences.getEditor("CommentColumn0"): |
|
2757 self.setSelection(line, 0, line, len(commentStr)) |
|
2758 else: |
|
2759 self.setSelection(line, self.indentation(line), |
|
2760 line, self.indentation(line) + len(commentStr)) |
|
2761 self.removeSelectedText() |
|
2762 self.endUndoAction() |
|
2763 |
|
2764 def commentSelection(self): |
|
2765 """ |
|
2766 Public slot to comment the current selection. |
|
2767 """ |
|
2768 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
|
2769 return |
|
2770 |
|
2771 if not self.hasSelectedText(): |
|
2772 return |
|
2773 |
|
2774 commentStr = self.lexer_.commentStr() |
|
2775 |
|
2776 # get the selection boundaries |
|
2777 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
|
2778 if indexTo == 0: |
|
2779 endLine = lineTo - 1 |
|
2780 else: |
|
2781 endLine = lineTo |
|
2782 |
|
2783 self.beginUndoAction() |
|
2784 # iterate over the lines |
|
2785 for line in range(lineFrom, endLine + 1): |
|
2786 if Preferences.getEditor("CommentColumn0"): |
|
2787 self.insertAt(commentStr, line, 0) |
|
2788 else: |
|
2789 self.insertAt(commentStr, line, self.indentation(line)) |
|
2790 |
|
2791 # change the selection accordingly |
|
2792 self.setSelection(lineFrom, 0, endLine + 1, 0) |
|
2793 self.endUndoAction() |
|
2794 |
|
2795 def uncommentSelection(self): |
|
2796 """ |
|
2797 Public slot to uncomment the current selection. |
|
2798 """ |
|
2799 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
|
2800 return |
|
2801 |
|
2802 if not self.hasSelectedText(): |
|
2803 return |
|
2804 |
|
2805 commentStr = self.lexer_.commentStr() |
|
2806 |
|
2807 # get the selection boundaries |
|
2808 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
|
2809 if indexTo == 0: |
|
2810 endLine = lineTo - 1 |
|
2811 else: |
|
2812 endLine = lineTo |
|
2813 |
|
2814 self.beginUndoAction() |
|
2815 # iterate over the lines |
|
2816 for line in range(lineFrom, endLine+1): |
|
2817 # check if line starts with our comment string (i.e. was commented |
|
2818 # by our comment...() slots |
|
2819 if not self.text(line).strip().startswith(commentStr): |
|
2820 continue |
|
2821 |
|
2822 if Preferences.getEditor("CommentColumn0"): |
|
2823 self.setSelection(line, 0, line, len(commentStr)) |
|
2824 else: |
|
2825 self.setSelection(line, self.indentation(line), |
|
2826 line, self.indentation(line) + len(commentStr)) |
|
2827 self.removeSelectedText() |
|
2828 |
|
2829 # adjust selection start |
|
2830 if line == lineFrom: |
|
2831 indexFrom -= len(commentStr) |
|
2832 if indexFrom < 0: |
|
2833 indexFrom = 0 |
|
2834 |
|
2835 # adjust selection end |
|
2836 if line == lineTo: |
|
2837 indexTo -= len(commentStr) |
|
2838 if indexTo < 0: |
|
2839 indexTo = 0 |
|
2840 |
|
2841 # change the selection accordingly |
|
2842 self.setSelection(lineFrom, indexFrom, lineTo, indexTo) |
|
2843 self.endUndoAction() |
|
2844 |
|
2845 def commentLineOrSelection(self): |
|
2846 """ |
|
2847 Public slot to comment the current line or current selection. |
|
2848 """ |
|
2849 if self.hasSelectedText(): |
|
2850 self.commentSelection() |
|
2851 else: |
|
2852 self.commentLine() |
|
2853 |
|
2854 def uncommentLineOrSelection(self): |
|
2855 """ |
|
2856 Public slot to uncomment the current line or current selection. |
|
2857 """ |
|
2858 if self.hasSelectedText(): |
|
2859 self.uncommentSelection() |
|
2860 else: |
|
2861 self.uncommentLine() |
|
2862 |
|
2863 def streamCommentLine(self): |
|
2864 """ |
|
2865 Public slot to stream comment the current line. |
|
2866 """ |
|
2867 if self.lexer_ is None or not self.lexer_.canStreamComment(): |
|
2868 return |
|
2869 |
|
2870 commentStr = self.lexer_.streamCommentStr() |
|
2871 line, index = self.getCursorPosition() |
|
2872 |
|
2873 self.beginUndoAction() |
|
2874 self.insertAt(commentStr['end'], line, self.lineLength(line)) |
|
2875 self.insertAt(commentStr['start'], line, 0) |
|
2876 self.endUndoAction() |
|
2877 |
|
2878 def streamCommentSelection(self): |
|
2879 """ |
|
2880 Public slot to comment the current selection. |
|
2881 """ |
|
2882 if self.lexer_ is None or not self.lexer_.canStreamComment(): |
|
2883 return |
|
2884 |
|
2885 if not self.hasSelectedText(): |
|
2886 return |
|
2887 |
|
2888 commentStr = self.lexer_.streamCommentStr() |
|
2889 |
|
2890 # get the selection boundaries |
|
2891 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
|
2892 if indexTo == 0: |
|
2893 endLine = lineTo - 1 |
|
2894 endIndex = self.lineLength(endLine) |
|
2895 else: |
|
2896 endLine = lineTo |
|
2897 endIndex = indexTo |
|
2898 |
|
2899 self.beginUndoAction() |
|
2900 self.insertAt(commentStr['end'], endLine, endIndex) |
|
2901 self.insertAt(commentStr['start'], lineFrom, indexFrom) |
|
2902 |
|
2903 # change the selection accordingly |
|
2904 if indexTo > 0: |
|
2905 indexTo += len(commentStr['end']) |
|
2906 if lineFrom == endLine: |
|
2907 indexTo += len(commentStr['start']) |
|
2908 self.setSelection(lineFrom, indexFrom, lineTo, indexTo) |
|
2909 self.endUndoAction() |
|
2910 |
|
2911 def streamCommentLineOrSelection(self): |
|
2912 """ |
|
2913 Public slot to stream comment the current line or current selection. |
|
2914 """ |
|
2915 if self.hasSelectedText(): |
|
2916 self.streamCommentSelection() |
|
2917 else: |
|
2918 self.streamCommentLine() |
|
2919 |
|
2920 def boxCommentLine(self): |
|
2921 """ |
|
2922 Public slot to box comment the current line. |
|
2923 """ |
|
2924 if self.lexer_ is None or not self.lexer_.canBoxComment(): |
|
2925 return |
|
2926 |
|
2927 commentStr = self.lexer_.boxCommentStr() |
|
2928 line, index = self.getCursorPosition() |
|
2929 |
|
2930 eol = self.getLineSeparator() |
|
2931 self.beginUndoAction() |
|
2932 self.insertAt(eol, line, self.lineLength(line)) |
|
2933 self.insertAt(commentStr['end'], line + 1, 0) |
|
2934 self.insertAt(commentStr['middle'], line, 0) |
|
2935 self.insertAt(eol, line, 0) |
|
2936 self.insertAt(commentStr['start'], line, 0) |
|
2937 self.endUndoAction() |
|
2938 |
|
2939 def boxCommentSelection(self): |
|
2940 """ |
|
2941 Public slot to box comment the current selection. |
|
2942 """ |
|
2943 if self.lexer_ is None or not self.lexer_.canBoxComment(): |
|
2944 return |
|
2945 |
|
2946 if not self.hasSelectedText(): |
|
2947 return |
|
2948 |
|
2949 commentStr = self.lexer_.boxCommentStr() |
|
2950 |
|
2951 # get the selection boundaries |
|
2952 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
|
2953 if indexTo == 0: |
|
2954 endLine = lineTo - 1 |
|
2955 else: |
|
2956 endLine = lineTo |
|
2957 |
|
2958 self.beginUndoAction() |
|
2959 # iterate over the lines |
|
2960 for line in range(lineFrom, endLine + 1): |
|
2961 self.insertAt(commentStr['middle'], line, 0) |
|
2962 |
|
2963 # now do the comments before and after the selection |
|
2964 eol = self.getLineSeparator() |
|
2965 self.insertAt(eol, endLine, self.lineLength(endLine)) |
|
2966 self.insertAt(commentStr['end'], endLine + 1, 0) |
|
2967 self.insertAt(eol, lineFrom, 0) |
|
2968 self.insertAt(commentStr['start'], lineFrom, 0) |
|
2969 |
|
2970 # change the selection accordingly |
|
2971 self.setSelection(lineFrom, 0, endLine + 3, 0) |
|
2972 self.endUndoAction() |
|
2973 |
|
2974 def boxCommentLineOrSelection(self): |
|
2975 """ |
|
2976 Public slot to box comment the current line or current selection. |
|
2977 """ |
|
2978 if self.hasSelectedText(): |
|
2979 self.boxCommentSelection() |
|
2980 else: |
|
2981 self.boxCommentLine() |
|
2982 |
|
2983 ############################################################################ |
|
2984 ## Indentation handling methods below |
|
2985 ############################################################################ |
|
2986 |
|
2987 def __indentLine(self, indent = True): |
|
2988 """ |
|
2989 Private method to indent or unindent the current line. |
|
2990 |
|
2991 @param indent flag indicating an indent operation (boolean) |
|
2992 <br />If the flag is true, an indent operation is performed. |
|
2993 Otherwise the current line is unindented. |
|
2994 """ |
|
2995 line, index = self.getCursorPosition() |
|
2996 self.beginUndoAction() |
|
2997 if indent: |
|
2998 self.indent(line) |
|
2999 else: |
|
3000 self.unindent(line) |
|
3001 self.endUndoAction() |
|
3002 if indent: |
|
3003 self.setCursorPosition(line, index + self.indentationWidth()) |
|
3004 else: |
|
3005 self.setCursorPosition(line, index - self.indentationWidth()) |
|
3006 |
|
3007 def __indentSelection(self, indent = True): |
|
3008 """ |
|
3009 Private method to indent or unindent the current selection. |
|
3010 |
|
3011 @param indent flag indicating an indent operation (boolean) |
|
3012 <br />If the flag is true, an indent operation is performed. |
|
3013 Otherwise the current line is unindented. |
|
3014 """ |
|
3015 if not self.hasSelectedText(): |
|
3016 return |
|
3017 |
|
3018 # get the selection |
|
3019 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
|
3020 |
|
3021 if indexTo == 0: |
|
3022 endLine = lineTo - 1 |
|
3023 else: |
|
3024 endLine = lineTo |
|
3025 |
|
3026 self.beginUndoAction() |
|
3027 # iterate over the lines |
|
3028 for line in range(lineFrom, endLine + 1): |
|
3029 if indent: |
|
3030 self.indent(line) |
|
3031 else: |
|
3032 self.unindent(line) |
|
3033 self.endUndoAction() |
|
3034 if indent: |
|
3035 if indexTo == 0: |
|
3036 self.setSelection(lineFrom, indexFrom + self.indentationWidth(), |
|
3037 lineTo, 0) |
|
3038 else: |
|
3039 self.setSelection(lineFrom, indexFrom + self.indentationWidth(), |
|
3040 lineTo, indexTo + self.indentationWidth()) |
|
3041 else: |
|
3042 indexStart = indexFrom - self.indentationWidth() |
|
3043 if indexStart < 0: |
|
3044 indexStart = 0 |
|
3045 indexEnd = indexTo - self.indentationWidth() |
|
3046 if indexEnd < 0: |
|
3047 indexEnd = 0 |
|
3048 self.setSelection(lineFrom, indexStart, lineTo, indexEnd) |
|
3049 |
|
3050 def indentLineOrSelection(self): |
|
3051 """ |
|
3052 Public slot to indent the current line or current selection |
|
3053 """ |
|
3054 if self.hasSelectedText(): |
|
3055 self.__indentSelection(True) |
|
3056 else: |
|
3057 self.__indentLine(True) |
|
3058 |
|
3059 def unindentLineOrSelection(self): |
|
3060 """ |
|
3061 Public slot to unindent the current line or current selection. |
|
3062 """ |
|
3063 if self.hasSelectedText(): |
|
3064 self.__indentSelection(False) |
|
3065 else: |
|
3066 self.__indentLine(False) |
|
3067 |
|
3068 def smartIndentLineOrSelection(self): |
|
3069 """ |
|
3070 Public slot to indent current line smartly. |
|
3071 """ |
|
3072 if self.hasSelectedText(): |
|
3073 if self.lexer_ and self.lexer_.hasSmartIndent(): |
|
3074 self.lexer_.smartIndentSelection(self) |
|
3075 else: |
|
3076 self.__indentSelection(True) |
|
3077 else: |
|
3078 if self.lexer_ and self.lexer_.hasSmartIndent(): |
|
3079 self.lexer_.smartIndentLine(self) |
|
3080 else: |
|
3081 self.__indentLine(True) |
|
3082 |
|
3083 def gotoLine(self, line): |
|
3084 """ |
|
3085 Public slot to jump to the beginning of a line. |
|
3086 |
|
3087 @param line line number to go to (integer) |
|
3088 """ |
|
3089 self.setCursorPosition(line - 1, 0) |
|
3090 self.ensureVisible(line) |
|
3091 |
|
3092 ############################################################################ |
|
3093 ## Setup methods below |
|
3094 ############################################################################ |
|
3095 |
|
3096 def readSettings(self): |
|
3097 """ |
|
3098 Public slot to read the settings into our lexer. |
|
3099 """ |
|
3100 # read the lexer settings and reinit the properties |
|
3101 if self.lexer_ is not None: |
|
3102 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
|
3103 self.lexer_.initProperties() |
|
3104 |
|
3105 # initialize the auto indent style of the lexer |
|
3106 ais = self.lexer_.autoIndentStyle() |
|
3107 |
|
3108 # read the typing completer settings |
|
3109 if self.completer is not None: |
|
3110 self.completer.readSettings() |
|
3111 |
|
3112 # set the margins layout |
|
3113 if QSCINTILLA_VERSION() >= 0x020301: |
|
3114 self.__unifiedMargins = Preferences.getEditor("UnifiedMargins") |
|
3115 |
|
3116 # set the line marker colours |
|
3117 self.__setLineMarkerColours() |
|
3118 |
|
3119 # set the text display |
|
3120 self.__setTextDisplay() |
|
3121 |
|
3122 # set margin 0 and 2 configuration |
|
3123 self.__setMarginsDisplay() |
|
3124 |
|
3125 # set the autocompletion and calltips function |
|
3126 self.__setAutoCompletion() |
|
3127 self.__setCallTips() |
|
3128 |
|
3129 # set the autosave flags |
|
3130 self.autosaveEnabled = Preferences.getEditor("AutosaveInterval") > 0 |
|
3131 |
|
3132 if Preferences.getEditor("MiniContextMenu") != self.miniMenu: |
|
3133 # regenerate context menu |
|
3134 self.__initContextMenu() |
|
3135 else: |
|
3136 # set checked context menu items |
|
3137 self.menuActs["AutoCompletionEnable"].setChecked(\ |
|
3138 self.autoCompletionThreshold() != -1) |
|
3139 self.menuActs["MonospacedFont"].setChecked(\ |
|
3140 self.useMonospaced) |
|
3141 self.menuActs["AutosaveEnable"].setChecked(\ |
|
3142 self.autosaveEnabled and not self.autosaveManuallyDisabled) |
|
3143 |
|
3144 # regenerate the margins context menu(s) |
|
3145 self.__initContextMenuMargins() |
|
3146 |
|
3147 if Preferences.getEditor("MarkOccurrencesEnabled"): |
|
3148 self.__markOccurrencesTimer.setInterval( |
|
3149 Preferences.getEditor("MarkOccurrencesTimeout")) |
|
3150 else: |
|
3151 self.__markOccurrencesTimer.stop() |
|
3152 self.clearSearchIndicators() |
|
3153 |
|
3154 def __setLineMarkerColours(self): |
|
3155 """ |
|
3156 Private method to set the line marker colours. |
|
3157 """ |
|
3158 self.setMarkerForegroundColor(Preferences.getEditorColour("CurrentMarker"), |
|
3159 self.currentline) |
|
3160 self.setMarkerBackgroundColor(Preferences.getEditorColour("CurrentMarker"), |
|
3161 self.currentline) |
|
3162 self.setMarkerForegroundColor(Preferences.getEditorColour("ErrorMarker"), |
|
3163 self.errorline) |
|
3164 self.setMarkerBackgroundColor(Preferences.getEditorColour("ErrorMarker"), |
|
3165 self.errorline) |
|
3166 |
|
3167 def __setMarginsDisplay(self): |
|
3168 """ |
|
3169 Private method to configure margins 0 and 2. |
|
3170 """ |
|
3171 # set the settings for all margins |
|
3172 self.setMarginsFont(Preferences.getEditorOtherFonts("MarginsFont")) |
|
3173 self.setMarginsForegroundColor(Preferences.getEditorColour("MarginsForeground")) |
|
3174 self.setMarginsBackgroundColor(Preferences.getEditorColour("MarginsBackground")) |
|
3175 |
|
3176 # reset standard margins settings |
|
3177 for margin in range(5): |
|
3178 self.setMarginLineNumbers(margin, False) |
|
3179 self.setMarginMarkerMask(margin, 0) |
|
3180 self.setMarginWidth(margin, 0) |
|
3181 self.setMarginSensitivity(margin, False) |
|
3182 |
|
3183 # set marker margin(s) settings |
|
3184 if self.__unifiedMargins: |
|
3185 margin1Mask = (1 << self.breakpoint) | \ |
|
3186 (1 << self.cbreakpoint) | \ |
|
3187 (1 << self.tbreakpoint) | \ |
|
3188 (1 << self.tcbreakpoint) | \ |
|
3189 (1 << self.dbreakpoint) | \ |
|
3190 (1 << self.currentline) | \ |
|
3191 (1 << self.errorline) | \ |
|
3192 (1 << self.bookmark) | \ |
|
3193 (1 << self.syntaxerror) | \ |
|
3194 (1 << self.notcovered) | \ |
|
3195 (1 << self.taskmarker) |
|
3196 self.setMarginWidth(1, 16) |
|
3197 self.setMarginSensitivity(1, True) |
|
3198 self.setMarginMarkerMask(1, margin1Mask) |
|
3199 |
|
3200 self.__linenoMargin = 0 |
|
3201 self.__foldMargin = 2 |
|
3202 else: |
|
3203 |
|
3204 self.__bmMargin = 0 |
|
3205 self.__linenoMargin = 1 |
|
3206 self.__bpMargin = 2 |
|
3207 self.__foldMargin = 3 |
|
3208 self.__indicMargin = 4 |
|
3209 |
|
3210 marginBmMask = (1 << self.bookmark) |
|
3211 self.setMarginWidth(self.__bmMargin, 16) |
|
3212 self.setMarginSensitivity(self.__bmMargin, True) |
|
3213 self.setMarginMarkerMask(self.__bmMargin, marginBmMask) |
|
3214 |
|
3215 marginBpMask = (1 << self.breakpoint) | \ |
|
3216 (1 << self.cbreakpoint) | \ |
|
3217 (1 << self.tbreakpoint) | \ |
|
3218 (1 << self.tcbreakpoint) | \ |
|
3219 (1 << self.dbreakpoint) | \ |
|
3220 (1 << self.currentline) | \ |
|
3221 (1 << self.errorline) |
|
3222 self.setMarginWidth(self.__bpMargin, 16) |
|
3223 self.setMarginSensitivity(self.__bpMargin, True) |
|
3224 self.setMarginMarkerMask(self.__bpMargin, marginBpMask) |
|
3225 |
|
3226 marginIndicMask = (1 << self.syntaxerror) | \ |
|
3227 (1 << self.notcovered) | \ |
|
3228 (1 << self.taskmarker) |
|
3229 self.setMarginWidth(self.__indicMargin, 16) |
|
3230 self.setMarginSensitivity(self.__indicMargin, True) |
|
3231 self.setMarginMarkerMask(self.__indicMargin, marginIndicMask) |
|
3232 |
|
3233 # set linenumber margin settings |
|
3234 linenoMargin = Preferences.getEditor("LinenoMargin") |
|
3235 self.setMarginLineNumbers(self.__linenoMargin, linenoMargin) |
|
3236 if linenoMargin: |
|
3237 self.setMarginWidth(self.__linenoMargin, |
|
3238 ' ' + '8' * Preferences.getEditor("LinenoWidth")) |
|
3239 else: |
|
3240 self.setMarginWidth(self.__linenoMargin, 0) |
|
3241 |
|
3242 # set folding margin settings |
|
3243 if Preferences.getEditor("FoldingMargin"): |
|
3244 self.setMarginWidth(self.__foldMargin, 16) |
|
3245 folding = Preferences.getEditor("FoldingStyle") |
|
3246 try: |
|
3247 folding = QsciScintilla.FoldStyle(folding) |
|
3248 except AttributeError: |
|
3249 pass |
|
3250 try: |
|
3251 self.setFolding(folding, self.__foldMargin) |
|
3252 except TypeError: |
|
3253 self.setFolding(folding) |
|
3254 self.setFoldMarginColors(Preferences.getEditorColour("FoldmarginBackground"), |
|
3255 Preferences.getEditorColour("FoldmarginBackground")) |
|
3256 else: |
|
3257 self.setMarginWidth(self.__foldMargin, 0) |
|
3258 try: |
|
3259 self.setFolding(QsciScintilla.NoFoldStyle, self.__foldMargin) |
|
3260 except TypeError: |
|
3261 self.setFolding(QsciScintilla.NoFoldStyle) |
|
3262 |
|
3263 def __setTextDisplay(self): |
|
3264 """ |
|
3265 Private method to configure the text display. |
|
3266 """ |
|
3267 self.setTabWidth(Preferences.getEditor("TabWidth")) |
|
3268 self.setIndentationWidth(Preferences.getEditor("IndentWidth")) |
|
3269 if self.lexer_ and self.lexer_.alwaysKeepTabs(): |
|
3270 self.setIndentationsUseTabs(True) |
|
3271 else: |
|
3272 self.setIndentationsUseTabs(Preferences.getEditor("TabForIndentation")) |
|
3273 self.setTabIndents(Preferences.getEditor("TabIndents")) |
|
3274 self.setBackspaceUnindents(Preferences.getEditor("TabIndents")) |
|
3275 self.setIndentationGuides(Preferences.getEditor("IndentationGuides")) |
|
3276 if Preferences.getEditor("ShowWhitespace"): |
|
3277 self.setWhitespaceVisibility(QsciScintilla.WsVisible) |
|
3278 else: |
|
3279 self.setWhitespaceVisibility(QsciScintilla.WsInvisible) |
|
3280 self.setEolVisibility(Preferences.getEditor("ShowEOL")) |
|
3281 self.setAutoIndent(Preferences.getEditor("AutoIndentation")) |
|
3282 if Preferences.getEditor("BraceHighlighting"): |
|
3283 self.setBraceMatching(QsciScintilla.SloppyBraceMatch) |
|
3284 else: |
|
3285 self.setBraceMatching(QsciScintilla.NoBraceMatch) |
|
3286 self.setMatchedBraceForegroundColor( |
|
3287 Preferences.getEditorColour("MatchingBrace")) |
|
3288 self.setMatchedBraceBackgroundColor( |
|
3289 Preferences.getEditorColour("MatchingBraceBack")) |
|
3290 self.setUnmatchedBraceForegroundColor( |
|
3291 Preferences.getEditorColour("NonmatchingBrace")) |
|
3292 self.setUnmatchedBraceBackgroundColor( |
|
3293 Preferences.getEditorColour("NonmatchingBraceBack")) |
|
3294 if Preferences.getEditor("CustomSelectionColours"): |
|
3295 self.setSelectionBackgroundColor(\ |
|
3296 Preferences.getEditorColour("SelectionBackground")) |
|
3297 else: |
|
3298 self.setSelectionBackgroundColor(\ |
|
3299 QApplication.palette().color(QPalette.Highlight)) |
|
3300 if Preferences.getEditor("ColourizeSelText"): |
|
3301 self.resetSelectionForegroundColor() |
|
3302 elif Preferences.getEditor("CustomSelectionColours"): |
|
3303 self.setSelectionForegroundColor(\ |
|
3304 Preferences.getEditorColour("SelectionForeground")) |
|
3305 else: |
|
3306 self.setSelectionForegroundColor(\ |
|
3307 QApplication.palette().color(QPalette.HighlightedText)) |
|
3308 self.setSelectionToEol(Preferences.getEditor("ExtendSelectionToEol")) |
|
3309 self.setCaretForegroundColor( |
|
3310 Preferences.getEditorColour("CaretForeground")) |
|
3311 self.setCaretLineBackgroundColor( |
|
3312 Preferences.getEditorColour("CaretLineBackground")) |
|
3313 self.setCaretLineVisible(Preferences.getEditor("CaretLineVisible")) |
|
3314 self.caretWidth = Preferences.getEditor("CaretWidth") |
|
3315 self.setCaretWidth(self.caretWidth) |
|
3316 self.useMonospaced = Preferences.getEditor("UseMonospacedFont") |
|
3317 self.setMonospaced(self.useMonospaced) |
|
3318 edgeMode = Preferences.getEditor("EdgeMode") |
|
3319 edge = QsciScintilla.EdgeMode(edgeMode) |
|
3320 self.setEdgeMode(edge) |
|
3321 if edgeMode: |
|
3322 self.setEdgeColumn(Preferences.getEditor("EdgeColumn")) |
|
3323 self.setEdgeColor(Preferences.getEditorColour("Edge")) |
|
3324 |
|
3325 if Preferences.getEditor("WrapLongLines"): |
|
3326 self.setWrapMode(QsciScintilla.WrapWord) |
|
3327 self.setWrapVisualFlags(\ |
|
3328 QsciScintilla.WrapFlagByBorder, QsciScintilla.WrapFlagByBorder) |
|
3329 else: |
|
3330 self.setWrapMode(QsciScintilla.WrapNone) |
|
3331 self.setWrapVisualFlags(\ |
|
3332 QsciScintilla.WrapFlagNone, QsciScintilla.WrapFlagNone) |
|
3333 |
|
3334 self.searchIndicator = QsciScintilla.INDIC_CONTAINER |
|
3335 self.indicatorDefine(self.searchIndicator, QsciScintilla.INDIC_BOX, |
|
3336 Preferences.getEditorColour("SearchMarkers")) |
|
3337 if not Preferences.getEditor("SearchMarkersEnabled") and \ |
|
3338 not Preferences.getEditor("QuickSearchMarkersEnabled") and \ |
|
3339 not Preferences.getEditor("MarkOccurrencesEnabled"): |
|
3340 self.clearAllIndicators(self.searchIndicator) |
|
3341 |
|
3342 self.spellingIndicator = QsciScintilla.INDIC_CONTAINER + 1 |
|
3343 self.indicatorDefine(self.spellingIndicator, QsciScintilla.INDIC_SQUIGGLE, |
|
3344 Preferences.getEditorColour("SpellingMarkers")) |
|
3345 self.__setSpelling() |
|
3346 |
|
3347 def __setEolMode(self): |
|
3348 """ |
|
3349 Private method to configure the eol mode of the editor. |
|
3350 """ |
|
3351 eolMode = Preferences.getEditor("EOLMode") |
|
3352 eolMode = QsciScintilla.EolMode(eolMode) |
|
3353 self.setEolMode(eolMode) |
|
3354 self.__eolChanged() |
|
3355 |
|
3356 def __setAutoCompletion(self): |
|
3357 """ |
|
3358 Private method to configure the autocompletion function. |
|
3359 """ |
|
3360 if self.lexer_: |
|
3361 self.setAutoCompletionFillupsEnabled( |
|
3362 Preferences.getEditor("AutoCompletionFillups")) |
|
3363 self.setAutoCompletionCaseSensitivity( |
|
3364 Preferences.getEditor("AutoCompletionCaseSensitivity")) |
|
3365 self.setAutoCompletionReplaceWord( |
|
3366 Preferences.getEditor("AutoCompletionReplaceWord")) |
|
3367 self.setAutoCompletionShowSingle( |
|
3368 Preferences.getEditor("AutoCompletionShowSingle")) |
|
3369 autoCompletionSource = Preferences.getEditor("AutoCompletionSource") |
|
3370 if autoCompletionSource == QsciScintilla.AcsDocument: |
|
3371 self.setAutoCompletionSource(QsciScintilla.AcsDocument) |
|
3372 elif autoCompletionSource == QsciScintilla.AcsAPIs: |
|
3373 self.setAutoCompletionSource(QsciScintilla.AcsAPIs) |
|
3374 else: |
|
3375 self.setAutoCompletionSource(QsciScintilla.AcsAll) |
|
3376 if Preferences.getEditor("AutoCompletionEnabled"): |
|
3377 if self.__acHookFunction is None: |
|
3378 self.setAutoCompletionThreshold( |
|
3379 Preferences.getEditor("AutoCompletionThreshold")) |
|
3380 else: |
|
3381 self.setAutoCompletionThreshold(0) |
|
3382 else: |
|
3383 self.setAutoCompletionThreshold(-1) |
|
3384 self.setAutoCompletionSource(QsciScintilla.AcsNone) |
|
3385 |
|
3386 def __setCallTips(self): |
|
3387 """ |
|
3388 Private method to configure the calltips function. |
|
3389 """ |
|
3390 if Preferences.getEditor("CallTipsEnabled"): |
|
3391 self.setCallTipsBackgroundColor( |
|
3392 Preferences.getEditorColour("CallTipsBackground")) |
|
3393 self.setCallTipsVisible(Preferences.getEditor("CallTipsVisible")) |
|
3394 calltipsStyle = Preferences.getEditor("CallTipsStyle") |
|
3395 if calltipsStyle == QsciScintilla.CallTipsNoContext: |
|
3396 self.setCallTipsStyle(QsciScintilla.CallTipsNoContext) |
|
3397 elif calltipsStyle == QsciScintilla.CallTipsNoAutoCompletionContext: |
|
3398 self.setCallTipsStyle(QsciScintilla.CallTipsNoAutoCompletionContext) |
|
3399 else: |
|
3400 self.setCallTipsStyle(QsciScintilla.CallTipsContext) |
|
3401 else: |
|
3402 self.setCallTipsStyle(QsciScintilla.CallTipsNone) |
|
3403 |
|
3404 ############################################################################ |
|
3405 ## Autocompletion handling methods below |
|
3406 ############################################################################ |
|
3407 |
|
3408 def canAutoCompleteFromAPIs(self): |
|
3409 """ |
|
3410 Public method to check for API availablity. |
|
3411 |
|
3412 @return flag indicating autocompletion from APIs is available (boolean) |
|
3413 """ |
|
3414 return self.acAPI |
|
3415 |
|
3416 def autoCompleteQScintilla(self): |
|
3417 """ |
|
3418 Public method to perform an autocompletion using QScintilla methods. |
|
3419 """ |
|
3420 acs = Preferences.getEditor("AutoCompletionSource") |
|
3421 if acs == QsciScintilla.AcsDocument: |
|
3422 self.autoCompleteFromDocument() |
|
3423 elif acs == QsciScintilla.AcsAPIs: |
|
3424 self.autoCompleteFromAPIs() |
|
3425 elif acs == QsciScintilla.AcsAll: |
|
3426 self.autoCompleteFromAll() |
|
3427 else: |
|
3428 QMessageBox.information(None, |
|
3429 self.trUtf8("Autocompletion"), |
|
3430 self.trUtf8("""Autocompletion is not available because""" |
|
3431 """ there is no autocompletion source set.""")) |
|
3432 |
|
3433 def setAutoCompletionEnabled(self, enable): |
|
3434 """ |
|
3435 Public method to enable/disable autocompletion. |
|
3436 |
|
3437 @param enable flag indicating the desired autocompletion status (boolean) |
|
3438 """ |
|
3439 if enable: |
|
3440 self.setAutoCompletionThreshold( |
|
3441 Preferences.getEditor("AutoCompletionThreshold")) |
|
3442 autoCompletionSource = Preferences.getEditor("AutoCompletionSource") |
|
3443 if autoCompletionSource == QsciScintilla.AcsDocument: |
|
3444 self.setAutoCompletionSource(QsciScintilla.AcsDocument) |
|
3445 elif autoCompletionSource == QsciScintilla.AcsAPIs: |
|
3446 self.setAutoCompletionSource(QsciScintilla.AcsAPIs) |
|
3447 else: |
|
3448 self.setAutoCompletionSource(QsciScintilla.AcsAll) |
|
3449 else: |
|
3450 self.setAutoCompletionThreshold(-1) |
|
3451 self.setAutoCompletionSource(QsciScintilla.AcsNone) |
|
3452 |
|
3453 def __toggleAutoCompletionEnable(self): |
|
3454 """ |
|
3455 Private slot to handle the Enable Autocompletion context menu entry. |
|
3456 """ |
|
3457 if self.menuActs["AutoCompletionEnable"].isChecked(): |
|
3458 self.setAutoCompletionEnabled(True) |
|
3459 else: |
|
3460 self.setAutoCompletionEnabled(False) |
|
3461 |
|
3462 ################################################################# |
|
3463 ## Support for autocompletion hook methods |
|
3464 ################################################################# |
|
3465 |
|
3466 def __charAdded(self, charNumber): |
|
3467 """ |
|
3468 Public slot called to handle the user entering a character. |
|
3469 |
|
3470 @param charNumber value of the character entered (integer) |
|
3471 """ |
|
3472 if self.isListActive(): |
|
3473 char = unichr(charNumber) |
|
3474 if self.__isStartChar(char): |
|
3475 self.cancelList() |
|
3476 self.autoComplete(auto = True, context = True) |
|
3477 return |
|
3478 elif char == '(': |
|
3479 self.cancelList() |
|
3480 |
|
3481 if self.callTipsStyle() != QsciScintilla.CallTipsNone and \ |
|
3482 self.lexer_ is not None and unichr(charNumber) in '()': |
|
3483 self.callTip() |
|
3484 |
|
3485 if not self.isCallTipActive(): |
|
3486 char = unichr(charNumber) |
|
3487 if self.__isStartChar(char): |
|
3488 self.autoComplete(auto = True, context = True) |
|
3489 return |
|
3490 |
|
3491 line, col = self.getCursorPosition() |
|
3492 txt = self.getWordLeft(line, col) |
|
3493 if len(txt) >= Preferences.getEditor("AutoCompletionThreshold"): |
|
3494 self.autoComplete(auto = True, context = False) |
|
3495 return |
|
3496 |
|
3497 def __isStartChar(self, ch): |
|
3498 """ |
|
3499 Private method to check, if a character is an autocompletion start character. |
|
3500 |
|
3501 @param ch character to be checked (one character string) |
|
3502 @return flag indicating the result (boolean) |
|
3503 """ |
|
3504 if self.lexer_ is None: |
|
3505 return False |
|
3506 |
|
3507 wseps = self.lexer_.autoCompletionWordSeparators() |
|
3508 for wsep in wseps: |
|
3509 if wsep.endswith(ch): |
|
3510 return True |
|
3511 |
|
3512 return False |
|
3513 |
|
3514 def setAutoCompletionHook(self, func): |
|
3515 """ |
|
3516 Public method to set an autocompletion hook. |
|
3517 |
|
3518 @param func Function to be set to handle autocompletion. func |
|
3519 should be a function taking a reference to the editor and |
|
3520 a boolean indicating to complete a context. |
|
3521 """ |
|
3522 if self.autoCompletionThreshold() > 0: |
|
3523 self.setAutoCompletionThreshold(0) |
|
3524 self.__acHookFunction = func |
|
3525 self.connect(self, SIGNAL("SCN_CHARADDED(int)"), self.__charAdded) |
|
3526 |
|
3527 def unsetAutoCompletionHook(self): |
|
3528 """ |
|
3529 Public method to unset a previously installed autocompletion hook. |
|
3530 """ |
|
3531 self.disconnect(self, SIGNAL("SCN_CHARADDED(int)"), self.__charAdded) |
|
3532 self.__acHookFunction = None |
|
3533 if self.autoCompletionThreshold() == 0: |
|
3534 self.setAutoCompletionThreshold( |
|
3535 Preferences.getEditor("AutoCompletionThreshold")) |
|
3536 |
|
3537 def autoCompletionHook(self): |
|
3538 """ |
|
3539 Public method to get the autocompletion hook function. |
|
3540 |
|
3541 @return function set by setAutoCompletionHook() |
|
3542 """ |
|
3543 return self.__acHookFunction |
|
3544 |
|
3545 def autoComplete(self, auto = False, context = True): |
|
3546 """ |
|
3547 Public method to start autocompletion. |
|
3548 |
|
3549 @keyparam auto flag indicating a call from the __charAdded method (boolean) |
|
3550 @keyparam context flag indicating to complete a context (boolean) |
|
3551 """ |
|
3552 if auto and self.autoCompletionThreshold() == -1: |
|
3553 # autocompletion is disabled |
|
3554 return |
|
3555 |
|
3556 if self.__acHookFunction is not None: |
|
3557 self.__acHookFunction(self, context) |
|
3558 elif not auto: |
|
3559 self.autoCompleteQScintilla() |
|
3560 elif self.autoCompletionSource() != QsciScintilla.AcsNone: |
|
3561 self.autoCompleteQScintilla() |
|
3562 |
|
3563 def callTip(self): |
|
3564 """ |
|
3565 Public method to show calltips. |
|
3566 """ |
|
3567 if self.__ctHookFunction is not None: |
|
3568 self.__callTip() |
|
3569 else: |
|
3570 QsciScintillaCompat.callTip(self) |
|
3571 |
|
3572 def __callTip(self): |
|
3573 """ |
|
3574 Private method to show call tips provided by a plugin. |
|
3575 """ |
|
3576 pos = self.currentPosition() |
|
3577 |
|
3578 # move backward to the start of the current calltip working out which argument |
|
3579 # to highlight |
|
3580 commas = 0 |
|
3581 found = False |
|
3582 ch, pos = self.__getCharacter(pos) |
|
3583 while ch: |
|
3584 if ch == ',': |
|
3585 commas += 1 |
|
3586 elif ch == ')': |
|
3587 depth = 1 |
|
3588 |
|
3589 # ignore everything back to the start of the corresponding parenthesis |
|
3590 ch, pos = self.__getCharacter(pos) |
|
3591 while ch: |
|
3592 if ch == ')': |
|
3593 depth += 1 |
|
3594 elif ch == '(': |
|
3595 depth -= 1 |
|
3596 if depth == 0: |
|
3597 break |
|
3598 ch, pos = self.__getCharacter(pos) |
|
3599 elif ch == '(': |
|
3600 found = True |
|
3601 break |
|
3602 |
|
3603 ch, pos = self.__getCharacter(pos) |
|
3604 |
|
3605 self.SendScintilla(QsciScintilla.SCI_CALLTIPCANCEL) |
|
3606 |
|
3607 if not found: |
|
3608 return |
|
3609 |
|
3610 try: |
|
3611 callTips = self.__ctHookFunction(self, pos, commas) |
|
3612 except TypeError: |
|
3613 # for backward compatibility |
|
3614 callTips = self.__ctHookFunction(self, pos) |
|
3615 if len(callTips) == 0: |
|
3616 if Preferences.getEditor("CallTipsScintillaOnFail"): |
|
3617 # try QScintilla calltips |
|
3618 QsciScintillaCompat.callTip(self) |
|
3619 return |
|
3620 |
|
3621 ctshift = 0 |
|
3622 for ct in callTips: |
|
3623 shift = ct.index("(") |
|
3624 if ctshift < shift: |
|
3625 ctshift = shift |
|
3626 |
|
3627 cv = self.callTipsVisible() |
|
3628 if cv > 0: |
|
3629 # this is just a safe guard |
|
3630 ct = "\n".join(callTips[:cv]) |
|
3631 else: |
|
3632 # until here and unindent below |
|
3633 ct = "\n".join(callTips) |
|
3634 |
|
3635 self.SendScintilla(QsciScintilla.SCI_CALLTIPSHOW, |
|
3636 self.__adjustedCallTipPosition(ctshift, pos), ct) |
|
3637 if '\n' in ct: |
|
3638 return |
|
3639 |
|
3640 # Highlight the current argument |
|
3641 if commas == 0: |
|
3642 astart = ct.find('(') |
|
3643 else: |
|
3644 astart = ct.find(',') |
|
3645 commas -= 1 |
|
3646 while astart != -1 and commas > 0: |
|
3647 astart = ct.find(',', astart + 1) |
|
3648 commas -= 1 |
|
3649 |
|
3650 if astart == -1: |
|
3651 return |
|
3652 |
|
3653 depth = 0 |
|
3654 for aend in range(astart + 1, len(ct)): |
|
3655 ch = ct[aend] |
|
3656 |
|
3657 if ch == ',' and depth == 0: |
|
3658 break |
|
3659 elif ch == '(': |
|
3660 depth += 1 |
|
3661 elif ch == ')': |
|
3662 if depth == 0: |
|
3663 break |
|
3664 |
|
3665 depth -= 1 |
|
3666 |
|
3667 if astart != aend: |
|
3668 self.SendScintilla(QsciScintilla.SCI_CALLTIPSETHLT, astart, aend) |
|
3669 |
|
3670 def __adjustedCallTipPosition(self, ctshift, pos): |
|
3671 """ |
|
3672 Private method to calculate an adjusted position for showing calltips. |
|
3673 |
|
3674 @param ctshift amount the calltip shall be shifted (integer) |
|
3675 @param pos position into the text (integer) |
|
3676 @return new position for the calltip (integer) |
|
3677 """ |
|
3678 ct = pos |
|
3679 if ctshift: |
|
3680 ctmin = self.SendScintilla(QsciScintilla.SCI_POSITIONFROMLINE, |
|
3681 self.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, ct)) |
|
3682 if ct - ctshift < ctmin: |
|
3683 ct = ctmin |
|
3684 else: |
|
3685 ct = ct - ctshift |
|
3686 return ct |
|
3687 |
|
3688 def setCallTipHook(self, func): |
|
3689 """ |
|
3690 Public method to set a calltip hook. |
|
3691 |
|
3692 @param func Function to be set to determine calltips. func |
|
3693 should be a function taking a reference to the editor, |
|
3694 a position into the text and the amount of commas to the |
|
3695 left of the cursor. It should return the possible |
|
3696 calltips as a list of strings. |
|
3697 """ |
|
3698 self.__ctHookFunction = func |
|
3699 |
|
3700 def unsetCallTipHook(self): |
|
3701 """ |
|
3702 Public method to unset a calltip hook. |
|
3703 """ |
|
3704 self.__ctHookFunction = None |
|
3705 |
|
3706 def callTipHook(self): |
|
3707 """ |
|
3708 Public method to get the calltip hook function. |
|
3709 |
|
3710 @return function set by setCallTipHook() |
|
3711 """ |
|
3712 return self.__ctHookFunction |
|
3713 |
|
3714 ################################################################# |
|
3715 ## Methods needed by the context menu |
|
3716 ################################################################# |
|
3717 |
|
3718 def __marginNumber(self, xPos): |
|
3719 """ |
|
3720 Private method to calculate the margin number based on a x position. |
|
3721 |
|
3722 @param xPos x position (integer) |
|
3723 @return margin number (integer, -1 for no margin) |
|
3724 """ |
|
3725 width = 0 |
|
3726 for margin in range(5): |
|
3727 width += self.marginWidth(margin) |
|
3728 if xPos <= width: |
|
3729 return margin |
|
3730 return -1 |
|
3731 |
|
3732 def contextMenuEvent(self, evt): |
|
3733 """ |
|
3734 Private method implementing the context menu event. |
|
3735 |
|
3736 @param evt the context menu event (QContextMenuEvent) |
|
3737 """ |
|
3738 evt.accept() |
|
3739 if self.__marginNumber(evt.x()) == -1: |
|
3740 self.spellingMenuPos = self.positionFromPoint(evt.pos()) |
|
3741 if self.spellingMenuPos >= 0 and \ |
|
3742 self.spell is not None and \ |
|
3743 self.hasIndicator(self.spellingIndicator, self.spellingMenuPos): |
|
3744 self.spellingMenu.popup(evt.globalPos()) |
|
3745 else: |
|
3746 self.menu.popup(evt.globalPos()) |
|
3747 else: |
|
3748 self.line = self.lineAt(evt.pos()) |
|
3749 if self.__unifiedMargins: |
|
3750 self.marginMenu.popup(evt.globalPos()) |
|
3751 else: |
|
3752 if self.__marginNumber(evt.x()) in [self.__bmMargin, self.__linenoMargin]: |
|
3753 self.bmMarginMenu.popup(evt.globalPos()) |
|
3754 elif self.__marginNumber(evt.x()) == self.__bpMargin: |
|
3755 self.bpMarginMenu.popup(evt.globalPos()) |
|
3756 elif self.__marginNumber(evt.x()) == self.__indicMargin: |
|
3757 self.indicMarginMenu.popup(evt.globalPos()) |
|
3758 |
|
3759 def __showContextMenu(self): |
|
3760 """ |
|
3761 Private slot handling the aboutToShow signal of the context menu. |
|
3762 """ |
|
3763 self.menuActs["Save"].setEnabled(self.isModified()) |
|
3764 self.menuActs["Undo"].setEnabled(self.isUndoAvailable()) |
|
3765 self.menuActs["Redo"].setEnabled(self.isRedoAvailable()) |
|
3766 self.menuActs["Revert"].setEnabled(self.isModified()) |
|
3767 if not self.miniMenu: |
|
3768 self.menuActs["Cut"].setEnabled(self.hasSelectedText()) |
|
3769 self.menuActs["Copy"].setEnabled(self.hasSelectedText()) |
|
3770 if not self.isResourcesFile: |
|
3771 if self.fileName and \ |
|
3772 (self.isPyFile() or self.isPy3File()): |
|
3773 self.menuActs["Show"].setEnabled(True) |
|
3774 else: |
|
3775 self.menuActs["Show"].setEnabled(False) |
|
3776 if self.fileName and \ |
|
3777 (self.isPyFile() or self.isPy3File() or self.isRubyFile()): |
|
3778 self.menuActs["Diagrams"].setEnabled(True) |
|
3779 else: |
|
3780 self.menuActs["Diagrams"].setEnabled(False) |
|
3781 if not self.miniMenu: |
|
3782 if self.lexer_ is not None: |
|
3783 self.menuActs["Comment"].setEnabled(self.lexer_.canBlockComment()) |
|
3784 self.menuActs["Uncomment"].setEnabled(self.lexer_.canBlockComment()) |
|
3785 self.menuActs["StreamComment"].setEnabled(self.lexer_.canStreamComment()) |
|
3786 self.menuActs["BoxComment"].setEnabled(self.lexer_.canBoxComment()) |
|
3787 else: |
|
3788 self.menuActs["Comment"].setEnabled(False) |
|
3789 self.menuActs["Uncomment"].setEnabled(False) |
|
3790 self.menuActs["StreamComment"].setEnabled(False) |
|
3791 self.menuActs["BoxComment"].setEnabled(False) |
|
3792 |
|
3793 self.menuActs["TypingAidsEnabled"].setEnabled(self.completer is not None) |
|
3794 self.menuActs["TypingAidsEnabled"].setChecked(\ |
|
3795 self.completer is not None and self.completer.isEnabled()) |
|
3796 |
|
3797 spellingAvailable = SpellChecker.isAvailable() |
|
3798 self.menuActs["SpellCheck"].setEnabled(spellingAvailable) |
|
3799 self.menuActs["SpellCheckSelection"].setEnabled( |
|
3800 spellingAvailable and self.hasSelectedText()) |
|
3801 self.menuActs["SpellCheckRemove"].setEnabled( |
|
3802 spellingAvailable and self.spellingMenuPos >= 0) |
|
3803 |
|
3804 self.emit(SIGNAL("showMenu"), "Main", self.menu, self) |
|
3805 |
|
3806 def __showContextMenuAutocompletion(self): |
|
3807 """ |
|
3808 Private slot called before the autocompletion menu is shown. |
|
3809 """ |
|
3810 self.menuActs["acDynamic"].setEnabled( |
|
3811 self.acAPI or self.__acHookFunction is not None) |
|
3812 self.menuActs["acAPI"].setEnabled(self.acAPI) |
|
3813 self.menuActs["acAPIDocument"].setEnabled(self.acAPI) |
|
3814 self.menuActs["calltip"].setEnabled(self.acAPI) |
|
3815 |
|
3816 self.emit(SIGNAL("showMenu"), "Autocompletion", self.autocompletionMenu, self) |
|
3817 |
|
3818 def __showContextMenuShow(self): |
|
3819 """ |
|
3820 Private slot called before the show menu is shown. |
|
3821 """ |
|
3822 prEnable = False |
|
3823 coEnable = False |
|
3824 |
|
3825 # first check if the file belongs to a project |
|
3826 project = e4App().getObject("Project") |
|
3827 if project.isOpen() and project.isProjectSource(self.fileName): |
|
3828 fn = project.getMainScript(True) |
|
3829 if fn is not None: |
|
3830 tfn = Utilities.getTestFileName(fn) |
|
3831 basename = os.path.splitext(fn)[0] |
|
3832 tbasename = os.path.splitext(tfn)[0] |
|
3833 prEnable = prEnable or \ |
|
3834 os.path.isfile("%s.profile" % basename) or \ |
|
3835 os.path.isfile("%s.profile" % tbasename) |
|
3836 coEnable = coEnable or \ |
|
3837 os.path.isfile("%s.coverage" % basename) or \ |
|
3838 os.path.isfile("%s.coverage" % tbasename) |
|
3839 |
|
3840 # now check ourself |
|
3841 fn = self.getFileName() |
|
3842 if fn is not None: |
|
3843 tfn = Utilities.getTestFileName(fn) |
|
3844 basename = os.path.splitext(fn)[0] |
|
3845 tbasename = os.path.splitext(tfn)[0] |
|
3846 prEnable = prEnable or \ |
|
3847 os.path.isfile("%s.profile" % basename) or \ |
|
3848 os.path.isfile("%s.profile" % tbasename) |
|
3849 coEnable = coEnable or \ |
|
3850 os.path.isfile("%s.coverage" % basename) or \ |
|
3851 os.path.isfile("%s.coverage" % tbasename) |
|
3852 |
|
3853 self.profileMenuAct.setEnabled(prEnable) |
|
3854 self.coverageMenuAct.setEnabled(coEnable) |
|
3855 self.coverageShowAnnotationMenuAct.setEnabled(\ |
|
3856 coEnable and not self.coverageMarkersShown) |
|
3857 self.coverageHideAnnotationMenuAct.setEnabled(\ |
|
3858 self.coverageMarkersShown) |
|
3859 |
|
3860 self.emit(SIGNAL("showMenu"), "Show", self.showMenu, self) |
|
3861 |
|
3862 def __showContextMenuGraphics(self): |
|
3863 """ |
|
3864 Private slot handling the aboutToShow signal of the diagrams context menu. |
|
3865 """ |
|
3866 project = e4App().getObject("Project") |
|
3867 if project.isOpen() and project.isProjectSource(self.fileName): |
|
3868 self.applicationDiagramMenuAct.setEnabled(True) |
|
3869 else: |
|
3870 self.applicationDiagramMenuAct.setEnabled(False) |
|
3871 |
|
3872 self.emit(SIGNAL("showMenu"), "Graphics", self.graphicsMenu, self) |
|
3873 |
|
3874 def __showContextMenuMargin(self): |
|
3875 """ |
|
3876 Private slot handling the aboutToShow signal of the margins context menu. |
|
3877 """ |
|
3878 if self.fileName and \ |
|
3879 (self.isPyFile() or self.isPy3File() or self.isRubyFile()): |
|
3880 self.marginMenuActs["Breakpoint"].setEnabled(True) |
|
3881 self.marginMenuActs["TempBreakpoint"].setEnabled(True) |
|
3882 if self.markersAtLine(self.line) & self.breakpointMask: |
|
3883 self.marginMenuActs["EditBreakpoint"].setEnabled(True) |
|
3884 self.marginMenuActs["EnableBreakpoint"].setEnabled(True) |
|
3885 else: |
|
3886 self.marginMenuActs["EditBreakpoint"].setEnabled(False) |
|
3887 self.marginMenuActs["EnableBreakpoint"].setEnabled(False) |
|
3888 if self.markersAtLine(self.line) & (1 << self.dbreakpoint): |
|
3889 self.marginMenuActs["EnableBreakpoint"].setText(\ |
|
3890 self.trUtf8('Enable breakpoint')) |
|
3891 else: |
|
3892 self.marginMenuActs["EnableBreakpoint"].setText(\ |
|
3893 self.trUtf8('Disable breakpoint')) |
|
3894 if self.breaks: |
|
3895 self.marginMenuActs["NextBreakpoint"].setEnabled(True) |
|
3896 self.marginMenuActs["PreviousBreakpoint"].setEnabled(True) |
|
3897 self.marginMenuActs["ClearBreakpoint"].setEnabled(True) |
|
3898 else: |
|
3899 self.marginMenuActs["NextBreakpoint"].setEnabled(False) |
|
3900 self.marginMenuActs["PreviousBreakpoint"].setEnabled(False) |
|
3901 self.marginMenuActs["ClearBreakpoint"].setEnabled(False) |
|
3902 else: |
|
3903 self.marginMenuActs["Breakpoint"].setEnabled(False) |
|
3904 self.marginMenuActs["TempBreakpoint"].setEnabled(False) |
|
3905 self.marginMenuActs["EditBreakpoint"].setEnabled(False) |
|
3906 self.marginMenuActs["EnableBreakpoint"].setEnabled(False) |
|
3907 self.marginMenuActs["NextBreakpoint"].setEnabled(False) |
|
3908 self.marginMenuActs["PreviousBreakpoint"].setEnabled(False) |
|
3909 self.marginMenuActs["ClearBreakpoint"].setEnabled(False) |
|
3910 |
|
3911 if self.bookmarks: |
|
3912 self.marginMenuActs["NextBookmark"].setEnabled(True) |
|
3913 self.marginMenuActs["PreviousBookmark"].setEnabled(True) |
|
3914 self.marginMenuActs["ClearBookmark"].setEnabled(True) |
|
3915 else: |
|
3916 self.marginMenuActs["NextBookmark"].setEnabled(False) |
|
3917 self.marginMenuActs["PreviousBookmark"].setEnabled(False) |
|
3918 self.marginMenuActs["ClearBookmark"].setEnabled(False) |
|
3919 |
|
3920 if len(self.syntaxerrors): |
|
3921 self.marginMenuActs["GotoSyntaxError"].setEnabled(True) |
|
3922 self.marginMenuActs["ClearSyntaxError"].setEnabled(True) |
|
3923 if self.markersAtLine(self.line) & (1 << self.syntaxerror): |
|
3924 self.marginMenuActs["ShowSyntaxError"].setEnabled(True) |
|
3925 else: |
|
3926 self.marginMenuActs["ShowSyntaxError"].setEnabled(False) |
|
3927 else: |
|
3928 self.marginMenuActs["GotoSyntaxError"].setEnabled(False) |
|
3929 self.marginMenuActs["ClearSyntaxError"].setEnabled(False) |
|
3930 self.marginMenuActs["ShowSyntaxError"].setEnabled(False) |
|
3931 |
|
3932 if self.notcoveredMarkers: |
|
3933 self.marginMenuActs["NextCoverageMarker"].setEnabled(True) |
|
3934 self.marginMenuActs["PreviousCoverageMarker"].setEnabled(True) |
|
3935 else: |
|
3936 self.marginMenuActs["NextCoverageMarker"].setEnabled(False) |
|
3937 self.marginMenuActs["PreviousCoverageMarker"].setEnabled(False) |
|
3938 |
|
3939 if self.__hasTaskMarkers: |
|
3940 self.marginMenuActs["PreviousTaskMarker"].setEnabled(True) |
|
3941 self.marginMenuActs["NextTaskMarker"].setEnabled(True) |
|
3942 else: |
|
3943 self.marginMenuActs["PreviousTaskMarker"].setEnabled(False) |
|
3944 self.marginMenuActs["NextTaskMarker"].setEnabled(False) |
|
3945 |
|
3946 self.emit(SIGNAL("showMenu"), "Margin", self.sender(), self) |
|
3947 |
|
3948 def __showContextMenuChecks(self): |
|
3949 """ |
|
3950 Private slot handling the aboutToShow signal of the checks context menu. |
|
3951 """ |
|
3952 self.emit(SIGNAL("showMenu"), "Checks", self.checksMenu, self) |
|
3953 |
|
3954 def __contextSave(self): |
|
3955 """ |
|
3956 Private slot handling the save context menu entry. |
|
3957 """ |
|
3958 ok, newName = self.saveFile() |
|
3959 if ok: |
|
3960 self.vm.setEditorName(self, newName) |
|
3961 |
|
3962 def __contextSaveAs(self): |
|
3963 """ |
|
3964 Private slot handling the save as context menu entry. |
|
3965 """ |
|
3966 ok, newName = self.saveFileAs() |
|
3967 if ok: |
|
3968 self.vm.setEditorName(self, newName) |
|
3969 |
|
3970 def __contextClose(self): |
|
3971 """ |
|
3972 Private slot handling the close context menu entry. |
|
3973 """ |
|
3974 self.vm.closeEditor(self) |
|
3975 |
|
3976 def __newView(self): |
|
3977 """ |
|
3978 Private slot to create a new view to an open document. |
|
3979 """ |
|
3980 self.vm.newEditorView(self.fileName, self, self.filetype) |
|
3981 |
|
3982 def __newViewNewSplit(self): |
|
3983 """ |
|
3984 Private slot to create a new view to an open document. |
|
3985 """ |
|
3986 self.vm.addSplit() |
|
3987 self.vm.newEditorView(self.fileName, self, self.filetype) |
|
3988 |
|
3989 def __selectAll(self): |
|
3990 """ |
|
3991 Private slot handling the select all context menu action. |
|
3992 """ |
|
3993 self.selectAll(True) |
|
3994 |
|
3995 def __deselectAll(self): |
|
3996 """ |
|
3997 Private slot handling the deselect all context menu action. |
|
3998 """ |
|
3999 self.selectAll(False) |
|
4000 |
|
4001 def shortenEmptyLines(self): |
|
4002 """ |
|
4003 Public slot to compress lines consisting solely of whitespace characters. |
|
4004 """ |
|
4005 searchRE = r"^[ \t]+$" |
|
4006 |
|
4007 ok = self.findFirstTarget(searchRE, True, False, False, 0, 0) |
|
4008 self.beginUndoAction() |
|
4009 while ok: |
|
4010 self.replaceTarget("") |
|
4011 ok = self.findNextTarget() |
|
4012 self.endUndoAction() |
|
4013 |
|
4014 def __autosaveEnable(self): |
|
4015 """ |
|
4016 Private slot handling the autosave enable context menu action. |
|
4017 """ |
|
4018 if self.menuActs["AutosaveEnable"].isChecked(): |
|
4019 self.autosaveManuallyDisabled = False |
|
4020 else: |
|
4021 self.autosaveManuallyDisabled = True |
|
4022 |
|
4023 def shouldAutosave(self): |
|
4024 """ |
|
4025 Public slot to check the autosave flags. |
|
4026 |
|
4027 @return flag indicating this editor should be saved (boolean) |
|
4028 """ |
|
4029 return self.fileName is not None and \ |
|
4030 not self.autosaveManuallyDisabled and \ |
|
4031 not self.isReadOnly() |
|
4032 |
|
4033 def __autoSyntaxCheck(self): |
|
4034 """ |
|
4035 Private method to perform an automatic syntax check of the file. |
|
4036 """ |
|
4037 if Preferences.getEditor("AutoCheckSyntax"): |
|
4038 self.clearSyntaxError() |
|
4039 if self.isPyFile(): |
|
4040 syntaxError, _fn, errorline, _code, _error = \ |
|
4041 Utilities.compile(self.fileName, self.text()) |
|
4042 if syntaxError: |
|
4043 self.toggleSyntaxError(int(errorline), 1, _error) |
|
4044 |
|
4045 def __showCodeMetrics(self): |
|
4046 """ |
|
4047 Private method to handle the code metrics context menu action. |
|
4048 """ |
|
4049 if not self.checkDirty(): |
|
4050 return |
|
4051 |
|
4052 self.codemetrics = CodeMetricsDialog() |
|
4053 self.codemetrics.show() |
|
4054 self.codemetrics.start(self.fileName) |
|
4055 |
|
4056 def __getCodeCoverageFile(self): |
|
4057 """ |
|
4058 Private method to get the filename of the file containing coverage info. |
|
4059 |
|
4060 @return filename of the coverage file (string) |
|
4061 """ |
|
4062 files = [] |
|
4063 |
|
4064 # first check if the file belongs to a project and there is |
|
4065 # a project coverage file |
|
4066 project = e4App().getObject("Project") |
|
4067 if project.isOpen() and project.isProjectSource(self.fileName): |
|
4068 fn = project.getMainScript(True) |
|
4069 if fn is not None: |
|
4070 tfn = Utilities.getTestFileName(fn) |
|
4071 basename = os.path.splitext(fn)[0] |
|
4072 tbasename = os.path.splitext(tfn)[0] |
|
4073 |
|
4074 f = "%s.coverage" % basename |
|
4075 tf = "%s.coverage" % tbasename |
|
4076 if os.path.isfile(f): |
|
4077 files.append(f) |
|
4078 if os.path.isfile(tf): |
|
4079 files.append(tf) |
|
4080 |
|
4081 # now check, if there are coverage files belonging to ourself |
|
4082 fn = self.getFileName() |
|
4083 if fn is not None: |
|
4084 tfn = Utilities.getTestFileName(fn) |
|
4085 basename = os.path.splitext(fn)[0] |
|
4086 tbasename = os.path.splitext(tfn)[0] |
|
4087 |
|
4088 f = "%s.coverage" % basename |
|
4089 tf = "%s.coverage" % tbasename |
|
4090 if os.path.isfile(f) and not f in files: |
|
4091 files.append(f) |
|
4092 if os.path.isfile(tf) and not tf in files: |
|
4093 files.append(tf) |
|
4094 |
|
4095 if files: |
|
4096 if len(files) > 1: |
|
4097 fn, ok = QInputDialog.getItem(\ |
|
4098 self, |
|
4099 self.trUtf8("Code Coverage"), |
|
4100 self.trUtf8("Please select a coverage file"), |
|
4101 files, |
|
4102 0, False) |
|
4103 if not ok: |
|
4104 return |
|
4105 else: |
|
4106 fn = files[0] |
|
4107 else: |
|
4108 fn = None |
|
4109 |
|
4110 return fn |
|
4111 |
|
4112 def __showCodeCoverage(self): |
|
4113 """ |
|
4114 Private method to handle the code coverage context menu action. |
|
4115 """ |
|
4116 fn = self.__getCodeCoverageFile() |
|
4117 if fn: |
|
4118 self.codecoverage = PyCoverageDialog() |
|
4119 self.codecoverage.show() |
|
4120 self.codecoverage.start(fn, self.fileName) |
|
4121 |
|
4122 def __codeCoverageShowAnnotations(self): |
|
4123 """ |
|
4124 Private method to handle the show code coverage annotations context menu action. |
|
4125 """ |
|
4126 fn = self.__getCodeCoverageFile() |
|
4127 if fn: |
|
4128 cover = coverage(data_file = fn) |
|
4129 cover.use_cache(True) |
|
4130 cover.load() |
|
4131 missing = cover.analysis2(self.fileName)[3] |
|
4132 if missing: |
|
4133 for line in missing: |
|
4134 handle = self.markerAdd(line - 1, self.notcovered) |
|
4135 self.notcoveredMarkers.append(handle) |
|
4136 self.emit(SIGNAL('coverageMarkersShown'), True) |
|
4137 self.coverageMarkersShown = True |
|
4138 else: |
|
4139 QMessageBox.information(None, |
|
4140 self.trUtf8("Show Code Coverage Annotations"), |
|
4141 self.trUtf8("""All lines have been covered.""")) |
|
4142 else: |
|
4143 QMessageBox.warning(None, |
|
4144 self.trUtf8("Show Code Coverage Annotations"), |
|
4145 self.trUtf8("""There is no coverage file available.""")) |
|
4146 |
|
4147 def __codeCoverageHideAnnotations(self): |
|
4148 """ |
|
4149 Private method to handle the hide code coverage annotations context menu action. |
|
4150 """ |
|
4151 for handle in self.notcoveredMarkers: |
|
4152 self.markerDeleteHandle(handle) |
|
4153 self.notcoveredMarkers = [] |
|
4154 self.emit(SIGNAL('coverageMarkersShown'), False) |
|
4155 self.coverageMarkersShown = False |
|
4156 |
|
4157 def hasCoverageMarkers(self): |
|
4158 """ |
|
4159 Public method to test, if there are coverage markers. |
|
4160 """ |
|
4161 return len(self.notcoveredMarkers) > 0 |
|
4162 |
|
4163 def nextUncovered(self): |
|
4164 """ |
|
4165 Public slot to handle the 'Next uncovered' context menu action. |
|
4166 """ |
|
4167 line, index = self.getCursorPosition() |
|
4168 if line == self.lines()-1: |
|
4169 line = 0 |
|
4170 else: |
|
4171 line += 1 |
|
4172 ucline = self.markerFindNext(line, 1 << self.notcovered) |
|
4173 if ucline < 0: |
|
4174 # wrap around |
|
4175 ucline = self.markerFindNext(0, 1 << self.notcovered) |
|
4176 if ucline >= 0: |
|
4177 self.setCursorPosition(ucline, 0) |
|
4178 self.ensureLineVisible(ucline) |
|
4179 |
|
4180 def previousUncovered(self): |
|
4181 """ |
|
4182 Public slot to handle the 'Previous uncovered' context menu action. |
|
4183 """ |
|
4184 line, index = self.getCursorPosition() |
|
4185 if line == 0: |
|
4186 line = self.lines()-1 |
|
4187 else: |
|
4188 line -= 1 |
|
4189 ucline = self.markerFindPrevious(line, 1 << self.notcovered) |
|
4190 if ucline < 0: |
|
4191 # wrap around |
|
4192 ucline = self.markerFindPrevious(self.lines() - 1, 1 << self.notcovered) |
|
4193 if ucline >= 0: |
|
4194 self.setCursorPosition(ucline, 0) |
|
4195 self.ensureLineVisible(ucline) |
|
4196 |
|
4197 def __showProfileData(self): |
|
4198 """ |
|
4199 Private method to handle the show profile data context menu action. |
|
4200 """ |
|
4201 files = [] |
|
4202 |
|
4203 # first check if the file belongs to a project and there is |
|
4204 # a project profile file |
|
4205 project = e4App().getObject("Project") |
|
4206 if project.isOpen() and project.isProjectSource(self.fileName): |
|
4207 fn = project.getMainScript(True) |
|
4208 if fn is not None: |
|
4209 tfn = Utilities.getTestFileName(fn) |
|
4210 basename = os.path.splitext(fn)[0] |
|
4211 tbasename = os.path.splitext(tfn)[0] |
|
4212 |
|
4213 f = "%s.profile" % basename |
|
4214 tf = "%s.profile" % tbasename |
|
4215 if os.path.isfile(f): |
|
4216 files.append(f) |
|
4217 if os.path.isfile(tf): |
|
4218 files.append(tf) |
|
4219 |
|
4220 # now check, if there are profile files belonging to ourself |
|
4221 fn = self.getFileName() |
|
4222 if fn is not None: |
|
4223 tfn = Utilities.getTestFileName(fn) |
|
4224 basename = os.path.splitext(fn)[0] |
|
4225 tbasename = os.path.splitext(tfn)[0] |
|
4226 |
|
4227 f = "%s.profile" % basename |
|
4228 tf = "%s.profile" % tbasename |
|
4229 if os.path.isfile(f) and not f in files: |
|
4230 files.append(f) |
|
4231 if os.path.isfile(tf) and not tf in files: |
|
4232 files.append(tf) |
|
4233 |
|
4234 if files: |
|
4235 if len(files) > 1: |
|
4236 fn, ok = QInputDialog.getItem(\ |
|
4237 self, |
|
4238 self.trUtf8("Profile Data"), |
|
4239 self.trUtf8("Please select a profile file"), |
|
4240 files, |
|
4241 0, False) |
|
4242 if not ok: |
|
4243 return |
|
4244 else: |
|
4245 fn = files[0] |
|
4246 else: |
|
4247 return |
|
4248 |
|
4249 self.profiledata = PyProfileDialog() |
|
4250 self.profiledata.show() |
|
4251 self.profiledata.start(fn, self.fileName) |
|
4252 |
|
4253 def __lmBbookmarks(self): |
|
4254 """ |
|
4255 Private method to handle the 'LMB toggles bookmark' context menu action. |
|
4256 """ |
|
4257 self.marginMenuActs["LMBbookmarks"].setChecked(True) |
|
4258 self.marginMenuActs["LMBbreakpoints"].setChecked(False) |
|
4259 |
|
4260 def __lmBbreakpoints(self): |
|
4261 """ |
|
4262 Private method to handle the 'LMB toggles breakpoint' context menu action. |
|
4263 """ |
|
4264 self.marginMenuActs["LMBbookmarks"].setChecked(True) |
|
4265 self.marginMenuActs["LMBbreakpoints"].setChecked(False) |
|
4266 |
|
4267 ############################################################################ |
|
4268 ## Syntax error handling methods below |
|
4269 ############################################################################ |
|
4270 |
|
4271 def toggleSyntaxError(self, line, error, msg = ""): |
|
4272 """ |
|
4273 Public method to toggle a syntax error indicator. |
|
4274 |
|
4275 @param line line number of the syntax error |
|
4276 @param error flag indicating if the error marker should be |
|
4277 set or deleted (boolean) |
|
4278 @param msg error message (string) |
|
4279 """ |
|
4280 if line == 0: |
|
4281 line = 1 |
|
4282 # hack to show a syntax error marker, if line is reported to be 0 |
|
4283 if error: |
|
4284 # set a new syntax error marker |
|
4285 markers = self.markersAtLine(line - 1) |
|
4286 if not (markers & (1 << self.syntaxerror)): |
|
4287 handle = self.markerAdd(line - 1, self.syntaxerror) |
|
4288 self.syntaxerrors[handle] = msg |
|
4289 self.emit(SIGNAL('syntaxerrorToggled'), self) |
|
4290 else: |
|
4291 for handle in self.syntaxerrors.keys(): |
|
4292 if self.markerLine(handle) == line - 1: |
|
4293 del self.syntaxerrors[handle] |
|
4294 self.markerDeleteHandle(handle) |
|
4295 self.emit(SIGNAL('syntaxerrorToggled'), self) |
|
4296 |
|
4297 def getSyntaxErrors(self): |
|
4298 """ |
|
4299 Public method to retrieve the syntax error markers. |
|
4300 |
|
4301 @return sorted list of all lines containing a syntax error |
|
4302 (list of integer) |
|
4303 """ |
|
4304 selist = [] |
|
4305 for handle in self.syntaxerrors.keys(): |
|
4306 selist.append(self.markerLine(handle) + 1) |
|
4307 |
|
4308 selist.sort() |
|
4309 return selist |
|
4310 |
|
4311 def hasSyntaxErrors(self): |
|
4312 """ |
|
4313 Public method to check for the presence of bookmarks. |
|
4314 |
|
4315 @return flag indicating the presence of bookmarks (boolean) |
|
4316 """ |
|
4317 return len(self.syntaxerrors) > 0 |
|
4318 |
|
4319 def gotoSyntaxError(self): |
|
4320 """ |
|
4321 Public slot to handle the 'Goto syntax error' context menu action. |
|
4322 """ |
|
4323 seline = self.markerFindNext(0, 1 << self.syntaxerror) |
|
4324 if seline >= 0: |
|
4325 self.setCursorPosition(seline, 0) |
|
4326 self.ensureLineVisible(seline) |
|
4327 |
|
4328 def clearSyntaxError(self): |
|
4329 """ |
|
4330 Public slot to handle the 'Clear all syntax error' context menu action. |
|
4331 """ |
|
4332 for handle in self.syntaxerrors.keys(): |
|
4333 line = self.markerLine(handle) + 1 |
|
4334 self.toggleSyntaxError(line, False) |
|
4335 |
|
4336 def __showSyntaxError(self, line = -1): |
|
4337 """ |
|
4338 Private slot to handle the 'Show syntax error message' context menu action. |
|
4339 |
|
4340 @param line line number to show the syntax error for (integer) |
|
4341 """ |
|
4342 if line == -1: |
|
4343 line = self.line |
|
4344 |
|
4345 for handle in self.syntaxerrors.keys(): |
|
4346 if self.markerLine(handle) == line: |
|
4347 QMessageBox.critical(None, |
|
4348 self.trUtf8("Syntax Error"), |
|
4349 self.syntaxerrors[handle]) |
|
4350 break |
|
4351 else: |
|
4352 QMessageBox.critical(None, |
|
4353 self.trUtf8("Syntax Error"), |
|
4354 self.trUtf8("No syntax error message available.")) |
|
4355 |
|
4356 ################################################################# |
|
4357 ## Macro handling methods |
|
4358 ################################################################# |
|
4359 |
|
4360 def __getMacroName(self): |
|
4361 """ |
|
4362 Private method to select a macro name from the list of macros. |
|
4363 |
|
4364 @return Tuple of macro name and a flag, indicating, if the user pressed ok or |
|
4365 canceled the operation. (string, boolean) |
|
4366 """ |
|
4367 qs = [] |
|
4368 for s in self.macros.keys(): |
|
4369 qs.append(s) |
|
4370 qs.sort() |
|
4371 return QInputDialog.getItem(\ |
|
4372 self, |
|
4373 self.trUtf8("Macro Name"), |
|
4374 self.trUtf8("Select a macro name:"), |
|
4375 qs, |
|
4376 0, False) |
|
4377 |
|
4378 def macroRun(self): |
|
4379 """ |
|
4380 Public method to execute a macro. |
|
4381 """ |
|
4382 name, ok = self.__getMacroName() |
|
4383 if ok and name: |
|
4384 self.macros[name].play() |
|
4385 |
|
4386 def macroDelete(self): |
|
4387 """ |
|
4388 Public method to delete a macro. |
|
4389 """ |
|
4390 name, ok = self.__getMacroName() |
|
4391 if ok and name: |
|
4392 del self.macros[name] |
|
4393 |
|
4394 def macroLoad(self): |
|
4395 """ |
|
4396 Public method to load a macro from a file. |
|
4397 """ |
|
4398 configDir = Utilities.getConfigDir() |
|
4399 fname = QFileDialog.getOpenFileName( |
|
4400 self, |
|
4401 self.trUtf8("Load macro file"), |
|
4402 configDir, |
|
4403 self.trUtf8("Macro files (*.macro)")) |
|
4404 |
|
4405 if not fname: |
|
4406 return # user aborted |
|
4407 |
|
4408 try: |
|
4409 f = open(fname, "rb") |
|
4410 lines = f.readlines() |
|
4411 f.close() |
|
4412 except IOError: |
|
4413 QMessageBox.critical(self, |
|
4414 self.trUtf8("Error loading macro"), |
|
4415 self.trUtf8("<p>The macro file <b>{0}</b> could not be read.</p>") |
|
4416 .format(fname)) |
|
4417 return |
|
4418 |
|
4419 if len(lines) != 2: |
|
4420 QMessageBox.critical(self, |
|
4421 self.trUtf8("Error loading macro"), |
|
4422 self.trUtf8("<p>The macro file <b>{0}</b> is corrupt.</p>") |
|
4423 .format(fname)) |
|
4424 return |
|
4425 |
|
4426 macro = QsciMacro(lines[1], self) |
|
4427 self.macros[lines[0].strip()] = macro |
|
4428 |
|
4429 def macroSave(self): |
|
4430 """ |
|
4431 Public method to save a macro to a file. |
|
4432 """ |
|
4433 configDir = Utilities.getConfigDir() |
|
4434 |
|
4435 name, ok = self.__getMacroName() |
|
4436 if not ok or not name: |
|
4437 return # user abort |
|
4438 |
|
4439 fname, selectedFilter = QFileDialog.getSaveFileNameAndFilter(\ |
|
4440 self, |
|
4441 self.trUtf8("Save macro file"), |
|
4442 configDir, |
|
4443 self.trUtf8("Macro files (*.macro)"), |
|
4444 "", |
|
4445 QFileDialog.Options(QFileDialog.DontConfirmOverwrite)) |
|
4446 |
|
4447 if not fname: |
|
4448 return # user aborted |
|
4449 |
|
4450 ext = QFileInfo(fname).suffix() |
|
4451 if not ext: |
|
4452 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
4453 if ex: |
|
4454 fname += ex |
|
4455 if QFileInfo(fname).exists(): |
|
4456 res = QMessageBox.warning(self, |
|
4457 self.trUtf8("Save macro"), |
|
4458 self.trUtf8("<p>The macro file <b>{0}</b> already exists.</p>") |
|
4459 .format(fname), |
|
4460 QMessageBox.StandardButtons(\ |
|
4461 QMessageBox.Abort | \ |
|
4462 QMessageBox.Save), |
|
4463 QMessageBox.Abort) |
|
4464 if res == QMessageBox.Abort or res == QMessageBox.Cancel: |
|
4465 return |
|
4466 fname = Utilities.toNativeSeparators(fname) |
|
4467 |
|
4468 try: |
|
4469 f = open(fname, "wb") |
|
4470 f.write("%s%s" % (name, os.linesep)) |
|
4471 f.write(self.macros[name].save()) |
|
4472 f.close() |
|
4473 except IOError: |
|
4474 QMessageBox.critical(self, |
|
4475 self.trUtf8("Error saving macro"), |
|
4476 self.trUtf8("<p>The macro file <b>{0}</b> could not be written.</p>") |
|
4477 .format(fname)) |
|
4478 return |
|
4479 |
|
4480 def macroRecordingStart(self): |
|
4481 """ |
|
4482 Public method to start macro recording. |
|
4483 """ |
|
4484 if self.recording: |
|
4485 res = QMessageBox.warning(self, |
|
4486 self.trUtf8("Start Macro Recording"), |
|
4487 self.trUtf8("Macro recording is already active. Start new?"), |
|
4488 QMessageBox.StandardButtons(\ |
|
4489 QMessageBox.No | \ |
|
4490 QMessageBox.Yes), |
|
4491 QMessageBox.Yes) |
|
4492 if res == QMessageBox.Yes: |
|
4493 self.macroRecordingStop() |
|
4494 else: |
|
4495 return |
|
4496 else: |
|
4497 self.recording = True |
|
4498 |
|
4499 self.curMacro = QsciMacro(self) |
|
4500 self.curMacro.startRecording() |
|
4501 |
|
4502 def macroRecordingStop(self): |
|
4503 """ |
|
4504 Public method to stop macro recording. |
|
4505 """ |
|
4506 if not self.recording: |
|
4507 return # we are not recording |
|
4508 |
|
4509 self.curMacro.endRecording() |
|
4510 self.recording = False |
|
4511 |
|
4512 name, ok = QInputDialog.getText(\ |
|
4513 self, |
|
4514 self.trUtf8("Macro Recording"), |
|
4515 self.trUtf8("Enter name of the macro:"), |
|
4516 QLineEdit.Normal) |
|
4517 |
|
4518 if ok and name: |
|
4519 self.macros[name] = self.curMacro |
|
4520 |
|
4521 self.curMacro = None |
|
4522 |
|
4523 ################################################################# |
|
4524 ## Overwritten methods |
|
4525 ################################################################# |
|
4526 |
|
4527 def undo(self): |
|
4528 """ |
|
4529 Public method to undo the last recorded change. |
|
4530 """ |
|
4531 QsciScintillaCompat.undo(self) |
|
4532 self.emit(SIGNAL('undoAvailable'), self.isUndoAvailable()) |
|
4533 self.emit(SIGNAL('redoAvailable'), self.isRedoAvailable()) |
|
4534 |
|
4535 def redo(self): |
|
4536 """ |
|
4537 Public method to redo the last recorded change. |
|
4538 """ |
|
4539 QsciScintillaCompat.redo(self) |
|
4540 self.emit(SIGNAL('undoAvailable'), self.isUndoAvailable()) |
|
4541 self.emit(SIGNAL('redoAvailable'), self.isRedoAvailable()) |
|
4542 |
|
4543 def close(self, alsoDelete = False): |
|
4544 """ |
|
4545 Public method called when the window gets closed. |
|
4546 |
|
4547 This overwritten method redirects the action to our |
|
4548 ViewManager.closeEditor, which in turn calls our closeIt |
|
4549 method. |
|
4550 |
|
4551 @param alsoDelete ignored |
|
4552 """ |
|
4553 return self.vm.closeEditor(self) |
|
4554 |
|
4555 def closeIt(self): |
|
4556 """ |
|
4557 Public method called by the viewmanager to finally get rid of us. |
|
4558 """ |
|
4559 if Preferences.getEditor("ClearBreaksOnClose"): |
|
4560 self.__menuClearBreakpoints() |
|
4561 |
|
4562 for clone in self.__clones[:]: |
|
4563 clone.removeClone(self) |
|
4564 |
|
4565 self.disconnect(self.breakpointModel, |
|
4566 SIGNAL("rowsAboutToBeRemoved(const QModelIndex &, int, int)"), |
|
4567 self.__deleteBreakPoints) |
|
4568 self.disconnect(self.breakpointModel, |
|
4569 SIGNAL("dataAboutToBeChanged(const QModelIndex &, const QModelIndex &)"), |
|
4570 self.__breakPointDataAboutToBeChanged) |
|
4571 self.disconnect(self.breakpointModel, |
|
4572 SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), |
|
4573 self.__changeBreakPoints) |
|
4574 self.disconnect(self.breakpointModel, |
|
4575 SIGNAL("rowsInserted(const QModelIndex &, int, int)"), |
|
4576 self.__addBreakPoints) |
|
4577 |
|
4578 self.disconnect(e4App().getObject("Project"), SIGNAL("projectPropertiesChanged"), |
|
4579 self.__projectPropertiesChanged) |
|
4580 |
|
4581 if self.spell: |
|
4582 self.spell.stopIncrementalCheck() |
|
4583 |
|
4584 QsciScintillaCompat.close(self) |
|
4585 |
|
4586 def keyPressEvent(self, ev): |
|
4587 """ |
|
4588 Re-implemented to handle the user input a key at a time. |
|
4589 |
|
4590 @param ev key event (QKeyEvent) |
|
4591 """ |
|
4592 txt = ev.text() |
|
4593 key = ev.key() |
|
4594 |
|
4595 # See it is text to insert. |
|
4596 if len(txt) and txt >= " ": |
|
4597 QsciScintillaCompat.keyPressEvent(self, ev) |
|
4598 else: |
|
4599 ev.ignore() |
|
4600 |
|
4601 def focusInEvent(self, event): |
|
4602 """ |
|
4603 Protected method called when the editor receives focus. |
|
4604 |
|
4605 This method checks for modifications of the current file and |
|
4606 rereads it upon request. The cursor is placed at the current position |
|
4607 assuming, that it is in the vicinity of the old position after the reread. |
|
4608 |
|
4609 @param event the event object (QFocusEvent) |
|
4610 """ |
|
4611 self.vm.editActGrp.setEnabled(True) |
|
4612 self.vm.editorActGrp.setEnabled(True) |
|
4613 self.vm.copyActGrp.setEnabled(True) |
|
4614 self.vm.viewActGrp.setEnabled(True) |
|
4615 try: |
|
4616 self.setCaretWidth(self.caretWidth) |
|
4617 except AttributeError: |
|
4618 pass |
|
4619 self.__updateReadOnly(False) |
|
4620 if self.vm.editorsCheckFocusInEnabled() and \ |
|
4621 not self.inReopenPrompt and self.fileName and \ |
|
4622 QFileInfo(self.fileName).lastModified().toString() != \ |
|
4623 self.lastModified.toString(): |
|
4624 if Preferences.getEditor("AutoReopen") and not self.isModified(): |
|
4625 self.refresh() |
|
4626 else: |
|
4627 self.inReopenPrompt = True |
|
4628 msg = self.trUtf8(\ |
|
4629 """<p>The file <b>{0}</b> has been changed while it was opened in""" |
|
4630 """ eric4. Reread it?</p>""").format(self.fileName) |
|
4631 default = QMessageBox.No |
|
4632 if self.isModified(): |
|
4633 msg += self.trUtf8(\ |
|
4634 """<br><b>Warning:</b> You will loose""" |
|
4635 """ your changes upon reopening it.""") |
|
4636 default = QMessageBox.Ok |
|
4637 res = QMessageBox.warning(None, |
|
4638 self.trUtf8("File changed"), msg, |
|
4639 QMessageBox.StandardButtons(\ |
|
4640 QMessageBox.Yes | \ |
|
4641 QMessageBox.No), |
|
4642 default) |
|
4643 if res == QMessageBox.Yes: |
|
4644 self.refresh() |
|
4645 else: |
|
4646 # do not prompt for this change again... |
|
4647 self.lastModified = QFileInfo(self.fileName).lastModified() |
|
4648 self.inReopenPrompt = False |
|
4649 |
|
4650 QsciScintillaCompat.focusInEvent(self, event) |
|
4651 |
|
4652 def focusOutEvent(self, event): |
|
4653 """ |
|
4654 Public method called when the editor loses focus. |
|
4655 |
|
4656 @param event the event object (QFocusEvent) |
|
4657 """ |
|
4658 self.vm.editorActGrp.setEnabled(False) |
|
4659 self.setCaretWidth(0) |
|
4660 |
|
4661 QsciScintillaCompat.focusOutEvent(self, event) |
|
4662 |
|
4663 def changeEvent(self, evt): |
|
4664 """ |
|
4665 Protected method called to process an event. |
|
4666 |
|
4667 This implements special handling for the events showMaximized, |
|
4668 showMinimized and showNormal. The windows caption is shortened |
|
4669 for the minimized mode and reset to the full filename for the |
|
4670 other modes. This is to make the editor windows work nicer |
|
4671 with the QWorkspace. |
|
4672 |
|
4673 @param evt the event, that was generated (QEvent) |
|
4674 @return flag indicating if the event could be processed (bool) |
|
4675 """ |
|
4676 if evt.type() == QEvent.WindowStateChange and \ |
|
4677 self.fileName is not None: |
|
4678 if self.windowState() == Qt.WindowStates(Qt.WindowMinimized): |
|
4679 cap = os.path.basename(self.fileName) |
|
4680 else: |
|
4681 cap = self.fileName |
|
4682 if self.isReadOnly(): |
|
4683 cap = self.trUtf8("{0} (ro)").format(cap) |
|
4684 self.setWindowTitle(cap) |
|
4685 |
|
4686 QsciScintillaCompat.changeEvent(self, evt) |
|
4687 |
|
4688 def mousePressEvent(self, event): |
|
4689 """ |
|
4690 Protected method to handle the mouse press event. |
|
4691 |
|
4692 @param event the mouse press event (QMouseEvent) |
|
4693 """ |
|
4694 self.vm.eventFilter(self, event) |
|
4695 QsciScintillaCompat.mousePressEvent(self, event) |
|
4696 |
|
4697 def __updateReadOnly(self, bForce = True): |
|
4698 """ |
|
4699 Private method to update the readOnly information for this editor. |
|
4700 |
|
4701 If bForce is True, then updates everything regardless if |
|
4702 the attributes have actually changed, such as during |
|
4703 initialization time. A signal is emitted after the |
|
4704 caption change. |
|
4705 |
|
4706 @param bForce True to force change, False to only update and emit |
|
4707 signal if there was an attribute change. |
|
4708 """ |
|
4709 if self.fileName is None: |
|
4710 return |
|
4711 readOnly = not QFileInfo(self.fileName).isWritable() |
|
4712 if not bForce and (readOnly == self.isReadOnly()): |
|
4713 return |
|
4714 cap = self.fileName |
|
4715 if readOnly: |
|
4716 cap = self.trUtf8("{0} (ro)".format(cap)) |
|
4717 self.setReadOnly(readOnly) |
|
4718 self.setWindowTitle(cap) |
|
4719 self.emit(SIGNAL('captionChanged'), cap, self) |
|
4720 |
|
4721 def refresh(self): |
|
4722 """ |
|
4723 Public slot to refresh the editor contents. |
|
4724 """ |
|
4725 # save cursor position |
|
4726 cline, cindex = self.getCursorPosition() |
|
4727 |
|
4728 # save bookmarks and breakpoints and clear them |
|
4729 bmlist = self.getBookmarks() |
|
4730 self.clearBookmarks() |
|
4731 |
|
4732 # clear syntax error markers |
|
4733 self.clearSyntaxError() |
|
4734 |
|
4735 # clear breakpoint markers |
|
4736 for handle in self.breaks.keys(): |
|
4737 self.markerDeleteHandle(handle) |
|
4738 self.breaks = {} |
|
4739 |
|
4740 # reread the file |
|
4741 try: |
|
4742 self.readFile(self.fileName) |
|
4743 except IOError: |
|
4744 # do not prompt for this change again... |
|
4745 self.lastModified = QDateTime.currentDateTime() |
|
4746 self.setModified(False) |
|
4747 |
|
4748 # reset cursor position |
|
4749 self.setCursorPosition(cline, cindex) |
|
4750 self.ensureCursorVisible() |
|
4751 |
|
4752 # reset bookmarks and breakpoints to their old position |
|
4753 if bmlist: |
|
4754 for bm in bmlist: |
|
4755 self.toggleBookmark(bm) |
|
4756 self.__restoreBreakpoints() |
|
4757 |
|
4758 self.emit(SIGNAL('editorSaved'), self.fileName) |
|
4759 self.__autoSyntaxCheck() |
|
4760 |
|
4761 def setMonospaced(self, on): |
|
4762 """ |
|
4763 Public method to set/reset a monospaced font. |
|
4764 |
|
4765 @param on flag to indicate usage of a monospace font (boolean) |
|
4766 """ |
|
4767 if on: |
|
4768 f = Preferences.getEditorOtherFonts("MonospacedFont") |
|
4769 self.monospacedStyles(f) |
|
4770 else: |
|
4771 if not self.lexer_: |
|
4772 self.clearStyles() |
|
4773 self.__setMarginsDisplay() |
|
4774 self.setFont(Preferences.getEditorOtherFonts("DefaultFont")) |
|
4775 |
|
4776 self.useMonospaced = on |
|
4777 |
|
4778 ################################################################# |
|
4779 ## Drag and Drop Support |
|
4780 ################################################################# |
|
4781 |
|
4782 def dragEnterEvent(self, event): |
|
4783 """ |
|
4784 Protected method to handle the drag enter event. |
|
4785 |
|
4786 @param event the drag enter event (QDragEnterEvent) |
|
4787 """ |
|
4788 self.inDragDrop = event.mimeData().hasUrls() |
|
4789 if self.inDragDrop: |
|
4790 event.acceptProposedAction() |
|
4791 else: |
|
4792 QsciScintillaCompat.dragEnterEvent(self, event) |
|
4793 |
|
4794 def dragMoveEvent(self, event): |
|
4795 """ |
|
4796 Protected method to handle the drag move event. |
|
4797 |
|
4798 @param event the drag move event (QDragMoveEvent) |
|
4799 """ |
|
4800 if self.inDragDrop: |
|
4801 event.accept() |
|
4802 else: |
|
4803 QsciScintillaCompat.dragMoveEvent(self, event) |
|
4804 |
|
4805 def dragLeaveEvent(self, event): |
|
4806 """ |
|
4807 Protected method to handle the drag leave event. |
|
4808 |
|
4809 @param event the drag leave event (QDragLeaveEvent) |
|
4810 """ |
|
4811 if self.inDragDrop: |
|
4812 self.inDragDrop = False |
|
4813 event.accept() |
|
4814 else: |
|
4815 QsciScintillaCompat.dragLeaveEvent(self, event) |
|
4816 |
|
4817 def dropEvent(self, event): |
|
4818 """ |
|
4819 Protected method to handle the drop event. |
|
4820 |
|
4821 @param event the drop event (QDropEvent) |
|
4822 """ |
|
4823 if event.mimeData().hasUrls(): |
|
4824 for url in event.mimeData().urls(): |
|
4825 fname = url.toLocalFile() |
|
4826 if fname: |
|
4827 if not QFileInfo(fname).isDir(): |
|
4828 self.vm.openSourceFile(fname) |
|
4829 else: |
|
4830 QMessageBox.information(None, |
|
4831 self.trUtf8("Drop Error"), |
|
4832 self.trUtf8("""<p><b>{0}</b> is not a file.</p>""") |
|
4833 .format(fname)) |
|
4834 event.acceptProposedAction() |
|
4835 else: |
|
4836 QsciScintillaCompat.dropEvent(self, event) |
|
4837 |
|
4838 self.inDragDrop = False |
|
4839 |
|
4840 ################################################################# |
|
4841 ## Support for Qt resources files |
|
4842 ################################################################# |
|
4843 |
|
4844 def __initContextMenuResources(self): |
|
4845 """ |
|
4846 Private method used to setup the Resources context sub menu. |
|
4847 """ |
|
4848 menu = QMenu(self.trUtf8('Resources')) |
|
4849 |
|
4850 menu.addAction(self.trUtf8('Add file...'), |
|
4851 self.__addFileResource) |
|
4852 menu.addAction(self.trUtf8('Add files...'), |
|
4853 self.__addFileResources) |
|
4854 menu.addAction(self.trUtf8('Add aliased file...'), |
|
4855 self.__addFileAliasResource) |
|
4856 menu.addAction(self.trUtf8('Add localized resource...'), |
|
4857 self.__addLocalizedResource) |
|
4858 menu.addSeparator() |
|
4859 menu.addAction(self.trUtf8('Add resource frame'), |
|
4860 self.__addResourceFrame) |
|
4861 |
|
4862 self.connect(menu, SIGNAL('aboutToShow()'), self.__showContextMenuResources) |
|
4863 |
|
4864 return menu |
|
4865 |
|
4866 def __showContextMenuResources(self): |
|
4867 """ |
|
4868 Private slot handling the aboutToShow signal of the resources context menu. |
|
4869 """ |
|
4870 self.emit(SIGNAL("showMenu"), "Resources", self.resourcesMenu, self) |
|
4871 |
|
4872 def __addFileResource(self): |
|
4873 """ |
|
4874 Private method to handle the Add file context menu action. |
|
4875 """ |
|
4876 dirStr = os.path.dirname(self.fileName) |
|
4877 file = QFileDialog.getOpenFileName(\ |
|
4878 self, |
|
4879 self.trUtf8("Add file resource"), |
|
4880 dirStr, |
|
4881 "") |
|
4882 if file: |
|
4883 relFile = QDir(dirStr).relativeFilePath(file) |
|
4884 line, index = self.getCursorPosition() |
|
4885 self.insert(" <file>{0}</file>\n".format(relFile)) |
|
4886 self.setCursorPosition(line + 1, index) |
|
4887 |
|
4888 def __addFileResources(self): |
|
4889 """ |
|
4890 Private method to handle the Add files context menu action. |
|
4891 """ |
|
4892 dirStr = os.path.dirname(self.fileName) |
|
4893 files = QFileDialog.getOpenFileNames(\ |
|
4894 self, |
|
4895 self.trUtf8("Add file resources"), |
|
4896 dirStr, |
|
4897 "") |
|
4898 if files: |
|
4899 myDir = QDir(dirStr) |
|
4900 filesText = "" |
|
4901 for file in files: |
|
4902 relFile = myDir.relativeFilePath(file) |
|
4903 filesText += " <file>{0}</file>\n".format(relFile) |
|
4904 line, index = self.getCursorPosition() |
|
4905 self.insert(filesText) |
|
4906 self.setCursorPosition(line + len(files), index) |
|
4907 |
|
4908 def __addFileAliasResource(self): |
|
4909 """ |
|
4910 Private method to handle the Add aliased file context menu action. |
|
4911 """ |
|
4912 dirStr = os.path.dirname(self.fileName) |
|
4913 file = QFileDialog.getOpenFileName(\ |
|
4914 self, |
|
4915 self.trUtf8("Add aliased file resource"), |
|
4916 dirStr, |
|
4917 "") |
|
4918 if file: |
|
4919 relFile = QDir(dirStr).relativeFilePath(file) |
|
4920 alias, ok = QInputDialog.getText(\ |
|
4921 self, |
|
4922 self.trUtf8("Add aliased file resource"), |
|
4923 self.trUtf8("Alias for file <b>{0}</b>:").format(relFile), |
|
4924 QLineEdit.Normal, |
|
4925 relFile) |
|
4926 if ok and alias: |
|
4927 line, index = self.getCursorPosition() |
|
4928 self.insert(' <file alias="{1}">{0}</file>\n'\ |
|
4929 .format(relFile, alias)) |
|
4930 self.setCursorPosition(line + 1, index) |
|
4931 |
|
4932 def __addLocalizedResource(self): |
|
4933 """ |
|
4934 Private method to handle the Add localized resource context menu action. |
|
4935 """ |
|
4936 from Project.AddLanguageDialog import AddLanguageDialog |
|
4937 dlg = AddLanguageDialog(self) |
|
4938 if dlg.exec_() == QDialog.Accepted: |
|
4939 lang = dlg.getSelectedLanguage() |
|
4940 line, index = self.getCursorPosition() |
|
4941 self.insert('<qresource lang="{0}">\n</qresource>\n'.format(lang)) |
|
4942 self.setCursorPosition(line + 2, index) |
|
4943 |
|
4944 def __addResourceFrame(self): |
|
4945 """ |
|
4946 Private method to handle the Add resource frame context menu action. |
|
4947 """ |
|
4948 line, index = self.getCursorPosition() |
|
4949 self.insert('<!DOCTYPE RCC>\n' |
|
4950 '<RCC version="1.0">\n' |
|
4951 '<qresource>\n' |
|
4952 '</qresource>\n' |
|
4953 '</RCC>\n') |
|
4954 self.setCursorPosition(line + 5, index) |
|
4955 |
|
4956 ################################################################# |
|
4957 ## Support for diagrams below |
|
4958 ################################################################# |
|
4959 |
|
4960 def __showClassDiagram(self): |
|
4961 """ |
|
4962 Private method to handle the Class Diagram context menu action. |
|
4963 """ |
|
4964 from Graphics.UMLClassDiagram import UMLClassDiagram |
|
4965 if not self.checkDirty(): |
|
4966 return |
|
4967 |
|
4968 self.classDiagram = UMLClassDiagram(self.fileName, self, noAttrs = False) |
|
4969 self.classDiagram.show() |
|
4970 |
|
4971 def __showPackageDiagram(self): |
|
4972 """ |
|
4973 Private method to handle the Package Diagram context menu action. |
|
4974 """ |
|
4975 from Graphics.PackageDiagram import PackageDiagram |
|
4976 if not self.checkDirty(): |
|
4977 return |
|
4978 |
|
4979 package = os.path.isdir(self.fileName) and self.fileName \ |
|
4980 or os.path.dirname(self.fileName) |
|
4981 res = QMessageBox.question(None, |
|
4982 self.trUtf8("Package Diagram"), |
|
4983 self.trUtf8("""Include class attributes?"""), |
|
4984 QMessageBox.StandardButtons(\ |
|
4985 QMessageBox.No | \ |
|
4986 QMessageBox.Yes), |
|
4987 QMessageBox.Yes) |
|
4988 self.packageDiagram = PackageDiagram(package, self, |
|
4989 noAttrs = (res == QMessageBox.No)) |
|
4990 self.packageDiagram.show() |
|
4991 |
|
4992 def __showImportsDiagram(self): |
|
4993 """ |
|
4994 Private method to handle the Imports Diagram context menu action. |
|
4995 """ |
|
4996 from Graphics.ImportsDiagram import ImportsDiagram |
|
4997 if not self.checkDirty(): |
|
4998 return |
|
4999 |
|
5000 package = os.path.isdir(self.fileName) and self.fileName \ |
|
5001 or os.path.dirname(self.fileName) |
|
5002 res = QMessageBox.question(None, |
|
5003 self.trUtf8("Imports Diagram"), |
|
5004 self.trUtf8("""Include imports from external modules?"""), |
|
5005 QMessageBox.StandardButtons(\ |
|
5006 QMessageBox.No | \ |
|
5007 QMessageBox.Yes), |
|
5008 QMessageBox.No) |
|
5009 self.importsDiagram = ImportsDiagram(package, self, |
|
5010 showExternalImports = (res == QMessageBox.Yes)) |
|
5011 self.importsDiagram.show() |
|
5012 |
|
5013 def __showApplicationDiagram(self): |
|
5014 """ |
|
5015 Private method to handle the Imports Diagram context menu action. |
|
5016 """ |
|
5017 from Graphics.ApplicationDiagram import ApplicationDiagram |
|
5018 res = QMessageBox.question(None, |
|
5019 self.trUtf8("Application Diagram"), |
|
5020 self.trUtf8("""Include module names?"""), |
|
5021 QMessageBox.StandardButtons(\ |
|
5022 QMessageBox.No | \ |
|
5023 QMessageBox.Yes), |
|
5024 QMessageBox.Yes) |
|
5025 self.applicationDiagram = ApplicationDiagram(e4App().getObject("Project"), |
|
5026 self, noModules = (res == QMessageBox.No)) |
|
5027 self.applicationDiagram.show() |
|
5028 |
|
5029 ####################################################################### |
|
5030 ## Typing aids related methods below |
|
5031 ####################################################################### |
|
5032 |
|
5033 def __toggleTypingAids(self): |
|
5034 """ |
|
5035 Private slot to toggle the typing aids. |
|
5036 """ |
|
5037 if self.menuActs["TypingAidsEnabled"].isChecked(): |
|
5038 self.completer.setEnabled(True) |
|
5039 else: |
|
5040 self.completer.setEnabled(False) |
|
5041 |
|
5042 ####################################################################### |
|
5043 ## Autocompleting templates |
|
5044 ####################################################################### |
|
5045 |
|
5046 def editorCommand(self, cmd): |
|
5047 """ |
|
5048 Public method to perform a simple editor command. |
|
5049 |
|
5050 @param cmd the scintilla command to be performed |
|
5051 """ |
|
5052 if cmd == QsciScintilla.SCI_TAB: |
|
5053 line, index = self.getCursorPosition() |
|
5054 tmplName = self.getWordLeft(line, index) |
|
5055 if tmplName: |
|
5056 if e4App().getObject("TemplateViewer").hasTemplate(tmplName): |
|
5057 self.__applyTemplate(tmplName) |
|
5058 return |
|
5059 else: |
|
5060 templateNames = \ |
|
5061 e4App().getObject("TemplateViewer").getTemplateNames(tmplName) |
|
5062 if len(templateNames) == 1: |
|
5063 self.__applyTemplate(templateNames[0]) |
|
5064 return |
|
5065 elif len(templateNames) > 1: |
|
5066 self.showUserList(TemplateCompletionListID, |
|
5067 ["%s?%d" % (t, self.TemplateImageID) for t in templateNames]) |
|
5068 return |
|
5069 |
|
5070 QsciScintillaCompat.editorCommand(self, cmd) |
|
5071 |
|
5072 def __completionListSelected(self, id, txt): |
|
5073 """ |
|
5074 Private slot to handle the selection from the completion list. |
|
5075 |
|
5076 @param id the ID of the user list (should be 1) (integer) |
|
5077 @param txt the selected text (string) |
|
5078 """ |
|
5079 if id == TemplateCompletionListID: |
|
5080 self.__applyTemplate(txt) |
|
5081 |
|
5082 def __applyTemplate(self, templateName): |
|
5083 """ |
|
5084 Private method to apply a template by name. |
|
5085 |
|
5086 @param templateName name of the template to apply (string) |
|
5087 """ |
|
5088 if e4App().getObject("TemplateViewer").hasTemplate(templateName): |
|
5089 self.extendSelectionWordLeft() |
|
5090 e4App().getObject("TemplateViewer").applyNamedTemplate(templateName) |
|
5091 |
|
5092 ####################################################################### |
|
5093 ## Project related methods |
|
5094 ####################################################################### |
|
5095 |
|
5096 def __projectPropertiesChanged(self): |
|
5097 """ |
|
5098 Private slot to handle changes of the project properties. |
|
5099 """ |
|
5100 project = e4App().getObject("Project") |
|
5101 if self.spell: |
|
5102 pwl, pel = project.getProjectDictionaries() |
|
5103 self.__setSpellingLanguage(project.getProjectSpellLanguage(), |
|
5104 pwl = pwl, pel = pel) |
|
5105 |
|
5106 def addedToProject(self): |
|
5107 """ |
|
5108 Public method to signal, that this editor has been added to a project. |
|
5109 """ |
|
5110 project = e4App().getObject("Project") |
|
5111 if self.spell: |
|
5112 pwl, pel = project.getProjectDictionaries() |
|
5113 self.__setSpellingLanguage(project.getProjectSpellLanguage(), |
|
5114 pwl = pwl, pel = pel) |
|
5115 self.connect(project, SIGNAL("projectPropertiesChanged"), |
|
5116 self.__projectPropertiesChanged) |
|
5117 |
|
5118 ####################################################################### |
|
5119 ## Spellchecking related methods |
|
5120 ####################################################################### |
|
5121 |
|
5122 def __setSpellingLanguage(self, language, pwl = "", pel = ""): |
|
5123 """ |
|
5124 Private slot to set the spell checking language. |
|
5125 |
|
5126 @param language spell checking language to be set (string) |
|
5127 @keyparam pwl name of the personal/project word list (string) |
|
5128 @keyparam pel name of the personal/project exclude list (string) |
|
5129 """ |
|
5130 if self.spell and self.spell.getLanguage() != language: |
|
5131 self.spell.setLanguage(language, pwl = pwl, pel = pel) |
|
5132 self.spell.checkDocumentIncrementally() |
|
5133 |
|
5134 def __setSpelling(self): |
|
5135 """ |
|
5136 Private method to initialize the spell checking functionality. |
|
5137 """ |
|
5138 if Preferences.getEditor("SpellCheckingEnabled"): |
|
5139 self.__spellCheckStringsOnly = Preferences.getEditor("SpellCheckStringsOnly") |
|
5140 if self.spell is None: |
|
5141 self.spell = SpellChecker(self, self.spellingIndicator, |
|
5142 checkRegion = self.isSpellCheckRegion) |
|
5143 self.setSpellingForProject() |
|
5144 self.connect(e4App().getObject("Project"), SIGNAL("projectPropertiesChanged"), |
|
5145 self.__projectPropertiesChanged) |
|
5146 self.spell.setMinimumWordSize( |
|
5147 Preferences.getEditor("SpellCheckingMinWordSize")) |
|
5148 |
|
5149 self.setAutoSpellChecking() |
|
5150 else: |
|
5151 self.spell = None |
|
5152 self.clearAllIndicators(self.spellingIndicator) |
|
5153 |
|
5154 def setSpellingForProject(self): |
|
5155 """ |
|
5156 Public method to set the spell checking options for files belonging |
|
5157 to the current project. |
|
5158 """ |
|
5159 project = e4App().getObject("Project") |
|
5160 if project.isOpen() and project.isProjectSource(self.fileName): |
|
5161 pwl, pel = project.getProjectDictionaries() |
|
5162 self.__setSpellingLanguage(project.getProjectSpellLanguage(), |
|
5163 pwl = pwl, pel = pel) |
|
5164 |
|
5165 def setAutoSpellChecking(self): |
|
5166 """ |
|
5167 Public method to set the automatic spell checking. |
|
5168 """ |
|
5169 if Preferences.getEditor("AutoSpellCheckingEnabled"): |
|
5170 self.connect(self, SIGNAL("SCN_CHARADDED(int)"), self.__spellCharAdded) |
|
5171 self.spell.checkDocumentIncrementally() |
|
5172 else: |
|
5173 self.disconnect(self, SIGNAL("SCN_CHARADDED(int)"), self.__spellCharAdded) |
|
5174 self.clearAllIndicators(self.spellingIndicator) |
|
5175 |
|
5176 def isSpellCheckRegion(self, pos): |
|
5177 """ |
|
5178 Public method to check, if the given position is within a region, that should |
|
5179 be spell checked. |
|
5180 |
|
5181 @param pos position to be checked (integer) |
|
5182 @return flag indicating pos is in a spell check region (boolean) |
|
5183 """ |
|
5184 if self.__spellCheckStringsOnly: |
|
5185 style = self.styleAt(pos) |
|
5186 if self.lexer_ is not None: |
|
5187 return self.lexer_.isCommentStyle(style) or \ |
|
5188 self.lexer_.isStringStyle(style) |
|
5189 return True |
|
5190 |
|
5191 def __spellCharAdded(self, charNumber): |
|
5192 """ |
|
5193 Public slot called to handle the user entering a character. |
|
5194 |
|
5195 @param charNumber value of the character entered (integer) |
|
5196 """ |
|
5197 if self.spell: |
|
5198 if not unichr(charNumber).isalnum(): |
|
5199 self.spell.checkWord(self.positionBefore(self.currentPosition()), True) |
|
5200 elif self.hasIndicator(self.spellingIndicator, self.currentPosition()): |
|
5201 self.spell.checkWord(self.currentPosition()) |
|
5202 |
|
5203 def checkSpelling(self): |
|
5204 """ |
|
5205 Public slot to perform an interactive spell check of the document. |
|
5206 """ |
|
5207 if self.spell: |
|
5208 cline, cindex = self.getCursorPosition() |
|
5209 dlg = SpellCheckingDialog(self.spell, 0, self.length(), self) |
|
5210 dlg.exec_() |
|
5211 self.setCursorPosition(cline, cindex) |
|
5212 if Preferences.getEditor("AutoSpellCheckingEnabled"): |
|
5213 self.spell.checkDocumentIncrementally() |
|
5214 |
|
5215 def __checkSpellingSelection(self): |
|
5216 """ |
|
5217 Private slot to spell check the current selection. |
|
5218 """ |
|
5219 sline, sindex, eline, eindex = self.getSelection() |
|
5220 startPos = self.positionFromLineIndex(sline, sindex) |
|
5221 endPos = self.positionFromLineIndex(eline, eindex) |
|
5222 dlg = SpellCheckingDialog(self.spell, startPos, endPos, self) |
|
5223 dlg.exec_() |
|
5224 |
|
5225 def __checkSpellingWord(self): |
|
5226 """ |
|
5227 Private slot to check the word below the spelling context menu. |
|
5228 """ |
|
5229 line, index = self.lineIndexFromPosition(self.spellingMenuPos) |
|
5230 wordStart, wordEnd = self.getWordBoundaries(line, index) |
|
5231 wordStartPos = self.positionFromLineIndex(line, wordStart) |
|
5232 wordEndPos = self.positionFromLineIndex(line, wordEnd) |
|
5233 dlg = SpellCheckingDialog(self.spell, wordStartPos, wordEndPos, self) |
|
5234 dlg.exec_() |
|
5235 |
|
5236 def __showContextMenuSpelling(self): |
|
5237 """ |
|
5238 Private slot to set up the spelling menu before it is shown. |
|
5239 """ |
|
5240 self.spellingMenu.clear() |
|
5241 self.spellingSuggActs = [] |
|
5242 line, index = self.lineIndexFromPosition(self.spellingMenuPos) |
|
5243 word = self.getWord(line, index) |
|
5244 suggestions = self.spell.getSuggestions(word) |
|
5245 for suggestion in suggestions[:5]: |
|
5246 self.spellingSuggActs.append(self.spellingMenu.addAction(suggestion)) |
|
5247 if suggestions: |
|
5248 self.spellingMenu.addSeparator() |
|
5249 self.spellingMenu.addAction(UI.PixmapCache.getIcon("spellchecking.png"), |
|
5250 self.trUtf8("Check spelling..."), self.__checkSpellingWord) |
|
5251 self.spellingMenu.addAction(self.trUtf8("Add to dictionary"), |
|
5252 self.__addToSpellingDictionary) |
|
5253 self.spellingMenu.addAction(self.trUtf8("Ignore All"), |
|
5254 self.__ignoreSpellingAlways) |
|
5255 |
|
5256 self.emit(SIGNAL("showMenu"), "Spelling", self.spellingMenu, self) |
|
5257 |
|
5258 def __contextMenuSpellingTriggered(self, action): |
|
5259 """ |
|
5260 Private slot to handle the selection of a suggestion of the spelling context menu. |
|
5261 |
|
5262 @param action reference to the action that was selected (QAction) |
|
5263 """ |
|
5264 if action in self.spellingSuggActs: |
|
5265 replacement = action.text() |
|
5266 line, index = self.lineIndexFromPosition(self.spellingMenuPos) |
|
5267 wordStart, wordEnd = self.getWordBoundaries(line, index) |
|
5268 self.setSelection(line, wordStart, line, wordEnd) |
|
5269 self.beginUndoAction() |
|
5270 self.removeSelectedText() |
|
5271 self.insert(replacement) |
|
5272 self.endUndoAction() |
|
5273 |
|
5274 def __addToSpellingDictionary(self): |
|
5275 """ |
|
5276 Private slot to add the word below the spelling context menu to the dictionary. |
|
5277 """ |
|
5278 line, index = self.lineIndexFromPosition(self.spellingMenuPos) |
|
5279 word = self.getWord(line, index) |
|
5280 self.spell.add(word) |
|
5281 |
|
5282 wordStart, wordEnd = self.getWordBoundaries(line, index) |
|
5283 self.clearIndicator(self.spellingIndicator, line, wordStart, line, wordEnd) |
|
5284 if Preferences.getEditor("AutoSpellCheckingEnabled"): |
|
5285 self.spell.checkDocumentIncrementally() |
|
5286 |
|
5287 def __removeFromSpellingDictionary(self): |
|
5288 """ |
|
5289 Private slot to remove the word below the context menu to the dictionary. |
|
5290 """ |
|
5291 line, index = self.lineIndexFromPosition(self.spellingMenuPos) |
|
5292 word = self.getWord(line, index) |
|
5293 self.spell.remove(word) |
|
5294 |
|
5295 if Preferences.getEditor("AutoSpellCheckingEnabled"): |
|
5296 self.spell.checkDocumentIncrementally() |
|
5297 |
|
5298 def __ignoreSpellingAlways(self): |
|
5299 """ |
|
5300 Private to always ignore the word below the spelling context menu. |
|
5301 """ |
|
5302 line, index = self.lineIndexFromPosition(self.spellingMenuPos) |
|
5303 word = self.getWord(line, index) |
|
5304 self.spell.ignoreAlways(word) |
|
5305 if Preferences.getEditor("AutoSpellCheckingEnabled"): |
|
5306 self.spell.checkDocumentIncrementally() |