# -*- coding: utf-8 -*-
# Copyright (c) 2002, 2003 Detlev Offenbach <detlev@die-offenbachs.de>
#
"""
Module implementing the editor component of the eric3 IDE.
"""
import re
import os
from qtext import QextScintilla, QextScintillaPrinter, QextScintillaMacro
from qt import *
from Icons.FileIcons import IconFilePython
from Debugger.DebugIcons import IconBreak, IconCBreak
from Checks.TabnannyDialog import TabnannyDialog
from Checks.SyntaxCheckerDialog import SyntaxCheckerDialog
from UI.CodeMetricsDialog import CodeMetricsDialog
from UI.PyCoverageDialog import PyCoverageDialog
from UI.PyProfileDialog import PyProfileDialog
from EditorIcons import *
from Icons.Icons import IconClose
from Printer import Printer
import Preferences
import Utilities
class Editor(QextScintilla):
"""
Class implementing the editor component of the eric3 IDE.
Signals
modificationStatusChanged(boolean, editor) -- emitted when the
modification status has changed
cursorChanged(string, int, int) -- emitted when the cursor position
was changed
editorSaved(string) -- emitted after the editor has been saved
captionChanged(string, editor) -- emitted when the caption is
updated. Typically due to a readOnly attribute change.
"""
def __init__(self,dbs,fn=None,parent=None,name=None,flags=0):
"""
Constructor
Arguments
dbs -- reference to the debug server object
fn -- name of the file to be opened (string). If it is None,
a new (empty) editor is opened
parent -- parent widget (viewmanager) of this editor (QWidget)
name -- name of this editor (string or QString)
flags -- window flags
"""
QextScintilla.__init__(self, parent, name, flags | Qt.WDestructiveClose)
self.setUtf8(1)
self.dbs = dbs
self.fileName = fn
self.vm = parent
# clear some variables
self.lastHighlight = None # remember the last highlighted line
self.lastErrorMarker = None # remember the last error line
self.lastCurrMarker = None # remember the last current line
self.breaks = {} # key: marker handle, value: (lineno, condition)
self.condHistory = QStringList()
self.lexer = None
self.encoding = None
self.apiLanguage = ''
self.lastModified = 0
self.fileInfo = None
self.zoom = 0
self.inReopenPrompt = 0
# true when the prompt to reload a changed source is present
self.macros = {} # list of defined macros
self.curMacro = None
self.recording = 0
self.connect(self, SIGNAL('modificationChanged(bool)'),
self.handleModificationChanged)
self.connect(self, SIGNAL('cursorPositionChanged(int,int)'),
self.handleCursorPositionChanged)
try:
self.connect(self, SIGNAL('modificationAttempted()'),
self.handleModificationReadOnly)
except:
pass
# define the markers
# markers for margin 1
try:
self.breakpoint = self.markerDefine(IconBreak)
self.cbreakpoint = self.markerDefine(IconCBreak)
except:
self.breakpoint = self.markerDefine(QextScintilla.Circle)
self.setMarkerForegroundColor(Qt.red, self.breakpoint)
self.setMarkerBackgroundColor(Qt.red, self.breakpoint)
self.cbreakpoint = self.markerDefine(QextScintilla.Circle)
self.setMarkerForegroundColor(QColor("#FFAA00"), self.cbreakpoint)
self.setMarkerBackgroundColor(QColor("#FFAA00"), self.cbreakpoint)
# set the line markers
self.currentline = self.markerDefine(QextScintilla.Background)
self.errorline = self.markerDefine(QextScintilla.Background)
self.setLineMarkerColours()
margin1Mask = (1 << self.breakpoint) | \
(1 << self.cbreakpoint) | \
(1 << self.currentline) | \
(1 << self.errorline)
# configure the margins
# margin 1 for markers (i.e. breakpoints, highlights)
self.setMarginWidth(1, 16)
self.setMarginSensitivity(1, 1)
self.setMarginMarkerMask(1, margin1Mask)
self.connect(self, SIGNAL('marginClicked(int, int, Qt::ButtonState)'),
self.handleMarginClicked)
# set margin 0 and 2 configuration
# margin 0 is used for line numbers
# margin 2 is the folding margin
self.setMargin0and2()
if self.fileName is not None:
self.fileInfo = QFileInfo(self.fileName)
self.fileInfo.setCaching(0)
self.readFile(self.fileName)
self.gotoLine(0)
ext = self.getExtension(self.fileName)
self.bindLexer(ext)
# set the text display
self.setTextDisplay()
# set the autocompletion and calltips function
self.setAutoCompletion()
self.setCallTips()
# perform automatic eol conversion
if Preferences.getEditor("AutomaticEOLConversion"):
self.convertEols(self.eolMode())
sh = self.sizeHint()
if sh.height() < 300:
sh.setHeight(300)
self.resize(sh)
# Make sure tabbing through a QWorkspace works.
self.setFocusPolicy(QWidget.StrongFocus)
self._updateReadOnly(1)
QWhatsThis.add(self,self.trUtf8(
"""<b>A Source Editor Window</b>"""
"""<p>This window is used to display and edit a Python source file."""
""" You can open as many of these as you like. The name of the file"""
""" is displayed in the window's titlebar.</p>"""
"""<p>In order to set breakpoints just click in the space between"""
""" the line numbers and the fold markers. If you press the Shift-key"""
""" while clicking in this space, you are presented with a dialog"""
""" to enter a breakpoint condition.</p>"""
))
# Set the editors size if it is too big for the parent.
if parent is not None:
req = self.size()
bnd = req.boundedTo(parent.size())
if bnd.width() < req.width() or bnd.height() < req.height():
self.resize(bnd)
self.initContextMenu()
def initContextMenu(self):
"""
Private method used to setup the context menu
"""
self.menuIds = {}
self.menu = QPopupMenu()
self.checksMenu = self.initContextMenuChecks()
self.showMenu = self.initContextMenuShow()
self.languagesMenu = self.initContextMenuLanguages()
self.menuIds["Undo"] = \
self.menu.insertItem(QIconSet(IconEditUndo),
self.trUtf8('Undo'), self.undo)
self.menuIds["Redo"] = \
self.menu.insertItem(QIconSet(IconEditRedo),
self.trUtf8('Redo'), self.redo)
self.menuIds["Revert"] = \
self.menu.insertItem(self.trUtf8("Revert to last saved state"),
self.revertToUnmodified)
self.menu.insertSeparator()
self.menuIds["Cut"] = \
self.menu.insertItem(QIconSet(IconEditCut),
self.trUtf8('Cut'), self.cut)
self.menuIds["Copy"] = \
self.menu.insertItem(QIconSet(IconEditCopy),
self.trUtf8('Copy'), self.copy)
self.menu.insertItem(QIconSet(IconEditPaste),
self.trUtf8('Paste'), self.paste)
self.menu.insertSeparator()
self.menu.insertItem(QIconSet(IconEditIndent),
self.trUtf8('Indent'), self.indentLineOrSelection)
self.menu.insertItem(QIconSet(IconEditUnindent),
self.trUtf8('Unindent'), self.unindentLineOrSelection)
self.menuIds["Comment"] = \
self.menu.insertItem(QIconSet(IconEditComment),
self.trUtf8('Comment'), self.commentLineOrSelection)
self.menuIds["Uncomment"] = \
self.menu.insertItem(QIconSet(IconEditUncomment),
self.trUtf8('Uncomment'), self.uncommentLineOrSelection)
self.menuIds["StreamComment"] = \
self.menu.insertItem(self.trUtf8('Stream Comment'),
self.streamCommentLineOrSelection)
self.menuIds["BoxComment"] = \
self.menu.insertItem(self.trUtf8('Box Comment'),
self.boxCommentLineOrSelection)
self.menu.insertSeparator()
self.menu.insertItem(self.trUtf8('Select to brace'), self.selectToMatchingBrace)
self.menu.insertItem(self.trUtf8('Select all'), self.handleSelectAll)
self.menu.insertItem(self.trUtf8('Deselect all'), self.handleDeselectAll)
self.menu.insertSeparator()
self.menuIds["Languages"] = \
self.menu.insertItem(self.trUtf8("Languages"), self.languagesMenu)
self.menu.insertItem(self.trUtf8('Autocomplete from Document'),
self.autoCompleteFromDocument)
self.menuIds["acAPI"] = \
self.menu.insertItem(self.trUtf8('Autocomplete from APIs'),
self.autoCompleteFromAPIs)
self.menu.insertSeparator()
self.menu.insertItem(self.trUtf8('Set/Reset breakpoint'),
self.handleToggleBreakpoint)
self.menu.insertItem(self.trUtf8('Set/Change conditional breakpoint'),
self.handleToggleCBreakpoint)
self.menu.insertSeparator()
self.menuIds["Check"] = \
self.menu.insertItem(self.trUtf8('Check'), self.checksMenu)
self.menu.insertSeparator()
self.menuIds["Show"] = \
self.menu.insertItem(self.trUtf8('Show'), self.showMenu)
self.menu.insertSeparator()
self.menu.insertItem(QIconSet(IconClose),
self.trUtf8('Close'), self.handleContextClose)
self.menu.insertSeparator()
self.menuIds["Save"] = \
self.menu.insertItem(QIconSet(IconFileSave),
self.trUtf8('Save'), self.handleContextSave)
self.menu.insertItem(QIconSet(IconFileSaveAs),
self.trUtf8('Save As...'), self.handleContextSaveAs)
self.menu.insertSeparator()
self.menu.insertItem(QIconSet(IconPrint),
self.trUtf8('Print'), self.printFile)
self.menuIds["PrintSel"] = \
self.menu.insertItem(QIconSet(IconPrint),
self.trUtf8('Print Selection'), self.printSelection)
self.connect(self.menu, SIGNAL('aboutToShow()'), self.handleShowContextMenu)
def initContextMenuChecks(self):
"""
Private method used to setup the Checks context sub menu.
"""
menu = QPopupMenu()
menu.insertItem(self.trUtf8('Syntax...'), self.handleSyntaxCheck)
menu.insertItem(self.trUtf8('Indentations...'), self.handleTabnanny)
return menu
def initContextMenuShow(self):
"""
Private method used to setup the Show context sub menu.
"""
menu = QPopupMenu()
menu.insertItem(self.trUtf8('Code metrics...'), self.handleCodeMetrics)
self.coverageMenuItem = \
menu.insertItem(self.trUtf8('Code coverage...'), self.handleCodeCoverage)
self.profileMenuItem = \
menu.insertItem(self.trUtf8('Profile data...'), self.handleProfileData)
self.connect(menu, SIGNAL('aboutToShow()'), self.handleShowShowMenu)
return menu
def initContextMenuLanguages(self):
"""
Private method used to setup the Languages context sub menu.
"""
menu = QPopupMenu()
# this list must be kept in synch with the supported languages and the order
# of the menu items defined below.
self.supportedLanguages = [\
(self.trUtf8("C/C++"), 'cpp'),
(self.trUtf8("C#"), 'cs'),
(self.trUtf8("HTML"), 'html'),
(self.trUtf8("IDL"), 'idl'),
(self.trUtf8("Java"), 'java'),
(self.trUtf8("JavaScript"), 'js'),
(self.trUtf8("PHP"), 'php'),
(self.trUtf8("Pyrex"), 'pyx'),
(self.trUtf8("Python"), 'py'),
(self.trUtf8("SQL"), 'sql'),
(self.trUtf8("XML"), 'xml'),
]
idx = 0
for lang in self.supportedLanguages:
id = menu.insertItem(lang[0], self.handleLanguage)
menu.setItemParameter(id, idx)
idx += 1
return menu
def handleLanguage(self, idx):
"""
Private method to handle the selection of a lexer language.
Arguments
idx -- index of the lexer language (int)
"""
self.setLanguage(self.supportedLanguages[idx][1])
def setLanguage(self, language):
"""
Private method to set a lexer language.
Arguments
language -- language (extension) of the desired lexer (string)
"""
self.bindLexer(language)
# set the autocompletion and calltips function
self.setAutoCompletion()
self.setCallTips()
def getExtension(self, fname):
"""
Private method to get the fileextension without a leading '.'.
Arguments
fname -- filename (string)
Returns
extension of the filename (string)
"""
dummy, ext = os.path.splitext(fname)
# now split off .
if ext.startswith('.'):
return ext[1:]
else:
return ext
def bindLexer(self, language):
"""
Private slot to set the correct lexer depending on language.
Arguments
language -- language (extension) of the desired lexer (string)
"""
if language in ['py', 'pyx']:
from LexerPython import LexerPython
self.lexer = LexerPython(self)
self.apiLanguage = "Python"
elif language in ['cpp', 'cxx', 'cc', 'c', 'hpp', 'hh', 'h']:
from LexerCPP import LexerCPP
self.lexer = LexerCPP(self)
self.apiLanguage = "C++"
elif language == 'cs':
from LexerCSharp import LexerCSharp
self.lexer = LexerCSharp(self)
self.apiLanguage = "C#"
elif language == 'idl':
from LexerIDL import LexerIDL
self.lexer = LexerIDL(self)
self.apiLanguage = "IDL"
elif language == 'java':
from LexerJava import LexerJava
self.lexer = LexerJava(self)
self.apiLanguage = "Java"
elif language == 'js':
from LexerJavaScript import LexerJavaScript
self.lexer = LexerJavaScript(self)
self.apiLanguage = "JavaScript"
elif language == 'sql':
try:
from LexerSQL import LexerSQL
self.lexer = LexerSQL(self)
self.apiLanguage = "SQL"
except ImportError:
return
elif language in ['html', 'htm', 'asp', 'shtml', 'css', \
'php', 'php3', 'php4', 'phtml', 'xml', 'xsl', \
'xslt', 'dtd', 'docbook']:
try:
from LexerHTML import LexerHTML
self.lexer = LexerHTML(self)
self.apiLanguage = "HTML"
except ImportError:
return
else:
return
# get the font for style 0 and set it as the default font
key = '/eric3/Scintilla/%s/style0/font' % str(self.lexer.language())
fontstr, ok = Preferences.Prefs.settings.readEntry(key)
if ok:
fdesc = QStringList.split(',', fontstr)
font = QFont(fdesc[0], int(str(fdesc[1])))
self.lexer.setDefaultFont(font)
self.setLexer(self.lexer)
self.lexer.readSettings(Preferences.Prefs.settings, "/eric3/Scintilla")
# now set the lexer properties
if language in ['py', 'pyx']:
# Python lexer stuff
self.lexer.setIndentationWarning(Preferences.getEditor("PythonBadIndentation"))
self.lexer.setFoldComments(Preferences.getEditor("PythonFoldComment"))
self.lexer.setFoldQuotes(Preferences.getEditor("PythonFoldString"))
self.lexer.setAutoIndentStyle(QextScintilla.AiMaintain)
elif language in ['cpp', 'cxx', 'cc', 'c', 'hpp', 'hh', 'h', 'cs', 'idl', 'java', 'js']:
# C++ lexer stuff
self.lexer.setFoldComments(Preferences.getEditor("CppFoldComment"))
self.lexer.setFoldPreprocessor(Preferences.getEditor("CppFoldPreprocessor"))
try:
self.lexer.setFoldAtElse(Preferences.getEditor("CppFoldAtElse"))
except AttributeError:
pass
self.lexer.setAutoIndentStyle(QextScintilla.AiOpening | QextScintilla.AiClosing)
elif language in ['html', 'htm', 'asp', 'shtml', 'css', \
'php', 'php3', 'php4', 'phtml', 'xml', 'xsl', \
'xslt', 'dtd', 'docbook']:
try:
self.lexer.setFoldPreprocessor(Preferences.getEditor("CppFoldPreprocessor"))
self.lexer.setCaseSensitiveTags(Preferences.getEditor("HtmlCaseSensitiveTags"))
except AttributeError:
pass
def getLexer(self):
"""
Public method to retrieve a reference to the lexer object.
Returns
the lexer object (Lexer)
"""
return self.lexer
def handleModificationChanged(self, m):
"""
Private slot to handle the modificationChanged signal.
It emits the signal modificationStatusChanged with parameters
m and self.
Arguments
m -- modification status
"""
self.emit(PYSIGNAL('modificationStatusChanged'), (m, self))
def handleCursorPositionChanged(self, line, pos):
"""
Private slot to handle the cursorPositionChanged signal.
It emits the signal cursorChanged with parameters fileName,
line and pos.
Arguments
line -- line number of the cursor
pos -- position in line of the cursor
"""
self.emit(PYSIGNAL('cursorChanged'), (self.fileName, line+1, pos))
def handleModificationReadOnly(self):
"""
Private slot to handle the modificationAttempted signal.
"""
QMessageBox.warning(None,
self.trUtf8("Modification of Read Only file"),
self.trUtf8("""You are attempting to change a read only file. Please save to a different file first."""),
self.trUtf8("&OK"),
None,
None,
0, -1)
def getFileName(self):
"""
Public method to return the name of the file being displayed.
Returns
filename of the displayed file (string)
"""
return self.fileName
def highlightVisible(self):
"""
Public method to make sure that the highlight is visible.
"""
if self.lastHighlight is not None:
lineno = self.markerLine(self.lastHighlight)
self.ensureVisible(lineno+1)
def highlight(self,line=None,error=0):
"""
Public method to highlight (or de-highlight) a particular line.
Arguments
line -- line number to highlight
error -- flag indicating whether the error highlight should be used
"""
if line is None:
self.lastHighlight = None
if self.lastErrorMarker is not None:
self.markerDeleteHandle(self.lastErrorMarker)
self.lastErrorMarker = None
if self.lastCurrMarker is not None:
self.markerDeleteHandle(self.lastCurrMarker)
self.lastCurrMarker = None
else:
if error:
if self.lastErrorMarker is not None:
self.markerDeleteHandle(self.lastErrorMarker)
self.lastErrorMarker = self.markerAdd(line-1, self.errorline)
self.lastHighlight = self.lastErrorMarker
else:
if self.lastCurrMarker is not None:
self.markerDeleteHandle(self.lastCurrMarker)
self.lastCurrMarker = self.markerAdd(line-1, self.currentline)
self.lastHighlight = self.lastCurrMarker
def getHighlightPosition(self):
"""
Public method to return the position of the highlight bar.
Returns
line number of the highlight bar
"""
if self.lastHighlight is not None:
return self.markerLine(self.lastHighlight)
else:
return 1
def handleBreakpoint(self, line, state, newcond = None):
"""
Public method to set, delete and change a breakpoint.
Arguments
line -- line number of the breakpoint
state -- mouse button state (only Qt.LeftButton and Qt.ShiftButton
is accepted)
newcond -- new breakpoint condition
"""
for handle, (ln, cond) in self.breaks.items():
if ln == line:
break
else:
# set a new breakpoint
if state & Qt.RightButton or state & Qt.MidButton:
return
if state & Qt.ShiftButton:
marker = self.cbreakpoint
else:
marker = self.breakpoint
handle = self.markerAdd(line-1, marker)
cond = None
# get condition for a conditional breakpoint
if state & Qt.ShiftButton:
if newcond:
cond = QString(newcond)
ok = 1
else:
if len(self.condHistory) > 0:
curr = 0
else:
curr = -1
cond, ok = QInputDialog.getItem(
self.trUtf8('Conditional Breakpoint'),
self.trUtf8('Enter condition for breakpoint'),
self.condHistory, curr, 1)
if not ok or cond.isEmpty():
self.markerDeleteHandle(handle)
return
else:
self.condHistory.remove(cond)
self.condHistory.prepend(cond)
self.breaks[handle] = (line, cond)
self.dbs.remoteBreakpoint(self.fileName,line,1,cond)
return
# we are not interested in right or middle button presses
if state & Qt.RightButton or state & Qt.MidButton:
return
# change condition (left button + shift) or delete (left button) breakpoint
if state & Qt.ShiftButton:
# change or remove condition
oldcond = cond
if newcond:
cond = QString(newcond)
ok = 1
else:
if cond == None:
cond = ''
curr = self.condHistory.findIndex(cond)
if curr == -1:
self.condHistory.prepend(cond)
curr = 0
cond, ok = QInputDialog.getItem(
self.trUtf8('Breakpoint'),
self.trUtf8('Enter condition for breakpoint'),
self.condHistory, curr, 1)
if not ok:
return
elif cond.isEmpty():
# change a conditional bp to an unconditional bp
del self.breaks[handle]
self.markerDeleteHandle(handle)
handle = self.markerAdd(line-1, self.breakpoint)
cond = None
else:
# change condition of the bp
self.condHistory.remove(cond)
self.condHistory.prepend(cond)
if oldcond == None:
# change unconditional bp to conditional bp
del self.breaks[handle]
self.markerDeleteHandle(handle)
handle = self.markerAdd(line-1, self.cbreakpoint)
self.dbs.remoteBreakpoint(self.fileName,line,0,oldcond)
self.breaks[handle] = (line, cond)
self.dbs.remoteBreakpoint(self.fileName,line,1,cond)
# delete breakpoint
else:
del self.breaks[handle]
self.markerDeleteHandle(handle)
self.dbs.remoteBreakpoint(self.fileName,line,0,cond)
def handleToggleBreakpoint(self):
"""
Private slot to handle the 'Set/Reset breakpoint' context menu action.
"""
line, index = self.getCursorPosition()
line += 1
state = Qt.LeftButton
self.handleBreakpoint(line, state)
def handleToggleCBreakpoint(self):
"""
Private slot to handle the 'SetChange conditional breakpoint' context menu action.
"""
line, index = self.getCursorPosition()
line += 1
state = Qt.LeftButton | Qt.ShiftButton
self.handleBreakpoint(line, state)
def sendAllBreakpoints(self):
"""
Private slot to send all breakpoints to the debug server.
"""
self.dbs.clearFileBreakpoints(self.fileName)
for handle, (line, cond) in self.breaks.items():
line = self.markerLine(handle)+1
self.breaks[handle] = (line, cond)
self.dbs.remoteBreakpoint(self.fileName, line, 1, cond)
def printFile(self):
"""
Public slot to print the text.
"""
printer = Printer()
sb = qApp.mainWidget().statusBar()
if printer.setup(self):
sb.message(self.trUtf8('Printing...'))
qApp.setOverrideCursor(Qt.waitCursor)
qApp.processEvents()
fn = self.getFileName()
if fn is not None:
printer.setDocName(os.path.basename(fn))
else:
printer.setDocName(self.trUtf8('Noname'))
res = printer.printRange(self)
qApp.restoreOverrideCursor()
if res:
sb.message(self.trUtf8('Printing completed'), 2000)
else:
sb.message(self.trUtf8('Error while printing'), 2000)
else:
sb.message(self.trUtf8('Printing aborted'), 2000)
def printSelection(self):
"""
Public slot to print the selected text.
"""
if not self.hasSelectedText():
QMessageBox.warning(self, self.trUtf8('Print Selection'),
self.trUtf8('There is no selected text, printing aborted.'),
QMessageBox.Ok, QMessageBox.NoButton)
return
# get the selection
fromLine, fromIndex, toLine, toIndex = self.getSelection()
printer = Printer()
sb = qApp.mainWidget().statusBar()
if printer.setup(self):
sb.message(self.trUtf8('Printing...'))
qApp.setOverrideCursor(Qt.waitCursor)
qApp.processEvents()
fn = self.getFileName()
if fn is not None:
printer.setDocName(os.path.basename(fn))
else:
printer.setDocName(self.trUtf8('Noname'))
res = printer.printRange(self, fromLine, toLine)
qApp.restoreOverrideCursor()
if res:
sb.message(self.trUtf8('Printing completed'), 2000)
else:
sb.message(self.trUtf8('Error while printing'), 2000)
else:
sb.message(self.trUtf8('Printing aborted'), 2000)
def readFile(self, fn):
"""
Public slot to read the text from a file.
Arguments
fn -- filename to read from (string or QString)
"""
fn = str(fn)
try:
f = open(fn, 'rb')
except:
QMessageBox.critical(self.vm, self.trUtf8('Open File'),
self.trUtf8('The file <b>%1</b> could not be opened.')
.arg(fn))
raise
QApplication.setOverrideCursor(Qt.waitCursor)
txt, self.encoding = Utilities.decode(f.read())
if (not Preferences.getEditor("TabForIndentation")) and \
Preferences.getEditor("ConvertTabsOnLoad"):
txt = txt.expandtabs(Preferences.getEditor("TabWidth"))
self.setText(txt)
f.close()
QApplication.restoreOverrideCursor()
self.lastModified = self.fileInfo.lastModified()
def writeFile(self, fn):
"""
Public slot to write the text to a file.
Arguments
fn -- filename to write to (string or QString)
Returns
flag indicating success
"""
fn = str(fn)
txt = Utilities.encode(unicode(self.text()), self.encoding)
# work around glitch in scintilla: always make sure,
# that the last line is terminated properly
m = self.eolMode()
eol = None
if m == QextScintilla.EolWindows:
eol = '\r\n'
elif m == QextScintilla.EolUnix:
eol = '\n'
elif m == QextScintilla.EolMac:
eol = '\r'
if eol:
if len(txt) >= len(eol):
if txt[-len(eol):] != eol:
txt += eol
else:
txt += eol
# create a backup file, if the option is set
if Preferences.getEditor("CreateBackupFile"):
bfn = '%s~' % fn
try:
os.remove(bfn)
except:
# if there was an error, ignore it
pass
try:
os.rename(fn, bfn)
except:
# if there was an error, ignore it
pass
# now write text to the file fn
try:
f = open(fn, 'wb')
f.write(txt)
f.close()
return 1
except IOError, why:
QMessageBox.critical(self, self.trUtf8('Save File'),
self.trUtf8('The file <b>%1</b> could not be saved.<br>Reason: %2')
.arg(fn).arg(str(why)))
return 0
def saveFile(self, saveas = 0, path = None):
"""
Public slot to save the text to a file.
Arguments
saveas -- flag indicating a 'save as' action
path -- directory to save the file in (string or QString)
Returns
tuple of two values (boolean, string) giving a success indicator and
the name of the saved file
"""
if not saveas and not self.isModified():
return (0, None) # do nothing if text wasn't changed
newName = None
if saveas or self.fileName is None:
selectedFilter = QString('')
fn = QFileDialog.getSaveFileName(path,
self.trUtf8("Python Files (*.py);;"
"Pyrex Files (*.pyx);;"
"IDL Files (*.idl);;"
"C Files (*.c);;"
"C++ Files (*.cpp);;"
"C++/C Header Files (*.h);;"
"C# Files (*.cs);;"
"HTML Files (*.html);;"
"PHP Files (*.php);;"
"ASP Files (*.asp);;"
"CSS Files (*.css);;"
"XML Files (*.xml);;"
"XSL Files (*.xsl);;"
"DTD Files (*.dtd);;"
"Java Files (*.java);;"
"JavaScript Files (*.js);;"
"SQL Files (*.sql);;"
"Docbook Files (*.docbook);;"
"All Files (*)"), self, None,
self.trUtf8("Save File"), selectedFilter, 0)
if not fn.isNull():
ext = QFileInfo(fn).extension()
if ext.isEmpty():
ex = selectedFilter.section('(*',1,1).section(')',0,0)
if not ex.isEmpty():
fn.append(ex)
if QFileInfo(fn).exists():
abort = QMessageBox.warning(self,
self.trUtf8("Save File"),
self.trUtf8("The file <b>%1</b> already exists.")
.arg(fn),
self.trUtf8("&Overwrite"),
self.trUtf8("&Abort"), None, 1)
if abort:
return (0, None)
fn = str(QDir.convertSeparators(fn))
newName = fn
else:
return (0, None)
else:
fn = self.fileName
if self.writeFile(fn):
self.fileName = fn
self.setModified(0)
self.setReadOnly(0)
self.setCaption(self.fileName)
if self.lexer is None:
ext = self.getExtension(self.fileName)
self.bindLexer(ext)
# set the autocompletion and calltips function
self.setAutoCompletion()
self.setCallTips()
if self.fileInfo is None or saveas:
self.fileInfo = QFileInfo(self.fileName)
self.fileInfo.setCaching(0)
self.lastModified = self.fileInfo.lastModified()
self.sendAllBreakpoints()
if newName is not None:
self.vm.addToRecentList(newName)
self.emit(PYSIGNAL('editorSaved'), (self.fileName,))
return (1, self.fileName)
else:
return (0, None)
def saveFileAs(self, path = None):
"""
Public slot to save a file with a new name.
saveas -- flag indicating a 'save as' action
path -- directory to save the file in (string or QString)
Returns
tuple of two values (boolean, string) giving a success indicator and
the name of the saved file
"""
return self.saveFile(1, path)
def ensureVisible(self, line):
"""
Public slot to ensure, that the specified line is visible.
Arguments
line -- line number to make visible
"""
self.ensureLineVisible(line-1)
def handleMarginClicked(self, margin, line, state):
"""
Private slot to handle the marginClicked signal.
Arguments
margin -- id of the clicked margin
line -- line number pf the click
state -- mouse button state
"""
if margin == 1:
self.handleBreakpoint(line+1, state)
def commentLine(self):
"""
Public slot to comment the current line.
"""
if self.lexer is None or not self.lexer.canBlockComment():
return
line, index = self.getCursorPosition()
self.beginUndoAction()
self.insertAt(self.lexer.commentStr(), line, 0)
self.endUndoAction()
def uncommentLine(self):
"""
Public slot to uncomment the current line.
This happens only, if it was commented by using
the commentLine() or commentSelection() slots
"""
if self.lexer is None or not self.lexer.canBlockComment():
return
commentStr = self.lexer.commentStr()
line, index = self.getCursorPosition()
# check if line starts with our comment string (i.e. was commented
# by our comment...() slots
if not self.text(line).startsWith(commentStr):
return
# now remove the comment string
self.beginUndoAction()
self.setSelection(line, 0, line, commentStr.length())
self.removeSelectedText()
self.endUndoAction()
def commentSelection(self):
"""
Public slot to comment the current selection.
"""
if self.lexer is None or not self.lexer.canBlockComment():
return
if not self.hasSelectedText():
return
commentStr = self.lexer.commentStr()
# get the selection boundaries
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
if indexTo == 0:
endLine = lineTo - 1
else:
endLine = lineTo
self.beginUndoAction()
# iterate over the lines
for line in range(lineFrom, endLine+1):
self.insertAt(commentStr, line, 0)
# change the selection accordingly
self.setSelection(lineFrom, 0, endLine+1, 0)
self.endUndoAction()
def uncommentSelection(self):
"""
Public slot to uncomment the current selection.
This happens only, if it was commented by using
the commentLine() or commentSelection() slots
"""
if self.lexer is None or not self.lexer.canBlockComment():
return
if not self.hasSelectedText():
return
commentStr = self.lexer.commentStr()
# get the selection boundaries
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
if indexTo == 0:
endLine = lineTo - 1
else:
endLine = lineTo
self.beginUndoAction()
# iterate over the lines
for line in range(lineFrom, endLine+1):
# check if line starts with our comment string (i.e. was commented
# by our comment...() slots
if not self.text(line).startsWith(commentStr):
continue
self.setSelection(line, 0, line, commentStr.length())
self.removeSelectedText()
# adjust selection start
if line == lineFrom:
indexFrom -= commentStr.length()
if indexFrom < 0:
indexFrom = 0
# adjust selection end
if line == lineTo:
indexTo -= commentStr.length()
if indexTo < 0:
indexTo = 0
# change the selection accordingly
self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
self.endUndoAction()
def commentLineOrSelection(self):
"""
Public slot to comment the current line or current selection.
"""
if self.hasSelectedText():
self.commentSelection()
else:
self.commentLine()
def uncommentLineOrSelection(self):
"""
Public slot to uncomment the current line or current selection.
This happens only, if it was commented by using
the commentLine() or commentSelection() slots
"""
if self.hasSelectedText():
self.uncommentSelection()
else:
self.uncommentLine()
def streamCommentLine(self):
"""
Public slot to stream comment the current line.
"""
if self.lexer is None or not self.lexer.canStreamComment():
return
commentStr = self.lexer.streamCommentStr()
line, index = self.getCursorPosition()
self.beginUndoAction()
self.insertAt(commentStr['end'], line, self.lineLength(line))
self.insertAt(commentStr['start'], line, 0)
self.endUndoAction()
def streamCommentSelection(self):
"""
Public slot to comment the current selection.
"""
if self.lexer is None or not self.lexer.canStreamComment():
return
if not self.hasSelectedText():
return
commentStr = self.lexer.streamCommentStr()
# get the selection boundaries
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
if indexTo == 0:
endLine = lineTo - 1
endIndex = self.lineLength(endLine)
else:
endLine = lineTo
endIndex = indexTo
self.beginUndoAction()
self.insertAt(commentStr['end'], endLine, endIndex)
self.insertAt(commentStr['start'], lineFrom, indexFrom)
# change the selection accordingly
if indexTo > 0:
indexTo += commentStr['end'].length()
if lineFrom == endLine:
indexTo += commentStr['start'].length()
self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
self.endUndoAction()
def streamCommentLineOrSelection(self):
"""
Public slot to stream comment the current line or current selection.
"""
if self.hasSelectedText():
self.streamCommentSelection()
else:
self.streamCommentLine()
def boxCommentLine(self):
"""
Public slot to box comment the current line.
"""
if self.lexer is None or not self.lexer.canBoxComment():
return
commentStr = self.lexer.boxCommentStr()
line, index = self.getCursorPosition()
self.beginUndoAction()
self.insertAt('\n', line, self.lineLength(line))
self.insertAt(commentStr['end'], line + 1, 0)
self.insertAt(commentStr['middle'], line, 0)
self.insertAt('\n', line, 0)
self.insertAt(commentStr['start'], line, 0)
self.endUndoAction()
def boxCommentSelection(self):
"""
Public slot to box comment the current selection.
"""
if self.lexer is None or not self.lexer.canBoxComment():
return
if not self.hasSelectedText():
return
commentStr = self.lexer.boxCommentStr()
# get the selection boundaries
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
if indexTo == 0:
endLine = lineTo - 1
else:
endLine = lineTo
self.beginUndoAction()
# iterate over the lines
for line in range(lineFrom, endLine+1):
self.insertAt(commentStr['middle'], line, 0)
# now do the comments before and after the selection
self.insertAt('\n', endLine, self.lineLength(endLine))
self.insertAt(commentStr['end'], endLine + 1, 0)
self.insertAt('\n', lineFrom, 0)
self.insertAt(commentStr['start'], lineFrom, 0)
# change the selection accordingly
self.setSelection(lineFrom, 0, endLine+3, 0)
self.endUndoAction()
def boxCommentLineOrSelection(self):
"""
Public slot to box comment the current line or current selection.
"""
if self.hasSelectedText():
self.boxCommentSelection()
else:
self.boxCommentLine()
def indentLine(self, indent = 1):
"""
Private method to indent or unindent the current line.
Arguments
indent -- flag indicating an indent operation
If the flag is true, an indent operation is performed.
Otherwise the current line is unindented.
"""
line, index = self.getCursorPosition()
self.beginUndoAction()
if indent:
self.indent(line)
else:
self.unindent(line)
self.endUndoAction()
def indentSelection(self, indent = 1):
"""
Private method to indent or unindent the current selection.
Arguments
indent -- flag indicating an indent operation
If the flag is true, an indent operation is performed.
Otherwise the current line is unindented.
"""
if not self.hasSelectedText():
return
# get the selection
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
if indexTo == 0:
endLine = lineTo - 1
else:
endLine = lineTo
self.beginUndoAction()
# iterate over the lines
for line in range(lineFrom, endLine+1):
if indent:
self.indent(line)
else:
self.unindent(line)
self.endUndoAction()
def indentLineOrSelection(self):
"""
Public slot to indent the current line or current selection
"""
if self.hasSelectedText():
self.indentSelection(1)
else:
self.indentLine(1)
def unindentLineOrSelection(self):
"""
Public slot to unindent the current line or current selection.
"""
if self.hasSelectedText():
self.indentSelection(0)
else:
self.indentLine(0)
def gotoLine(self, line):
"""
Public slot to jump to the beginning of a line.
Arguments
line -- line number to go to
"""
self.setCursorPosition(line, 0)
def readSettings(self):
"""
Public slot to read the settings into our lexer.
"""
# read the lexer settings
if self.lexer is not None:
self.lexer.readSettings(Preferences.Prefs.settings, "/eric3/Scintilla")
# set the line marker colours
self.setLineMarkerColours()
# set margin 0 and 2 configuration
self.setMargin0and2()
# set the text display
self.setTextDisplay()
# set the autocompletion and calltips function
self.setAutoCompletion()
self.setCallTips()
def setLineMarkerColours(self):
"""
Private method to set the line marker colours.
"""
self.setMarkerForegroundColor(Preferences.getEditorColour("CurrentMarker"),
self.currentline)
self.setMarkerBackgroundColor(Preferences.getEditorColour("CurrentMarker"),
self.currentline)
self.setMarkerForegroundColor(Preferences.getEditorColour("ErrorMarker"),
self.errorline)
self.setMarkerBackgroundColor(Preferences.getEditorColour("ErrorMarker"),
self.errorline)
def setMargin0and2(self):
"""
Private method to configure margins 0 and 2.
"""
# set the font for all margins
self.setMarginsFont(Preferences.getEditorOtherFonts("MarginsFont"))
# set margin 0 settings
linenoMargin = Preferences.getEditor("LinenoMargin")
self.setMarginLineNumbers(0, linenoMargin)
if linenoMargin:
self.setMarginWidth(0, ' ' + '8' * Preferences.getEditor("LinenoWidth") + ' ')
else:
self.setMarginWidth(0, 0)
# set margin 2 settings
if Preferences.getEditor("FoldingMargin"):
self.setFolding(Preferences.getEditor("FoldingStyle"))
else:
self.setFolding(QextScintilla.NoFoldStyle)
def setTextDisplay(self):
"""
Private method to configure the text display.
"""
self.setTabWidth(Preferences.getEditor("TabWidth"))
self.setIndentationWidth(Preferences.getEditor("IndentWidth"))
self.setIndentationsUseTabs(Preferences.getEditor("TabForIndentation")) # no tabs for indentation
self.setTabIndents(Preferences.getEditor("TabIndents"))
self.setBackspaceUnindents(Preferences.getEditor("TabIndents"))
self.setIndentationGuides(Preferences.getEditor("IndentationGuides"))
self.setWhitespaceVisibility(Preferences.getEditor("ShowWhitespace"))
self.setEolVisibility(Preferences.getEditor("ShowEOL"))
self.setAutoIndent(Preferences.getEditor("AutoIndentation"))
if Preferences.getEditor("BraceHighlighting"):
self.setBraceMatching(QextScintilla.SloppyBraceMatch)
else:
self.setBraceMatching(QextScintilla.NoBraceMatch)
self.setMatchedBraceForegroundColor(
Preferences.getEditorColour("MatchingBrace"))
self.setUnmatchedBraceForegroundColor(
Preferences.getEditorColour("NonmatchingBrace"))
self.setEolMode(Preferences.getEditor("EOLMode"))
def setAutoCompletion(self):
"""
Private method to configure the autocompletion function.
"""
self.setAutoCompletionCaseSensitivity(
Preferences.getEditor("AutoCompletionCaseSensitivity"))
self.setAutoCompletionReplaceWord(
Preferences.getEditor("AutoCompletionReplaceWord"))
self.setAutoCompletionShowSingle(
Preferences.getEditor("AutoCompletionShowSingle"))
api = self.vm.getAPIs(self.apiLanguage)
self.setAutoCompletionAPIs(api)
if api is None:
self.setAutoCompletionSource(QextScintilla.AcsDocument)
self.acAPI = 0
else:
self.setAutoCompletionSource(
Preferences.getEditor("AutoCompletionSource"))
self.acAPI = 1
if Preferences.getEditor("AutoCompletionEnabled"):
self.setAutoCompletionThreshold(
Preferences.getEditor("AutoCompletionThreshold"))
else:
self.setAutoCompletionThreshold(-1)
def setCallTips(self):
"""
Private method to configure the calltips function.
"""
if Preferences.getEditor("CallTipsEnabled"):
api = self.vm.getAPIs(self.apiLanguage)
self.setCallTipsAPIs(api)
self.setCallTipsBackgroundColor(
Preferences.getEditorColour("CallTipsBackground"))
self.setCallTipsVisible(Preferences.getEditor("CallTipsVisible"))
else:
self.setCallTipsAPIs(None)
def checkDirty(self):
"""
Private method to check dirty status and open a message window.
Returns
flag indicating successful reset of the dirty flag (boolean)
"""
if self.isModified():
fn = self.fileName
if fn is None:
fn = self.trUtf8('Noname')
res = QMessageBox.warning(self.vm,
self.trUtf8("File Modified"),
self.trUtf8("The file <b>%1</b> has unsaved changes.")
.arg(fn),
self.trUtf8("&Save"), self.trUtf8("&Abort"), None, 0, 1)
if res == 0:
ok, newName = self.saveFile()
if ok:
self.vm.setEditorName(self, newName)
return ok
elif res == 1:
return 0
return 1
def revertToUnmodified(self):
"""
Private method to revert back to the last saved state.
"""
while self.isModified():
self.undo()
#################################################################
## Methods needed by the context menu
#################################################################
def contextMenuEvent(self, evt):
"""
Private method implementing the context menu event.
Arguments
evt -- the context menu event (QContextMenuEvent)
"""
evt.accept()
self.menu.popup(evt.globalPos())
def handleShowContextMenu(self):
"""
Private slot handling the aboutToShow signal of the context menu.
"""
self.menu.setItemEnabled(self.menuIds["Save"], self.isModified())
self.menu.setItemEnabled(self.menuIds["Undo"], self.isUndoAvailable())
self.menu.setItemEnabled(self.menuIds["Redo"], self.isRedoAvailable())
self.menu.setItemEnabled(self.menuIds["Revert"], self.isModified())
self.menu.setItemEnabled(self.menuIds["Cut"], self.hasSelectedText())
self.menu.setItemEnabled(self.menuIds["Copy"], self.hasSelectedText())
self.menu.setItemEnabled(self.menuIds["PrintSel"], self.hasSelectedText())
self.menu.setItemEnabled(self.menuIds["acAPI"], self.acAPI)
if self.fileName and self.fileName.endswith(".py"):
self.menu.setItemEnabled(self.menuIds["Check"], 1)
self.menu.setItemEnabled(self.menuIds["Show"], 1)
else:
self.menu.setItemEnabled(self.menuIds["Check"], 0)
self.menu.setItemEnabled(self.menuIds["Show"], 0)
if self.lexer is not None:
self.menu.setItemEnabled(self.menuIds["Comment"], self.lexer.canBlockComment())
self.menu.setItemEnabled(self.menuIds["Uncomment"], self.lexer.canBlockComment())
self.menu.setItemEnabled(self.menuIds["StreamComment"], self.lexer.canStreamComment())
self.menu.setItemEnabled(self.menuIds["BoxComment"], self.lexer.canBoxComment())
else:
self.menu.setItemEnabled(self.menuIds["Comment"], 0)
self.menu.setItemEnabled(self.menuIds["Uncomment"], 0)
self.menu.setItemEnabled(self.menuIds["StreamComment"], 0)
self.menu.setItemEnabled(self.menuIds["BoxComment"], 0)
def handleShowShowMenu(self):
"""
Private slot called before the show menu is shown.
"""
project = self.vm.getProject()
if project.isOpen() and project.isProjectSource(self.getFileName()):
fn = project.getMainScript(1)
else:
fn = self.getFileName()
if fn is not None:
basename = os.path.splitext(fn)[0]
self.showMenu.setItemEnabled(self.profileMenuItem,
os.path.isfile("%s.profile" % basename))
self.showMenu.setItemEnabled(self.coverageMenuItem,
os.path.isfile("%s.coverage" % basename))
else:
self.showMenu.setItemEnabled(self.profileMenuItem, 0)
self.showMenu.setItemEnabled(self.coverageMenuItem, 0)
def handleContextSave(self):
"""
Private slot handling the save context menu entry.
"""
ok, newName = self.saveFile()
if ok:
self.vm.setEditorName(self, newName)
def handleContextSaveAs(self):
"""
Private slot handling the save as context menu entry.
"""
ok, newName = self.saveFileAs()
if ok:
self.vm.setEditorName(self, newName)
def handleContextClose(self):
"""
Private slot handling the close context menu entry.
"""
self.vm.closeEditor(self)
def handleSelectAll(self):
"""
Private slot handling the select all context menu action.
"""
self.selectAll(1)
def handleDeselectAll(self):
"""
Private slot handling the deselect all context menu action.
"""
self.selectAll(0)
def handleTabnanny(self):
"""
Private method to handle the tabnanny context menu action.
"""
if not self.checkDirty():
return
self.tabnanny = TabnannyDialog(self.vm)
self.tabnanny.show()
self.tabnanny.start(self.fileName)
def handleSyntaxCheck(self):
"""
Private method to handle the syntax check context menu action.
"""
if not self.checkDirty():
return
self.syntaxcheck = SyntaxCheckerDialog(self.vm)
self.syntaxcheck.show()
self.syntaxcheck.start(self.fileName)
def handleCodeMetrics(self):
"""
Private method to handle the code metrics context menu action.
"""
if not self.checkDirty():
return
self.codemetrics = CodeMetricsDialog()
self.codemetrics.show()
self.codemetrics.start(self.fileName)
def handleCodeCoverage(self):
"""
Private method to handle the code coverage context menu action.
"""
if not self.checkDirty():
return
project = self.vm.getProject()
if project.isOpen() and project.isProjectSource(self.fileName):
fn = project.getMainScript(1)
else:
fn = self.fileName
self.codecoverage = PyCoverageDialog()
self.codecoverage.show()
self.codecoverage.start(fn, self.fileName)
def handleProfileData(self):
"""
Private method to handle the show profile data context menu action.
"""
if not self.checkDirty():
return
project = self.vm.getProject()
if project.isOpen() and project.isProjectSource(self.fileName):
fn = project.getMainScript(1)
else:
fn = self.fileName
self.profiledata = PyProfileDialog()
self.profiledata.show()
self.profiledata.start(fn, self.fileName)
#################################################################
## Macro handling methods
#################################################################
def getMacroName(self):
"""
Private method to select a macro name from the list of macros.
Returns
Tuple of macro name and a flag, indicating, if the user pressed ok or
canceled the operation. (QString, boolean)
"""
qs = QStringList()
for s in self.macros.keys():
qs.append(s)
qs.sort()
return QInputDialog.getItem(\
self.trUtf8("Macro Name"),
self.trUtf8("Select a macro name:"),
qs, 0, 0, self)
def handleRunMacro(self):
"""
Public method to execute a macro.
"""
name, ok = self.getMacroName()
if ok and not name.isEmpty():
self.macros[str(name)].play()
def handleDeleteMacro(self):
"""
Public method to delete a macro.
"""
name, ok = self.getMacroName()
if ok and not name.isEmpty():
del self.macros[str(name)]
def handleLoadMacro(self):
"""
Public method to load a macro from a file.
"""
configDir = Preferences.getConfigDir()
fname = QFileDialog.getOpenFileName(configDir,
self.trUtf8("Macro files (*.macro)"),
self, None,
self.trUtf8("Load macro file"))
if fname.isEmpty():
return # user aborted
try:
f = open(str(fname), "rb")
lines = f.readlines()
f.close()
except IOError:
QMessageBox.critical(self,
self.trUtf8("Error loading macro"),
self.trUtf8("The macro file <b>%1</b> could not be read.")
.arg(fname),
self.trUtf8("OK"))
return
if len(lines) != 2:
QMessageBox.critical(self,
self.trUtf8("Error loading macro"),
self.trUtf8("The macro file <b>%1</b> is corrupt.")
.arg(fname),
self.trUtf8("OK"))
return
macro = QextScintillaMacro(lines[1], self)
self.macros[lines[0].strip()] = macro
def handleSaveMacro(self):
"""
Public method to save a macro to a file.
"""
configDir = Preferences.getConfigDir()
name, ok = self.getMacroName()
if not ok or name.isEmpty():
return # user abort
name = str(name)
selectedFilter = QString('')
fname = QFileDialog.getSaveFileName(configDir,
self.trUtf8("Macro files (*.macro)"),
self, None,
self.trUtf8("Save macro file"), selectedFilter, 0)
if fname.isEmpty():
return # user aborted
ext = QFileInfo(fname).extension()
if ext.isEmpty():
ex = selectedFilter.section('(*',1,1).section(')',0,0)
if not ex.isEmpty():
fname.append(ex)
if QFileInfo(fname).exists():
abort = QMessageBox.warning(self,
self.trUtf8("Save macro"),
self.trUtf8("The macro file <b>%1</b> already exists.")
.arg(fname),
self.trUtf8("&Overwrite"),
self.trUtf8("&Abort"), None, 1)
if abort:
return
fname = str(QDir.convertSeparators(fname))
try:
f = open(fname, "wb")
f.write("%s%s" % (name, os.linesep))
f.write(str(self.macros[name].save()))
f.close()
except IOError:
QMessageBox.critical(self,
self.trUtf8("Error saving macro"),
self.trUtf8("The macro file <b>%1</b> could not be written.")
.arg(fname),
self.trUtf8("OK"))
return
def handleStartMacroRecording(self):
"""
Public method to start macro recording.
"""
if self.recording:
res = QMessageBox.warning(self,
self.trUtf8("Start Macro Recording"),
self.trUtf8("Macro recording is already active."),
self.trUtf8("&Start new"), self.trUtf8("&Cancel"))
if res == 0:
self.handleStopMacroRecording()
else:
return
else:
self.recording = 1
self.curMacro = QextScintillaMacro(self)
self.curMacro.startRecording()
def handleStopMacroRecording(self):
"""
Public method to stop macro recording.
"""
if not self.recording:
return # we are not recording
self.curMacro.endRecording()
self.recording = 0
name, ok = QInputDialog.getText(self.trUtf8("Macro Recording"),
self.trUtf8("Enter name of the macro:"))
if ok and not name.isEmpty():
self.macros[str(name)] = self.curMacro
self.curMacro = None
#################################################################
## Overwritten methods
#################################################################
def close(self, alsoDelete=0):
"""
Public method called when the window gets closed.
This overwritten method redirects the action to our
ViewManager.closeEditor, which in turn calls our closeIt
method.
Arguments
alsoDelete -- ignored
"""
return self.vm.closeEditor(self)
def closeIt(self):
"""
Public method called by the viewmanager to finally get rid of us.
"""
QextScintilla.close(self, 1)
def focusInEvent(self, event):
"""
Public method called when the editor receives focus.
Arguments
event -- the event object (QFocusEvent)
"""
self.vm.editorActGrp.setEnabled(1)
self._updateReadOnly(0)
if not self.inReopenPrompt and self.fileInfo is not None and \
self.fileInfo.lastModified().toString().compare(self.lastModified.toString()):
self.inReopenPrompt = 1
msg = self.trUtf8("""The file <b>%1</b> has been changed while it was opened in"""
""" eric3.""").arg(self.fileName)
if self.isModified():
msg.append(self.trUtf8("""<br><b>Warning:</b> You will loose"""
""" your changes upon reopening it."""))
res = QMessageBox.warning(None,
self.trUtf8("File changed"), msg,
self.trUtf8("&OK"), self.trUtf8("&Reopen"),
None, 0, -1)
self.inReopenPrompt = 0
if res == 1:
self.readFile(self.fileName)
self.setModified(0)
self.gotoLine(0)
self.emit(PYSIGNAL('editorSaved'), (self.fileName,))
else:
# do not prompt for this change again...
self.lastModified = self.fileInfo.lastModified()
QextScintilla.focusInEvent(self, event)
def focusOutEvent(self, event):
"""
Public method called when the editor loses focus.
Arguments
event -- the event object (QFocusEvent)
"""
self.vm.editorActGrp.setEnabled(0)
def zoomIn(self, zoom = 1):
"""
public method used to increase the zoom factor.
Arguments
zoom -- zoom factor increment
"""
self.zoom += zoom
QextScintilla.zoomIn(self, zoom)
def zoomOut(self, zoom = 1):
"""
public method used to decrease the zoom factor.
Arguments
zoom -- zoom factor decrement
"""
self.zoom -= zoom
QextScintilla.zoomOut(self, zoom)
def zoomTo(self, zoom):
"""
Public method used to zoom to a specific zoom factor.
Arguments
zoom -- zoom factor
"""
self.zoom = zoom
QextScintilla.zoomTo(self, zoom)
def getZoom(self):
"""
Public method used to retrieve the current zoom factor.
Returns
zoom factor (int)
"""
return self.zoom
def eventFilter(self, object, event):
"""
Private method called to filter an event.
Arguments:
object -- object, that generated the event (QObject)
event -- the event, that was generated by object (QEvent)
Returns:
flag indicating if event was filtered out
"""
if event.type() == QEvent.MouseButtonPress:
self.vm.eventFilter(self, event)
return QextScintilla.eventFilter(self, object, event)
def _updateReadOnly(self, bForce=1):
"""
Private method to update the readOnly information for this editor.
If bForce is True, then updates everything regardless if
the attributes have actually changed, such as during
initialization time. A signal is emitted after the
caption change.
Arguments
bForce -- 1 to force change, 0 to only update and emit
signal if there was an attribute change.
"""
if self.fileName is None:
return
readOnly = not QFileInfo(self.fileName).isWritable() and 1 or 0
if not bForce and (readOnly == self.isReadOnly()):
return
cap = self.fileName
if readOnly:
cap = "%s (ro)" % str(cap)
self.setReadOnly(readOnly)
self.setCaption(cap)
self.emit(PYSIGNAL('captionChanged'), (cap, self))
|