--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QScintilla/Terminal.py Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,934 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2008 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a simple terminal based on QScintilla. +""" + +import sys +import os +import re + +from PyQt4.QtCore import * +from PyQt4.QtGui import * +from PyQt4.Qsci import QsciScintilla + +from E4Gui.E4Application import e4App + +import Lexers +from QsciScintillaCompat import QsciScintillaCompat, QSCINTILLA_VERSION + +import Preferences +import Utilities + +import UI.PixmapCache +from Utilities import toUnicode + +from ShellHistoryDialog import ShellHistoryDialog + +class Terminal(QsciScintillaCompat): + """ + Class implementing a simple terminal based on QScintilla. + + A user can enter commands that are executed by a shell process. + """ + def __init__(self, vm, parent = None): + """ + Constructor + + @param vm reference to the viewmanager object + @param parent parent widget (QWidget) + """ + QsciScintillaCompat.__init__(self, parent) + self.setUtf8(True) + + self.vm = vm + + self.linesepRegExp = r"\r\n|\n|\r" + + self.setWindowTitle(self.trUtf8('Terminal')) + + self.setWhatsThis(self.trUtf8( + """<b>The Terminal Window</b>""" + """<p>This is a very simple terminal like window, that runs a shell""" + """ process in the background.</p>""" + """<p>The process can be stopped and started via the context menu. Some""" + """ Ctrl command may be sent as well. However, the shell may ignore""" + """ them.</p>""" + """<p>You can use the cursor keys while entering commands. There is also a""" + """ history of commands that can be recalled using the up and down cursor""" + """ keys. Pressing the up or down key after some text has been entered will""" + """ start an incremental search.</p>""" + )) + + self.ansi_re = re.compile("\033\[\??[\d;]*\w") + + # Initialise instance variables. + self.prline = 0 + self.prcol = 0 + self.inDragDrop = False + self.lexer_ = None + + # Initialize history + self.maxHistoryEntries = Preferences.getTerminal("MaxHistoryEntries") + self.history = [] + self.histidx = -1 + + # clear QScintilla defined keyboard commands + # we do our own handling through the view manager + self.clearAlternateKeys() + self.clearKeys() + self.__actionsAdded = False + + # Create the history context menu + self.hmenu = QMenu(self.trUtf8('History')) + self.hmenu.addAction(self.trUtf8('Select entry'), self.__selectHistory) + self.hmenu.addAction(self.trUtf8('Show'), self.__showHistory) + self.hmenu.addAction(self.trUtf8('Clear'), self.__clearHistory) + + # Create a little context menu to send Ctrl-C, Ctrl-D or Ctrl-Z + self.csm = QSignalMapper(self) + self.connect(self.csm, SIGNAL('mapped(int)'), self.__sendCtrl) + + self.cmenu = QMenu(self.trUtf8('Ctrl Commands')) + act = self.cmenu.addAction(self.trUtf8('Ctrl-C')) + self.csm.setMapping(act, 3) + self.connect(act, SIGNAL('triggered()'), self.csm, SLOT('map()')) + act = self.cmenu.addAction(self.trUtf8('Ctrl-D')) + self.csm.setMapping(act, 4) + self.connect(act, SIGNAL('triggered()'), self.csm, SLOT('map()')) + act = self.cmenu.addAction(self.trUtf8('Ctrl-Z')) + self.csm.setMapping(act, 26) + self.connect(act, SIGNAL('triggered()'), self.csm, SLOT('map()')) + + # Create a little context menu + self.menu = QMenu(self) + self.menu.addAction(self.trUtf8('Cut'), self.cut) + self.menu.addAction(self.trUtf8('Copy'), self.copy) + self.menu.addAction(self.trUtf8('Paste'), self.paste) + self.menu.addMenu(self.hmenu) + self.menu.addSeparator() + self.menu.addAction(self.trUtf8('Clear'), self.clear) + self.__startAct = self.menu.addAction(self.trUtf8("Start"), self.__startShell) + self.__stopAct = self.menu.addAction(self.trUtf8("Stop"), self.__stopShell) + self.__resetAct = self.menu.addAction(self.trUtf8('Reset'), self.__reset) + self.menu.addSeparator() + self.__ctrlAct = self.menu.addMenu(self.cmenu) + self.menu.addSeparator() + self.menu.addAction(self.trUtf8("Configure..."), self.__configure) + + self.__bindLexer() + self.__setTextDisplay() + self.__setMargin0() + + self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) + + self.incrementalSearchString = "" + self.incrementalSearchActive = False + + self.supportedEditorCommands = { + QsciScintilla.SCI_LINEDELETE : self.__clearCurrentLine, + QsciScintilla.SCI_NEWLINE : self.__QScintillaNewline, + + QsciScintilla.SCI_DELETEBACK : self.__QScintillaDeleteBack, + QsciScintilla.SCI_CLEAR : self.__QScintillaDelete, + QsciScintilla.SCI_DELWORDLEFT : self.__QScintillaDeleteWordLeft, + QsciScintilla.SCI_DELWORDRIGHT : self.__QScintillaDeleteWordRight, + QsciScintilla.SCI_DELLINELEFT : self.__QScintillaDeleteLineLeft, + QsciScintilla.SCI_DELLINERIGHT : self.__QScintillaDeleteLineRight, + + QsciScintilla.SCI_CHARLEFT : self.__QScintillaCharLeft, + QsciScintilla.SCI_CHARRIGHT : self.__QScintillaCharRight, + QsciScintilla.SCI_WORDLEFT : self.__QScintillaWordLeft, + QsciScintilla.SCI_WORDRIGHT : self.__QScintillaWordRight, + QsciScintilla.SCI_VCHOME : self.__QScintillaVCHome, + QsciScintilla.SCI_LINEEND : self.__QScintillaLineEnd, + QsciScintilla.SCI_LINEUP : self.__QScintillaLineUp, + QsciScintilla.SCI_LINEDOWN : self.__QScintillaLineDown, + + QsciScintilla.SCI_CHARLEFTEXTEND : self.__QScintillaCharLeftExtend, + QsciScintilla.SCI_CHARRIGHTEXTEND : self.extendSelectionRight, + QsciScintilla.SCI_WORDLEFTEXTEND : self.__QScintillaWordLeftExtend, + QsciScintilla.SCI_WORDRIGHTEXTEND : self.extendSelectionWordRight, + QsciScintilla.SCI_VCHOMEEXTEND : self.__QScintillaVCHomeExtend, + QsciScintilla.SCI_LINEENDEXTEND : self.extendSelectionToEOL, + } + + self.__ioEncoding = Preferences.getSystem("IOEncoding") + + self.__process = QProcess() + self.__process.setProcessChannelMode(QProcess.MergedChannels) + self.__process.setReadChannel(QProcess.StandardOutput) + + self.connect(self.__process, SIGNAL("readyReadStandardOutput()"), + self.__readOutput) + self.connect(self.__process, SIGNAL("started()"), self.__started) + self.connect(self.__process, SIGNAL("finished(int)"), self.__finished) + + self.__ctrl = {} + for ascii_number, letter in enumerate("abcdefghijklmnopqrstuvwxyz"): + self.__ctrl[letter] = chr(ascii_number + 1) + + self.__lastPos = (0, 0) + + self.__startShell() + + def __readOutput(self): + """ + Private method to process the output of the shell. + """ + output = unicode(self.__process.readAllStandardOutput(), + self.__ioEncoding, 'replace') + self.__write(self.ansi_re.sub("", output)) + self.__lastPos = self.__getEndPos() + + def __started(self): + """ + Private method called, when the shell process has started. + """ + if not Utilities.isWindowsPlatform(): + QTimer.singleShot(250, self.clear) + + self.__startAct.setEnabled(False) + self.__stopAct.setEnabled(True) + self.__resetAct.setEnabled(True) + self.__ctrlAct.setEnabled(True) + + def __finished(self): + """ + Private method called, when the shell process has finished. + """ + QsciScintilla.clear(self) + + self.__startAct.setEnabled(True) + self.__stopAct.setEnabled(False) + self.__resetAct.setEnabled(False) + self.__ctrlAct.setEnabled(False) + + def __send(self, data): + """ + Private method to send data to the shell process. + + @param data data to be sent to the shell process (string) + """ + pdata = QByteArray() + pdata.append(data) + self.__process.write(pdata) + + def __sendCtrl(self, cmd): + """ + Private slot to send a control command to the shell process. + + @param the control command to be sent (integer) + """ + self.__send(chr(cmd)) + + def closeTerminal(self): + """ + Public method to shutdown the terminal. + """ + self.__stopShell() + self.saveHistory() + + def __bindLexer(self): + """ + Private slot to set the lexer. + """ + if Utilities.isWindowsPlatform(): + self.language = "Batch" + else: + self.language = "Bash" + if Preferences.getTerminal("SyntaxHighlightingEnabled"): + self.lexer_ = Lexers.getLexer(self.language, self) + else: + self.lexer_ = None + + if self.lexer_ is None: + self.setLexer(None) + font = Preferences.getTerminal("MonospacedFont") + self.monospacedStyles(font) + return + + # get the font for style 0 and set it as the default font + key = 'Scintilla/%s/style0/font' % self.lexer_.language() + fontVariant = Preferences.Prefs.settings.value(key) + if fontVariant.isValid(): + fdesc = fontVariant.toStringList() + font = QFont(fdesc[0], int(fdesc[1])) + self.lexer_.setDefaultFont(font) + self.setLexer(self.lexer_) + self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") + + def __setMargin0(self): + """ + Private method to configure margin 0. + """ + # set the settings for all margins + self.setMarginsFont(Preferences.getTerminal("MarginsFont")) + self.setMarginsForegroundColor(Preferences.getEditorColour("MarginsForeground")) + self.setMarginsBackgroundColor(Preferences.getEditorColour("MarginsBackground")) + + # set margin 0 settings + linenoMargin = Preferences.getTerminal("LinenoMargin") + self.setMarginLineNumbers(0, linenoMargin) + if linenoMargin: + self.setMarginWidth(0, ' ' + '8' * Preferences.getTerminal("LinenoWidth")) + else: + self.setMarginWidth(0, 0) + + # disable margins 1 and 2 + self.setMarginWidth(1, 0) + self.setMarginWidth(2, 0) + + def __setTextDisplay(self): + """ + Private method to configure the text display. + """ + self.setTabWidth(Preferences.getEditor("TabWidth")) + if Preferences.getEditor("ShowWhitespace"): + self.setWhitespaceVisibility(QsciScintilla.WsVisible) + else: + self.setWhitespaceVisibility(QsciScintilla.WsInvisible) + self.setEolVisibility(Preferences.getEditor("ShowEOL")) + if Preferences.getEditor("BraceHighlighting"): + self.setBraceMatching(QsciScintilla.SloppyBraceMatch) + else: + self.setBraceMatching(QsciScintilla.NoBraceMatch) + self.setMatchedBraceForegroundColor( + Preferences.getEditorColour("MatchingBrace")) + self.setMatchedBraceBackgroundColor( + Preferences.getEditorColour("MatchingBraceBack")) + self.setUnmatchedBraceForegroundColor( + Preferences.getEditorColour("NonmatchingBrace")) + self.setUnmatchedBraceBackgroundColor( + Preferences.getEditorColour("NonmatchingBraceBack")) + if Preferences.getEditor("CustomSelectionColours"): + self.setSelectionBackgroundColor(\ + Preferences.getEditorColour("SelectionBackground")) + else: + self.setSelectionBackgroundColor(\ + QApplication.palette().color(QPalette.Highlight)) + if Preferences.getEditor("ColourizeSelText"): + self.resetSelectionForegroundColor() + elif Preferences.getEditor("CustomSelectionColours"): + self.setSelectionForegroundColor(\ + Preferences.getEditorColour("SelectionForeground")) + else: + self.setSelectionForegroundColor(\ + QApplication.palette().color(QPalette.HighlightedText)) + self.setSelectionToEol(Preferences.getEditor("ExtendSelectionToEol")) + self.setCaretForegroundColor( + Preferences.getEditorColour("CaretForeground")) + self.setCaretLineBackgroundColor( + Preferences.getEditorColour("CaretLineBackground")) + self.setCaretLineVisible(Preferences.getEditor("CaretLineVisible")) + self.caretWidth = Preferences.getEditor("CaretWidth") + self.setCaretWidth(self.caretWidth) + self.setWrapMode(QsciScintilla.WrapNone) + self.useMonospaced = Preferences.getTerminal("UseMonospacedFont") + self.__setMonospaced(self.useMonospaced) + + def __setMonospaced(self, on): + """ + Private method to set/reset a monospaced font. + + @param on flag to indicate usage of a monospace font (boolean) + """ + if on: + f = Preferences.getTerminal("MonospacedFont") + self.monospacedStyles(f) + else: + if not self.lexer_: + self.clearStyles() + self.__setMargin0() + self.setFont(Preferences.getTerminal("MonospacedFont")) + + self.useMonospaced = on + + def loadHistory(self): + """ + Public method to load the history. + """ + hVariant = Preferences.Prefs.settings.value("Terminal/History") + if hVariant.isValid(): + hl = hVariant.toStringList() + self.history = hl[-self.maxHistoryEntries:] + else: + self.history = [] + + def reloadHistory(self): + """ + Public method to reload the history. + """ + self.loadHistory(self.clientType) + self.history = self.historyLists[self.clientType] + self.histidx = -1 + + def saveHistory(self): + """ + Public method to save the history. + """ + Preferences.Prefs.settings.setValue(\ + "Terminal/History", QVariant(self.history)) + + def getHistory(self): + """ + Public method to get the history. + + @return reference to the history list (list of strings) + """ + return self.history + + def __clearHistory(self): + """ + Private slot to clear the current history. + """ + self.history.clear() + + def __selectHistory(self): + """ + Private slot to select a history entry to execute. + """ + cmd, ok = QInputDialog.getItem(\ + self, + self.trUtf8("Select History"), + self.trUtf8("Select the history entry to execute (most recent shown last)."), + self.history, + 0, False) + if ok: + self.__insertHistory(cmd) + + def __showHistory(self): + """ + Private slot to show the shell history dialog. + """ + dlg = ShellHistoryDialog(self.history, self.vm, self) + if dlg.exec_() == QDialog.Accepted: + self.history = dlg.getHistory() + self.histidx = -1 + + def __getEndPos(self): + """ + Private method to return the line and column of the last character. + + @return tuple of two values (int, int) giving the line and column + """ + line = self.lines() - 1 + return (line, self.lineLength(line)) + + def __write(self, s): + """ + Private method to display some text. + + @param s text to be displayed (string) + """ + line, col = self.__getEndPos() + self.setCursorPosition(line, col) + self.insert(toUnicode(s)) + self.prline, self.prcol = self.getCursorPosition() + self.ensureCursorVisible() + self.ensureLineVisible(self.prline) + + def __clearCurrentLine(self): + """ + Private method to clear the line containing the cursor. + """ + line, col = self.getCursorPosition() + if self.text(line).startswith(sys.ps1): + col = len(sys.ps1) + elif self.text(line).startswith(sys.ps2): + col = len(sys.ps2) + else: + col = 0 + self.setCursorPosition(line, col) + self.deleteLineRight() + + def __insertText(self, s): + """ + Private method to insert some text at the current cursor position. + + @param s text to be inserted (string) + """ + line, col = self.getCursorPosition() + self.insertAt(s, line, col) + self.setCursorPosition(line, col + len(s)) + + def __insertTextAtEnd(self, s): + """ + Private method to insert some text at the end of the command line. + + @param s text to be inserted (string) + """ + line, col = self.__getEndPos() + self.setCursorPosition(line, col) + self.insert(s) + self.prline, self.prcol = self.getCursorPosition() + + def mousePressEvent(self, event): + """ + Protected method to handle the mouse press event. + + @param event the mouse press event (QMouseEvent) + """ + self.setFocus() + QsciScintillaCompat.mousePressEvent(self, event) + + def editorCommand(self, cmd): + """ + Public method to perform an editor command. + + @param cmd the scintilla command to be performed + """ + try: + self.supportedEditorCommands[cmd]() + except TypeError: + self.supportedEditorCommands[cmd](cmd) + except KeyError: + pass + + def __isCursorOnLastLine(self): + """ + Private method to check, if the cursor is on the last line. + """ + cline, ccol = self.getCursorPosition() + return cline == self.lines() - 1 + + def keyPressEvent(self, ev): + """ + Re-implemented to handle the user input a key at a time. + + @param ev key event (QKeyEvent) + """ + txt = ev.text() + key = ev.key() + + # See it is text to insert. + if len(txt) and txt >= " ": + if not self.__isCursorOnLastLine(): + line, col = self.__getEndPos() + self.setCursorPosition(line, col) + self.prline, self.prcol = self.getCursorPosition() + QsciScintillaCompat.keyPressEvent(self, ev) + self.incrementalSearchActive = True + else: + ev.ignore() + + def __QScintillaLeftDeleteCommand(self, method): + """ + Private method to handle a QScintilla delete command working to the left. + + @param method shell method to execute + """ + if self.__isCursorOnLastLine(): + line, col = self.getCursorPosition() + if col > self.__lastPos[1]: + method() + + def __QScintillaDeleteBack(self): + """ + Private method to handle the Backspace key. + """ + self.__QScintillaLeftDeleteCommand(self.deleteBack) + + def __QScintillaDeleteWordLeft(self): + """ + Private method to handle the Delete Word Left command. + """ + self.__QScintillaLeftDeleteCommand(self.deleteWordLeft) + + def __QScintillaDelete(self): + """ + Private method to handle the delete command. + """ + if self.__isCursorOnLastLine(): + if self.hasSelectedText(): + lineFrom, indexFrom, lineTo, indexTo = self.getSelection() + if indexFrom >= self.__lastPos[1]: + self.delete() + self.setSelection(lineTo, indexTo, lineTo, indexTo) + else: + self.delete() + + def __QScintillaDeleteLineLeft(self): + """ + Private method to handle the Delete Line Left command. + """ + if self.__isCursorOnLastLine(): + if self.isListActive(): + self.cancelList() + + line, col = self.getCursorPosition() + prompt = self.text(line)[:self.__lastPos[1]] + self.deleteLineLeft() + self.insertAt(prompt, line, 0) + self.setCursorPosition(line, len(prompt)) + + def __QScintillaNewline(self, cmd): + """ + Private method to handle the Return key. + + @param cmd QScintilla command + """ + if self.__isCursorOnLastLine(): + self.incrementalSearchString = "" + self.incrementalSearchActive = False + line, col = self.__getEndPos() + self.setCursorPosition(line, col) + self.setSelection(*(self.__lastPos + self.getCursorPosition())) + buf = self.selectedText() + self.setCursorPosition(line, col) # select nothin + self.insert('\n') + self.__executeCommand(buf) + + def __QScintillaLeftCommand(self, method, allLinesAllowed = False): + """ + Private method to handle a QScintilla command working to the left. + + @param method shell method to execute + """ + if self.__isCursorOnLastLine() or allLinesAllowed: + line, col = self.getCursorPosition() + if col > self.__lastPos[1]: + method() + + def __QScintillaCharLeft(self): + """ + Private method to handle the Cursor Left command. + """ + self.__QScintillaLeftCommand(self.moveCursorLeft) + + def __QScintillaWordLeft(self): + """ + Private method to handle the Cursor Word Left command. + """ + self.__QScintillaLeftCommand(self.moveCursorWordLeft) + + def __QScintillaRightCommand(self, method): + """ + Private method to handle a QScintilla command working to the right. + + @param method shell method to execute + """ + if self.__isCursorOnLastLine(): + method() + + def __QScintillaCharRight(self): + """ + Private method to handle the Cursor Right command. + """ + self.__QScintillaRightCommand(self.moveCursorRight) + + def __QScintillaWordRight(self): + """ + Private method to handle the Cursor Word Right command. + """ + self.__QScintillaRightCommand(self.moveCursorWordRight) + + def __QScintillaDeleteWordRight(self): + """ + Private method to handle the Delete Word Right command. + """ + self.__QScintillaRightCommand(self.deleteWordRight) + + def __QScintillaDeleteLineRight(self): + """ + Private method to handle the Delete Line Right command. + """ + self.__QScintillaRightCommand(self.deleteLineRight) + + def __QScintillaVCHome(self, cmd): + """ + Private method to handle the Home key. + + @param cmd QScintilla command + """ + self.setCursorPosition(*self.__lastPos) + + def __QScintillaLineEnd(self, cmd): + """ + Private method to handle the End key. + + @param cmd QScintilla command + """ + self.moveCursorToEOL() + + def __QScintillaLineUp(self, cmd): + """ + Private method to handle the Up key. + + @param cmd QScintilla command + """ + line, col = self.__getEndPos() + buf = self.text(line)[self.__lastPos[1]:] + if buf and self.incrementalSearchActive: + if self.incrementalSearchString: + idx = self.__rsearchHistory(self.incrementalSearchString, + self.histidx) + if idx >= 0: + self.histidx = idx + self.__useHistory() + else: + idx = self.__rsearchHistory(buf) + if idx >= 0: + self.histidx = idx + self.incrementalSearchString = buf + self.__useHistory() + else: + if self.histidx < 0: + self.histidx = len(self.history) + if self.histidx > 0: + self.histidx = self.histidx - 1 + self.__useHistory() + + def __QScintillaLineDown(self, cmd): + """ + Private method to handle the Down key. + + @param cmd QScintilla command + """ + line, col = self.__getEndPos() + buf = self.text(line)[self.__lastPos[1]:] + if buf and self.incrementalSearchActive: + if self.incrementalSearchString: + idx = self.__searchHistory(self.incrementalSearchString, self.histidx) + if idx >= 0: + self.histidx = idx + self.__useHistory() + else: + idx = self.__searchHistory(buf) + if idx >= 0: + self.histidx = idx + self.incrementalSearchString = buf + self.__useHistory() + else: + if self.histidx >= 0 and self.histidx < len(self.history): + self.histidx += 1 + self.__useHistory() + + def __QScintillaCharLeftExtend(self): + """ + Private method to handle the Extend Selection Left command. + """ + self.__QScintillaLeftCommand(self.extendSelectionLeft, True) + + def __QScintillaWordLeftExtend(self): + """ + Private method to handle the Extend Selection Left one word command. + """ + self.__QScintillaLeftCommand(self.extendSelectionWordLeft, True) + + def __QScintillaVCHomeExtend(self): + """ + Private method to handle the Extend Selection to start of line command. + """ + col = self.__lastPos[1] + self.extendSelectionToBOL() + while col > 0: + self.extendSelectionRight() + col -= 1 + + def __executeCommand(self, cmd): + """ + Private slot to execute a command. + + @param cmd command to be executed by debug client (string) + """ + if not cmd: + cmd = '' + if len(self.history) == 0 or self.history[-1] != cmd: + if len(self.history) == self.maxHistoryEntries: + del self.history[0] + self.history.append(cmd) + self.histidx = -1 + + if cmd.lower() in ["clear", "cls"]: + self.clear() + return + else: + if not cmd.endswith("\n"): + cmd = "%s\n" % cmd + self.__send(cmd) + + def __useHistory(self): + """ + Private method to display a command from the history. + """ + if self.histidx < len(self.history): + cmd = self.history[self.histidx] + else: + cmd = "" + self.incrementalSearchString = "" + self.incrementalSearchActive = False + + self.__insertHistory(cmd) + + def __insertHistory(self, cmd): + """ + Private method to insert a command selected from the history. + + @param cmd history entry to be inserted (string) + """ + self.setCursorPosition(self.prline, self.prcol) + self.setSelection(self.prline, self.prcol,\ + self.prline, self.lineLength(self.prline)) + self.removeSelectedText() + self.__insertText(cmd) + + def __searchHistory(self, txt, startIdx = -1): + """ + Private method used to search the history. + + @param txt text to match at the beginning (string) + @param startIdx index to start search from (integer) + @return index of found entry (integer) + """ + if startIdx == -1: + idx = 0 + else: + idx = startIdx + 1 + while idx < len(self.history) and \ + not self.history[idx].startswith(txt): + idx += 1 + return idx + + def __rsearchHistory(self, txt, startIdx = -1): + """ + Private method used to reverse search the history. + + @param txt text to match at the beginning (string) + @param startIdx index to start search from (integer) + @return index of found entry (integer) + """ + if startIdx == -1: + idx = len(self.history) - 1 + else: + idx = startIdx - 1 + while idx >= 0 and \ + not self.history[idx].startswith(txt): + idx -= 1 + return idx + + def contextMenuEvent(self,ev): + """ + Reimplemented to show our own context menu. + + @param ev context menu event (QContextMenuEvent) + """ + self.menu.popup(ev.globalPos()) + ev.accept() + + def clear(self): + """ + Public slot to clear the display. + """ + QsciScintillaCompat.clear(self) + self.__send("\n") + + def __reset(self): + """ + Private slot to handle the 'reset' context menu entry. + """ + self.__stopShell() + self.__startShell() + + def __startShell(self): + """ + Private slot to start the shell process. + """ + args = [] + if Utilities.isWindowsPlatform(): + args.append("/Q") + self.__process.start("cmd.exe", args) + else: + shell = Preferences.getTerminal("Shell") + if not shell: + shell = os.environ.get('SHELL') + if shell is None: + self.__insertText(self.trUtf8("No shell has been configured.")) + return + if Preferences.getTerminal("ShellInteractive"): + args.append("-i") + self.__process.start(shell, args) + + def __stopShell(self): + """ + Private slot to stop the shell process. + """ + self.__process.kill() + res = self.__process.waitForFinished(3000) + + def handlePreferencesChanged(self): + """ + Public slot to handle the preferencesChanged signal. + """ + # rebind the lexer + self.__bindLexer() + self.recolor() + + # set margin 0 configuration + self.__setTextDisplay() + self.__setMargin0() + + # do the history related stuff + self.maxHistoryEntries = Preferences.getTerminal("MaxHistoryEntries") + self.history = self.history[-self.maxHistoryEntries:] + + # do the I/O encoding + self.__ioEncoding = Preferences.getSystem("IOEncoding") + + def focusInEvent(self, event): + """ + Public method called when the shell receives focus. + + @param event the event object (QFocusEvent) + """ + if not self.__actionsAdded: + self.addActions(self.vm.editorActGrp.actions()) + self.addActions(self.vm.copyActGrp.actions()) + self.addActions(self.vm.viewActGrp.actions()) + + try: + self.vm.editActGrp.setEnabled(False) + self.vm.editorActGrp.setEnabled(True) + self.vm.copyActGrp.setEnabled(True) + self.vm.viewActGrp.setEnabled(True) + except AttributeError: + pass + self.setCaretWidth(self.caretWidth) + QsciScintillaCompat.focusInEvent(self, event) + + def focusOutEvent(self, event): + """ + Public method called when the shell loses focus. + + @param event the event object (QFocusEvent) + """ + try: + self.vm.editorActGrp.setEnabled(False) + except AttributeError: + pass + self.setCaretWidth(0) + QsciScintillaCompat.focusOutEvent(self, event) + + def insert(self, txt): + """ + Public slot to insert text at the current cursor position. + + The cursor is advanced to the end of the inserted text. + + @param txt text to be inserted (string) + """ + l = len(txt) + line, col = self.getCursorPosition() + self.insertAt(txt, line, col) + if re.search(self.linesepRegExp, txt) is not None: + line += 1 + self.setCursorPosition(line, col + l) + + def __configure(self): + """ + Private method to open the configuration dialog. + """ + e4App().getObject("UserInterface").showPreferences("terminalPage")