QScintilla/Terminal.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2008 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a simple terminal based on QScintilla.
8 """
9
10 import sys
11 import os
12 import re
13
14 from PyQt4.QtCore import *
15 from PyQt4.QtGui import *
16 from PyQt4.Qsci import QsciScintilla
17
18 from E4Gui.E4Application import e4App
19
20 import Lexers
21 from QsciScintillaCompat import QsciScintillaCompat, QSCINTILLA_VERSION
22
23 import Preferences
24 import Utilities
25
26 import UI.PixmapCache
27 from Utilities import toUnicode
28
29 from ShellHistoryDialog import ShellHistoryDialog
30
31 class Terminal(QsciScintillaCompat):
32 """
33 Class implementing a simple terminal based on QScintilla.
34
35 A user can enter commands that are executed by a shell process.
36 """
37 def __init__(self, vm, parent = None):
38 """
39 Constructor
40
41 @param vm reference to the viewmanager object
42 @param parent parent widget (QWidget)
43 """
44 QsciScintillaCompat.__init__(self, parent)
45 self.setUtf8(True)
46
47 self.vm = vm
48
49 self.linesepRegExp = r"\r\n|\n|\r"
50
51 self.setWindowTitle(self.trUtf8('Terminal'))
52
53 self.setWhatsThis(self.trUtf8(
54 """<b>The Terminal Window</b>"""
55 """<p>This is a very simple terminal like window, that runs a shell"""
56 """ process in the background.</p>"""
57 """<p>The process can be stopped and started via the context menu. Some"""
58 """ Ctrl command may be sent as well. However, the shell may ignore"""
59 """ them.</p>"""
60 """<p>You can use the cursor keys while entering commands. There is also a"""
61 """ history of commands that can be recalled using the up and down cursor"""
62 """ keys. Pressing the up or down key after some text has been entered will"""
63 """ start an incremental search.</p>"""
64 ))
65
66 self.ansi_re = re.compile("\033\[\??[\d;]*\w")
67
68 # Initialise instance variables.
69 self.prline = 0
70 self.prcol = 0
71 self.inDragDrop = False
72 self.lexer_ = None
73
74 # Initialize history
75 self.maxHistoryEntries = Preferences.getTerminal("MaxHistoryEntries")
76 self.history = []
77 self.histidx = -1
78
79 # clear QScintilla defined keyboard commands
80 # we do our own handling through the view manager
81 self.clearAlternateKeys()
82 self.clearKeys()
83 self.__actionsAdded = False
84
85 # Create the history context menu
86 self.hmenu = QMenu(self.trUtf8('History'))
87 self.hmenu.addAction(self.trUtf8('Select entry'), self.__selectHistory)
88 self.hmenu.addAction(self.trUtf8('Show'), self.__showHistory)
89 self.hmenu.addAction(self.trUtf8('Clear'), self.__clearHistory)
90
91 # Create a little context menu to send Ctrl-C, Ctrl-D or Ctrl-Z
92 self.csm = QSignalMapper(self)
93 self.connect(self.csm, SIGNAL('mapped(int)'), self.__sendCtrl)
94
95 self.cmenu = QMenu(self.trUtf8('Ctrl Commands'))
96 act = self.cmenu.addAction(self.trUtf8('Ctrl-C'))
97 self.csm.setMapping(act, 3)
98 self.connect(act, SIGNAL('triggered()'), self.csm, SLOT('map()'))
99 act = self.cmenu.addAction(self.trUtf8('Ctrl-D'))
100 self.csm.setMapping(act, 4)
101 self.connect(act, SIGNAL('triggered()'), self.csm, SLOT('map()'))
102 act = self.cmenu.addAction(self.trUtf8('Ctrl-Z'))
103 self.csm.setMapping(act, 26)
104 self.connect(act, SIGNAL('triggered()'), self.csm, SLOT('map()'))
105
106 # Create a little context menu
107 self.menu = QMenu(self)
108 self.menu.addAction(self.trUtf8('Cut'), self.cut)
109 self.menu.addAction(self.trUtf8('Copy'), self.copy)
110 self.menu.addAction(self.trUtf8('Paste'), self.paste)
111 self.menu.addMenu(self.hmenu)
112 self.menu.addSeparator()
113 self.menu.addAction(self.trUtf8('Clear'), self.clear)
114 self.__startAct = self.menu.addAction(self.trUtf8("Start"), self.__startShell)
115 self.__stopAct = self.menu.addAction(self.trUtf8("Stop"), self.__stopShell)
116 self.__resetAct = self.menu.addAction(self.trUtf8('Reset'), self.__reset)
117 self.menu.addSeparator()
118 self.__ctrlAct = self.menu.addMenu(self.cmenu)
119 self.menu.addSeparator()
120 self.menu.addAction(self.trUtf8("Configure..."), self.__configure)
121
122 self.__bindLexer()
123 self.__setTextDisplay()
124 self.__setMargin0()
125
126 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
127
128 self.incrementalSearchString = ""
129 self.incrementalSearchActive = False
130
131 self.supportedEditorCommands = {
132 QsciScintilla.SCI_LINEDELETE : self.__clearCurrentLine,
133 QsciScintilla.SCI_NEWLINE : self.__QScintillaNewline,
134
135 QsciScintilla.SCI_DELETEBACK : self.__QScintillaDeleteBack,
136 QsciScintilla.SCI_CLEAR : self.__QScintillaDelete,
137 QsciScintilla.SCI_DELWORDLEFT : self.__QScintillaDeleteWordLeft,
138 QsciScintilla.SCI_DELWORDRIGHT : self.__QScintillaDeleteWordRight,
139 QsciScintilla.SCI_DELLINELEFT : self.__QScintillaDeleteLineLeft,
140 QsciScintilla.SCI_DELLINERIGHT : self.__QScintillaDeleteLineRight,
141
142 QsciScintilla.SCI_CHARLEFT : self.__QScintillaCharLeft,
143 QsciScintilla.SCI_CHARRIGHT : self.__QScintillaCharRight,
144 QsciScintilla.SCI_WORDLEFT : self.__QScintillaWordLeft,
145 QsciScintilla.SCI_WORDRIGHT : self.__QScintillaWordRight,
146 QsciScintilla.SCI_VCHOME : self.__QScintillaVCHome,
147 QsciScintilla.SCI_LINEEND : self.__QScintillaLineEnd,
148 QsciScintilla.SCI_LINEUP : self.__QScintillaLineUp,
149 QsciScintilla.SCI_LINEDOWN : self.__QScintillaLineDown,
150
151 QsciScintilla.SCI_CHARLEFTEXTEND : self.__QScintillaCharLeftExtend,
152 QsciScintilla.SCI_CHARRIGHTEXTEND : self.extendSelectionRight,
153 QsciScintilla.SCI_WORDLEFTEXTEND : self.__QScintillaWordLeftExtend,
154 QsciScintilla.SCI_WORDRIGHTEXTEND : self.extendSelectionWordRight,
155 QsciScintilla.SCI_VCHOMEEXTEND : self.__QScintillaVCHomeExtend,
156 QsciScintilla.SCI_LINEENDEXTEND : self.extendSelectionToEOL,
157 }
158
159 self.__ioEncoding = Preferences.getSystem("IOEncoding")
160
161 self.__process = QProcess()
162 self.__process.setProcessChannelMode(QProcess.MergedChannels)
163 self.__process.setReadChannel(QProcess.StandardOutput)
164
165 self.connect(self.__process, SIGNAL("readyReadStandardOutput()"),
166 self.__readOutput)
167 self.connect(self.__process, SIGNAL("started()"), self.__started)
168 self.connect(self.__process, SIGNAL("finished(int)"), self.__finished)
169
170 self.__ctrl = {}
171 for ascii_number, letter in enumerate("abcdefghijklmnopqrstuvwxyz"):
172 self.__ctrl[letter] = chr(ascii_number + 1)
173
174 self.__lastPos = (0, 0)
175
176 self.__startShell()
177
178 def __readOutput(self):
179 """
180 Private method to process the output of the shell.
181 """
182 output = unicode(self.__process.readAllStandardOutput(),
183 self.__ioEncoding, 'replace')
184 self.__write(self.ansi_re.sub("", output))
185 self.__lastPos = self.__getEndPos()
186
187 def __started(self):
188 """
189 Private method called, when the shell process has started.
190 """
191 if not Utilities.isWindowsPlatform():
192 QTimer.singleShot(250, self.clear)
193
194 self.__startAct.setEnabled(False)
195 self.__stopAct.setEnabled(True)
196 self.__resetAct.setEnabled(True)
197 self.__ctrlAct.setEnabled(True)
198
199 def __finished(self):
200 """
201 Private method called, when the shell process has finished.
202 """
203 QsciScintilla.clear(self)
204
205 self.__startAct.setEnabled(True)
206 self.__stopAct.setEnabled(False)
207 self.__resetAct.setEnabled(False)
208 self.__ctrlAct.setEnabled(False)
209
210 def __send(self, data):
211 """
212 Private method to send data to the shell process.
213
214 @param data data to be sent to the shell process (string)
215 """
216 pdata = QByteArray()
217 pdata.append(data)
218 self.__process.write(pdata)
219
220 def __sendCtrl(self, cmd):
221 """
222 Private slot to send a control command to the shell process.
223
224 @param the control command to be sent (integer)
225 """
226 self.__send(chr(cmd))
227
228 def closeTerminal(self):
229 """
230 Public method to shutdown the terminal.
231 """
232 self.__stopShell()
233 self.saveHistory()
234
235 def __bindLexer(self):
236 """
237 Private slot to set the lexer.
238 """
239 if Utilities.isWindowsPlatform():
240 self.language = "Batch"
241 else:
242 self.language = "Bash"
243 if Preferences.getTerminal("SyntaxHighlightingEnabled"):
244 self.lexer_ = Lexers.getLexer(self.language, self)
245 else:
246 self.lexer_ = None
247
248 if self.lexer_ is None:
249 self.setLexer(None)
250 font = Preferences.getTerminal("MonospacedFont")
251 self.monospacedStyles(font)
252 return
253
254 # get the font for style 0 and set it as the default font
255 key = 'Scintilla/%s/style0/font' % self.lexer_.language()
256 fontVariant = Preferences.Prefs.settings.value(key)
257 if fontVariant.isValid():
258 fdesc = fontVariant.toStringList()
259 font = QFont(fdesc[0], int(fdesc[1]))
260 self.lexer_.setDefaultFont(font)
261 self.setLexer(self.lexer_)
262 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla")
263
264 def __setMargin0(self):
265 """
266 Private method to configure margin 0.
267 """
268 # set the settings for all margins
269 self.setMarginsFont(Preferences.getTerminal("MarginsFont"))
270 self.setMarginsForegroundColor(Preferences.getEditorColour("MarginsForeground"))
271 self.setMarginsBackgroundColor(Preferences.getEditorColour("MarginsBackground"))
272
273 # set margin 0 settings
274 linenoMargin = Preferences.getTerminal("LinenoMargin")
275 self.setMarginLineNumbers(0, linenoMargin)
276 if linenoMargin:
277 self.setMarginWidth(0, ' ' + '8' * Preferences.getTerminal("LinenoWidth"))
278 else:
279 self.setMarginWidth(0, 0)
280
281 # disable margins 1 and 2
282 self.setMarginWidth(1, 0)
283 self.setMarginWidth(2, 0)
284
285 def __setTextDisplay(self):
286 """
287 Private method to configure the text display.
288 """
289 self.setTabWidth(Preferences.getEditor("TabWidth"))
290 if Preferences.getEditor("ShowWhitespace"):
291 self.setWhitespaceVisibility(QsciScintilla.WsVisible)
292 else:
293 self.setWhitespaceVisibility(QsciScintilla.WsInvisible)
294 self.setEolVisibility(Preferences.getEditor("ShowEOL"))
295 if Preferences.getEditor("BraceHighlighting"):
296 self.setBraceMatching(QsciScintilla.SloppyBraceMatch)
297 else:
298 self.setBraceMatching(QsciScintilla.NoBraceMatch)
299 self.setMatchedBraceForegroundColor(
300 Preferences.getEditorColour("MatchingBrace"))
301 self.setMatchedBraceBackgroundColor(
302 Preferences.getEditorColour("MatchingBraceBack"))
303 self.setUnmatchedBraceForegroundColor(
304 Preferences.getEditorColour("NonmatchingBrace"))
305 self.setUnmatchedBraceBackgroundColor(
306 Preferences.getEditorColour("NonmatchingBraceBack"))
307 if Preferences.getEditor("CustomSelectionColours"):
308 self.setSelectionBackgroundColor(\
309 Preferences.getEditorColour("SelectionBackground"))
310 else:
311 self.setSelectionBackgroundColor(\
312 QApplication.palette().color(QPalette.Highlight))
313 if Preferences.getEditor("ColourizeSelText"):
314 self.resetSelectionForegroundColor()
315 elif Preferences.getEditor("CustomSelectionColours"):
316 self.setSelectionForegroundColor(\
317 Preferences.getEditorColour("SelectionForeground"))
318 else:
319 self.setSelectionForegroundColor(\
320 QApplication.palette().color(QPalette.HighlightedText))
321 self.setSelectionToEol(Preferences.getEditor("ExtendSelectionToEol"))
322 self.setCaretForegroundColor(
323 Preferences.getEditorColour("CaretForeground"))
324 self.setCaretLineBackgroundColor(
325 Preferences.getEditorColour("CaretLineBackground"))
326 self.setCaretLineVisible(Preferences.getEditor("CaretLineVisible"))
327 self.caretWidth = Preferences.getEditor("CaretWidth")
328 self.setCaretWidth(self.caretWidth)
329 self.setWrapMode(QsciScintilla.WrapNone)
330 self.useMonospaced = Preferences.getTerminal("UseMonospacedFont")
331 self.__setMonospaced(self.useMonospaced)
332
333 def __setMonospaced(self, on):
334 """
335 Private method to set/reset a monospaced font.
336
337 @param on flag to indicate usage of a monospace font (boolean)
338 """
339 if on:
340 f = Preferences.getTerminal("MonospacedFont")
341 self.monospacedStyles(f)
342 else:
343 if not self.lexer_:
344 self.clearStyles()
345 self.__setMargin0()
346 self.setFont(Preferences.getTerminal("MonospacedFont"))
347
348 self.useMonospaced = on
349
350 def loadHistory(self):
351 """
352 Public method to load the history.
353 """
354 hVariant = Preferences.Prefs.settings.value("Terminal/History")
355 if hVariant.isValid():
356 hl = hVariant.toStringList()
357 self.history = hl[-self.maxHistoryEntries:]
358 else:
359 self.history = []
360
361 def reloadHistory(self):
362 """
363 Public method to reload the history.
364 """
365 self.loadHistory(self.clientType)
366 self.history = self.historyLists[self.clientType]
367 self.histidx = -1
368
369 def saveHistory(self):
370 """
371 Public method to save the history.
372 """
373 Preferences.Prefs.settings.setValue(\
374 "Terminal/History", QVariant(self.history))
375
376 def getHistory(self):
377 """
378 Public method to get the history.
379
380 @return reference to the history list (list of strings)
381 """
382 return self.history
383
384 def __clearHistory(self):
385 """
386 Private slot to clear the current history.
387 """
388 self.history.clear()
389
390 def __selectHistory(self):
391 """
392 Private slot to select a history entry to execute.
393 """
394 cmd, ok = QInputDialog.getItem(\
395 self,
396 self.trUtf8("Select History"),
397 self.trUtf8("Select the history entry to execute (most recent shown last)."),
398 self.history,
399 0, False)
400 if ok:
401 self.__insertHistory(cmd)
402
403 def __showHistory(self):
404 """
405 Private slot to show the shell history dialog.
406 """
407 dlg = ShellHistoryDialog(self.history, self.vm, self)
408 if dlg.exec_() == QDialog.Accepted:
409 self.history = dlg.getHistory()
410 self.histidx = -1
411
412 def __getEndPos(self):
413 """
414 Private method to return the line and column of the last character.
415
416 @return tuple of two values (int, int) giving the line and column
417 """
418 line = self.lines() - 1
419 return (line, self.lineLength(line))
420
421 def __write(self, s):
422 """
423 Private method to display some text.
424
425 @param s text to be displayed (string)
426 """
427 line, col = self.__getEndPos()
428 self.setCursorPosition(line, col)
429 self.insert(toUnicode(s))
430 self.prline, self.prcol = self.getCursorPosition()
431 self.ensureCursorVisible()
432 self.ensureLineVisible(self.prline)
433
434 def __clearCurrentLine(self):
435 """
436 Private method to clear the line containing the cursor.
437 """
438 line, col = self.getCursorPosition()
439 if self.text(line).startswith(sys.ps1):
440 col = len(sys.ps1)
441 elif self.text(line).startswith(sys.ps2):
442 col = len(sys.ps2)
443 else:
444 col = 0
445 self.setCursorPosition(line, col)
446 self.deleteLineRight()
447
448 def __insertText(self, s):
449 """
450 Private method to insert some text at the current cursor position.
451
452 @param s text to be inserted (string)
453 """
454 line, col = self.getCursorPosition()
455 self.insertAt(s, line, col)
456 self.setCursorPosition(line, col + len(s))
457
458 def __insertTextAtEnd(self, s):
459 """
460 Private method to insert some text at the end of the command line.
461
462 @param s text to be inserted (string)
463 """
464 line, col = self.__getEndPos()
465 self.setCursorPosition(line, col)
466 self.insert(s)
467 self.prline, self.prcol = self.getCursorPosition()
468
469 def mousePressEvent(self, event):
470 """
471 Protected method to handle the mouse press event.
472
473 @param event the mouse press event (QMouseEvent)
474 """
475 self.setFocus()
476 QsciScintillaCompat.mousePressEvent(self, event)
477
478 def editorCommand(self, cmd):
479 """
480 Public method to perform an editor command.
481
482 @param cmd the scintilla command to be performed
483 """
484 try:
485 self.supportedEditorCommands[cmd]()
486 except TypeError:
487 self.supportedEditorCommands[cmd](cmd)
488 except KeyError:
489 pass
490
491 def __isCursorOnLastLine(self):
492 """
493 Private method to check, if the cursor is on the last line.
494 """
495 cline, ccol = self.getCursorPosition()
496 return cline == self.lines() - 1
497
498 def keyPressEvent(self, ev):
499 """
500 Re-implemented to handle the user input a key at a time.
501
502 @param ev key event (QKeyEvent)
503 """
504 txt = ev.text()
505 key = ev.key()
506
507 # See it is text to insert.
508 if len(txt) and txt >= " ":
509 if not self.__isCursorOnLastLine():
510 line, col = self.__getEndPos()
511 self.setCursorPosition(line, col)
512 self.prline, self.prcol = self.getCursorPosition()
513 QsciScintillaCompat.keyPressEvent(self, ev)
514 self.incrementalSearchActive = True
515 else:
516 ev.ignore()
517
518 def __QScintillaLeftDeleteCommand(self, method):
519 """
520 Private method to handle a QScintilla delete command working to the left.
521
522 @param method shell method to execute
523 """
524 if self.__isCursorOnLastLine():
525 line, col = self.getCursorPosition()
526 if col > self.__lastPos[1]:
527 method()
528
529 def __QScintillaDeleteBack(self):
530 """
531 Private method to handle the Backspace key.
532 """
533 self.__QScintillaLeftDeleteCommand(self.deleteBack)
534
535 def __QScintillaDeleteWordLeft(self):
536 """
537 Private method to handle the Delete Word Left command.
538 """
539 self.__QScintillaLeftDeleteCommand(self.deleteWordLeft)
540
541 def __QScintillaDelete(self):
542 """
543 Private method to handle the delete command.
544 """
545 if self.__isCursorOnLastLine():
546 if self.hasSelectedText():
547 lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
548 if indexFrom >= self.__lastPos[1]:
549 self.delete()
550 self.setSelection(lineTo, indexTo, lineTo, indexTo)
551 else:
552 self.delete()
553
554 def __QScintillaDeleteLineLeft(self):
555 """
556 Private method to handle the Delete Line Left command.
557 """
558 if self.__isCursorOnLastLine():
559 if self.isListActive():
560 self.cancelList()
561
562 line, col = self.getCursorPosition()
563 prompt = self.text(line)[:self.__lastPos[1]]
564 self.deleteLineLeft()
565 self.insertAt(prompt, line, 0)
566 self.setCursorPosition(line, len(prompt))
567
568 def __QScintillaNewline(self, cmd):
569 """
570 Private method to handle the Return key.
571
572 @param cmd QScintilla command
573 """
574 if self.__isCursorOnLastLine():
575 self.incrementalSearchString = ""
576 self.incrementalSearchActive = False
577 line, col = self.__getEndPos()
578 self.setCursorPosition(line, col)
579 self.setSelection(*(self.__lastPos + self.getCursorPosition()))
580 buf = self.selectedText()
581 self.setCursorPosition(line, col) # select nothin
582 self.insert('\n')
583 self.__executeCommand(buf)
584
585 def __QScintillaLeftCommand(self, method, allLinesAllowed = False):
586 """
587 Private method to handle a QScintilla command working to the left.
588
589 @param method shell method to execute
590 """
591 if self.__isCursorOnLastLine() or allLinesAllowed:
592 line, col = self.getCursorPosition()
593 if col > self.__lastPos[1]:
594 method()
595
596 def __QScintillaCharLeft(self):
597 """
598 Private method to handle the Cursor Left command.
599 """
600 self.__QScintillaLeftCommand(self.moveCursorLeft)
601
602 def __QScintillaWordLeft(self):
603 """
604 Private method to handle the Cursor Word Left command.
605 """
606 self.__QScintillaLeftCommand(self.moveCursorWordLeft)
607
608 def __QScintillaRightCommand(self, method):
609 """
610 Private method to handle a QScintilla command working to the right.
611
612 @param method shell method to execute
613 """
614 if self.__isCursorOnLastLine():
615 method()
616
617 def __QScintillaCharRight(self):
618 """
619 Private method to handle the Cursor Right command.
620 """
621 self.__QScintillaRightCommand(self.moveCursorRight)
622
623 def __QScintillaWordRight(self):
624 """
625 Private method to handle the Cursor Word Right command.
626 """
627 self.__QScintillaRightCommand(self.moveCursorWordRight)
628
629 def __QScintillaDeleteWordRight(self):
630 """
631 Private method to handle the Delete Word Right command.
632 """
633 self.__QScintillaRightCommand(self.deleteWordRight)
634
635 def __QScintillaDeleteLineRight(self):
636 """
637 Private method to handle the Delete Line Right command.
638 """
639 self.__QScintillaRightCommand(self.deleteLineRight)
640
641 def __QScintillaVCHome(self, cmd):
642 """
643 Private method to handle the Home key.
644
645 @param cmd QScintilla command
646 """
647 self.setCursorPosition(*self.__lastPos)
648
649 def __QScintillaLineEnd(self, cmd):
650 """
651 Private method to handle the End key.
652
653 @param cmd QScintilla command
654 """
655 self.moveCursorToEOL()
656
657 def __QScintillaLineUp(self, cmd):
658 """
659 Private method to handle the Up key.
660
661 @param cmd QScintilla command
662 """
663 line, col = self.__getEndPos()
664 buf = self.text(line)[self.__lastPos[1]:]
665 if buf and self.incrementalSearchActive:
666 if self.incrementalSearchString:
667 idx = self.__rsearchHistory(self.incrementalSearchString,
668 self.histidx)
669 if idx >= 0:
670 self.histidx = idx
671 self.__useHistory()
672 else:
673 idx = self.__rsearchHistory(buf)
674 if idx >= 0:
675 self.histidx = idx
676 self.incrementalSearchString = buf
677 self.__useHistory()
678 else:
679 if self.histidx < 0:
680 self.histidx = len(self.history)
681 if self.histidx > 0:
682 self.histidx = self.histidx - 1
683 self.__useHistory()
684
685 def __QScintillaLineDown(self, cmd):
686 """
687 Private method to handle the Down key.
688
689 @param cmd QScintilla command
690 """
691 line, col = self.__getEndPos()
692 buf = self.text(line)[self.__lastPos[1]:]
693 if buf and self.incrementalSearchActive:
694 if self.incrementalSearchString:
695 idx = self.__searchHistory(self.incrementalSearchString, self.histidx)
696 if idx >= 0:
697 self.histidx = idx
698 self.__useHistory()
699 else:
700 idx = self.__searchHistory(buf)
701 if idx >= 0:
702 self.histidx = idx
703 self.incrementalSearchString = buf
704 self.__useHistory()
705 else:
706 if self.histidx >= 0 and self.histidx < len(self.history):
707 self.histidx += 1
708 self.__useHistory()
709
710 def __QScintillaCharLeftExtend(self):
711 """
712 Private method to handle the Extend Selection Left command.
713 """
714 self.__QScintillaLeftCommand(self.extendSelectionLeft, True)
715
716 def __QScintillaWordLeftExtend(self):
717 """
718 Private method to handle the Extend Selection Left one word command.
719 """
720 self.__QScintillaLeftCommand(self.extendSelectionWordLeft, True)
721
722 def __QScintillaVCHomeExtend(self):
723 """
724 Private method to handle the Extend Selection to start of line command.
725 """
726 col = self.__lastPos[1]
727 self.extendSelectionToBOL()
728 while col > 0:
729 self.extendSelectionRight()
730 col -= 1
731
732 def __executeCommand(self, cmd):
733 """
734 Private slot to execute a command.
735
736 @param cmd command to be executed by debug client (string)
737 """
738 if not cmd:
739 cmd = ''
740 if len(self.history) == 0 or self.history[-1] != cmd:
741 if len(self.history) == self.maxHistoryEntries:
742 del self.history[0]
743 self.history.append(cmd)
744 self.histidx = -1
745
746 if cmd.lower() in ["clear", "cls"]:
747 self.clear()
748 return
749 else:
750 if not cmd.endswith("\n"):
751 cmd = "%s\n" % cmd
752 self.__send(cmd)
753
754 def __useHistory(self):
755 """
756 Private method to display a command from the history.
757 """
758 if self.histidx < len(self.history):
759 cmd = self.history[self.histidx]
760 else:
761 cmd = ""
762 self.incrementalSearchString = ""
763 self.incrementalSearchActive = False
764
765 self.__insertHistory(cmd)
766
767 def __insertHistory(self, cmd):
768 """
769 Private method to insert a command selected from the history.
770
771 @param cmd history entry to be inserted (string)
772 """
773 self.setCursorPosition(self.prline, self.prcol)
774 self.setSelection(self.prline, self.prcol,\
775 self.prline, self.lineLength(self.prline))
776 self.removeSelectedText()
777 self.__insertText(cmd)
778
779 def __searchHistory(self, txt, startIdx = -1):
780 """
781 Private method used to search the history.
782
783 @param txt text to match at the beginning (string)
784 @param startIdx index to start search from (integer)
785 @return index of found entry (integer)
786 """
787 if startIdx == -1:
788 idx = 0
789 else:
790 idx = startIdx + 1
791 while idx < len(self.history) and \
792 not self.history[idx].startswith(txt):
793 idx += 1
794 return idx
795
796 def __rsearchHistory(self, txt, startIdx = -1):
797 """
798 Private method used to reverse search the history.
799
800 @param txt text to match at the beginning (string)
801 @param startIdx index to start search from (integer)
802 @return index of found entry (integer)
803 """
804 if startIdx == -1:
805 idx = len(self.history) - 1
806 else:
807 idx = startIdx - 1
808 while idx >= 0 and \
809 not self.history[idx].startswith(txt):
810 idx -= 1
811 return idx
812
813 def contextMenuEvent(self,ev):
814 """
815 Reimplemented to show our own context menu.
816
817 @param ev context menu event (QContextMenuEvent)
818 """
819 self.menu.popup(ev.globalPos())
820 ev.accept()
821
822 def clear(self):
823 """
824 Public slot to clear the display.
825 """
826 QsciScintillaCompat.clear(self)
827 self.__send("\n")
828
829 def __reset(self):
830 """
831 Private slot to handle the 'reset' context menu entry.
832 """
833 self.__stopShell()
834 self.__startShell()
835
836 def __startShell(self):
837 """
838 Private slot to start the shell process.
839 """
840 args = []
841 if Utilities.isWindowsPlatform():
842 args.append("/Q")
843 self.__process.start("cmd.exe", args)
844 else:
845 shell = Preferences.getTerminal("Shell")
846 if not shell:
847 shell = os.environ.get('SHELL')
848 if shell is None:
849 self.__insertText(self.trUtf8("No shell has been configured."))
850 return
851 if Preferences.getTerminal("ShellInteractive"):
852 args.append("-i")
853 self.__process.start(shell, args)
854
855 def __stopShell(self):
856 """
857 Private slot to stop the shell process.
858 """
859 self.__process.kill()
860 res = self.__process.waitForFinished(3000)
861
862 def handlePreferencesChanged(self):
863 """
864 Public slot to handle the preferencesChanged signal.
865 """
866 # rebind the lexer
867 self.__bindLexer()
868 self.recolor()
869
870 # set margin 0 configuration
871 self.__setTextDisplay()
872 self.__setMargin0()
873
874 # do the history related stuff
875 self.maxHistoryEntries = Preferences.getTerminal("MaxHistoryEntries")
876 self.history = self.history[-self.maxHistoryEntries:]
877
878 # do the I/O encoding
879 self.__ioEncoding = Preferences.getSystem("IOEncoding")
880
881 def focusInEvent(self, event):
882 """
883 Public method called when the shell receives focus.
884
885 @param event the event object (QFocusEvent)
886 """
887 if not self.__actionsAdded:
888 self.addActions(self.vm.editorActGrp.actions())
889 self.addActions(self.vm.copyActGrp.actions())
890 self.addActions(self.vm.viewActGrp.actions())
891
892 try:
893 self.vm.editActGrp.setEnabled(False)
894 self.vm.editorActGrp.setEnabled(True)
895 self.vm.copyActGrp.setEnabled(True)
896 self.vm.viewActGrp.setEnabled(True)
897 except AttributeError:
898 pass
899 self.setCaretWidth(self.caretWidth)
900 QsciScintillaCompat.focusInEvent(self, event)
901
902 def focusOutEvent(self, event):
903 """
904 Public method called when the shell loses focus.
905
906 @param event the event object (QFocusEvent)
907 """
908 try:
909 self.vm.editorActGrp.setEnabled(False)
910 except AttributeError:
911 pass
912 self.setCaretWidth(0)
913 QsciScintillaCompat.focusOutEvent(self, event)
914
915 def insert(self, txt):
916 """
917 Public slot to insert text at the current cursor position.
918
919 The cursor is advanced to the end of the inserted text.
920
921 @param txt text to be inserted (string)
922 """
923 l = len(txt)
924 line, col = self.getCursorPosition()
925 self.insertAt(txt, line, col)
926 if re.search(self.linesepRegExp, txt) is not None:
927 line += 1
928 self.setCursorPosition(line, col + l)
929
930 def __configure(self):
931 """
932 Private method to open the configuration dialog.
933 """
934 e4App().getObject("UserInterface").showPreferences("terminalPage")

eric ide

mercurial