# -*- python -*-
#
# OpenAlea.OALab: Multi-Paradigm GUI
#
# Copyright 2013 INRIA - CIRAD - INRA
#
# File author(s): Julien Coste <julien.coste@inria.fr>
#
# File contributor(s):
#
# Distributed under the Cecill-C License.
# See accompanying file LICENSE.txt or copy at
# http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
# OpenAlea WebSite : http://openalea.gforge.inria.fr
#
###############################################################################
__revision__ = ""
from openalea.vpltk.qt import QtCore, QtGui
from openalea.core.path import path
from openalea.oalab.editor.search import SearchWidget
from openalea.oalab.editor.completion import DictionaryCompleter
from openalea.oalab.editor.line_number import Margin
from openalea.oalab.editor.goto import GoToWidget
from openalea.core import logger
from openalea.core import settings
[docs]class RichTextEditor(QtGui.QWidget):
def __init__(self, session, controller, parent=None):
super(RichTextEditor, self).__init__(parent)
self.completer = DictionaryCompleter(parent=self)
self.editor = TextEditor(session=session, controller=controller, parent=self)
# self.editor.setCompleter(self.completer)
self.goto_widget = GoToWidget(parent=self.editor)
self.search_widget = SearchWidget(parent=self, session=session)
self.layout = QtGui.QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.editor)
self.layout.addWidget(self.search_widget)
self.setLayout(self.layout)
self.search_widget.hide()
[docs] def actions(self):
"""
:return: list of actions to set in the menu.
"""
return None
[docs] def mainMenu(self):
return "Simulation"
[docs] def set_text(self, txt):
"""
Set text in the editor
:param text: text you want to set
"""
self.editor.set_text(txt)
set_script = set_text
[docs] def get_selected_text(self):
return self.editor.get_selected_text()
[docs] def get_text(self, start='sof', end='eof'):
"""
Return a part of the text.
:param start: is the begining of what you want to get
:param end: is the end of what you want to get
:return: text which is contained in the editor between 'start' and 'end'
"""
return self.editor.get_text(start='sof', end='eof')
[docs] def save(self, name=None):
self.editor.save(name)
[docs] def goto(self):
self.goto_widget.show()
[docs] def undo(self):
self.editor.undo()
[docs] def redo(self):
self.editor.redo()
[docs] def search(self):
if self.search_widget.hiden:
self.search_widget.show()
# self.search_widget.raise_()
self.search_widget.lineEdit.setFocus()
txt = self.get_selected_text()
self.search_widget.lineEdit.setText(txt)
self.search_widget.hiden = False
else:
self.search_widget.hide()
self.search_widget.hiden = True
[docs]class TextEditor(QtGui.QTextEdit):
def __init__(self, session, controller, parent=None):
super(TextEditor, self).__init__(parent)
self.session = session
self.controller = controller
self.indentation = " "
self.completer = None
self.name = None
self.set_tab_size()
# Line Number Area from LPy
self.setViewportMargins(50, 0, 0, 0)
self.sidebar = Margin(self, self)
self.sidebar.setGeometry(0, 0, 50, 100)
self.sidebar.show()
QtCore.QObject.connect(self, QtCore.SIGNAL("cursorPositionChanged()"), self.display_line_number)
QtCore.QObject.connect(self, QtCore.SIGNAL("textChanged()"), self.controller.applet_container.setTabRed)
# QtCore.QObject.connect(self, QtCore.SIGNAL("cursorPositionChanged()"),self.highlightCurrentLine)
self.default_names = [applet.default_file_name for applet in self.controller.applet_container.paradigms.values()]
[docs] def set_tab_size(self):
# Set tab size : to fix
try:
font = self.currentFont()
metrics = QtGui.QFontMetrics(font)
length = metrics.width(self.indentation)
if length > 0:
self.setTabStopWidth(length)
else:
self.setTabStopWidth(14)
except:
pass
[docs] def actions(self):
"""
:return: list of actions to set in the menu.
"""
return None
[docs] def mainMenu(self):
return "Simulation"
[docs] def setText(self, txt):
self.setPlainText(txt)
self.controller.applet_container.setTabBlack()
[docs] def set_text(self, txt):
"""
Set text in the editor
:param text: text you want to set
"""
self.setText(txt)
set_script = set_text
[docs] def get_selected_text(self):
cursor = self.textCursor()
txt = cursor.selectedText()
return txt
[docs] def get_text(self, start='sof', end='eof'):
"""
Return a part of the text.
:param start: is the begining of what you want to get
:param end: is the end of what you want to get
:return: text which is contained in the editor between 'start' and 'end'
"""
txt = self.toPlainText()
if txt is None:
txt = ""
return txt
[docs] def save(self, name=None):
"""
Save current file.
:param name: name of the file to save.
If not name, self.name is used.
If self.name is not setted and name is not give in parameter,
a File Dialog is opened.
"""
logger.debug("Try to save text")
txt = self.get_text()
if self.session.current_is_project():
if name is not None:
self.name = name
if self.name is None:
temp_path = path(settings.get_project_dir())
self.name = QtGui.QFileDialog.getSaveFileName(self, 'Select name to save the file', temp_path)
if self.name is not None:
project = self.session.project
project.src[self.name] = txt
project._save("src")
self.controller.applet_container.setAllTabBlack()
logger.debug("Try to save script in project")
[docs] def keyPressEvent(self, event):
# Auto-indent
if event.key() == QtCore.Qt.Key_Return or event.key() == QtCore.Qt.Key_Enter:
super(TextEditor, self).keyPressEvent(event)
self.returnEvent()
return
elif self.completer and self.completer.popup().isVisible():
if event.key() in (
QtCore.Qt.Key_Enter,
QtCore.Qt.Key_Return,
QtCore.Qt.Key_Escape,
QtCore.Qt.Key_Tab,
QtCore.Qt.Key_Backtab):
event.ignore()
return
# # has ctrl-E been pressed??
isShortcut = (event.modifiers() == QtCore.Qt.ControlModifier and
event.key() == QtCore.Qt.Key_E)
if (not self.completer or not isShortcut):
super(TextEditor, self).keyPressEvent(event)
# # ctrl or shift key on it's own??
ctrlOrShift = event.modifiers() in (QtCore.Qt.ControlModifier ,
QtCore.Qt.ShiftModifier)
if ctrlOrShift and event.text() is str():
# ctrl or shift key on it's own
return
eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" # end of word
hasModifier = ((event.modifiers() != QtCore.Qt.NoModifier) and
not ctrlOrShift)
completionPrefix = self.textUnderCursor()
if len(event.text()) == 0:
f = -1
else:
f = eow.find(event.text()[-1])
if f == -1:
finded = False
else:
finded = True
if self.completer is not None:
if (not isShortcut and (hasModifier or (event.text() is str()) or
len(completionPrefix) < 3 or finded)):
self.completer.popup().hide()
return
if (completionPrefix != self.completer.completionPrefix()):
self.completer.setCompletionPrefix(completionPrefix)
popup = self.completer.popup()
popup.setCurrentIndex(self.completer.completionModel().index(0, 0))
cr = self.cursorRect()
cr.setWidth(self.completer.popup().sizeHintForColumn(0)
+ self.completer.popup().verticalScrollBar().sizeHint().width())
self.completer.complete(cr) # # popup it up!
####################################################################
#### Auto Indent (cf lpycodeeditor)
####################################################################
[docs] def returnEvent(self):
cursor = self.textCursor()
beg = cursor.selectionStart()
end = cursor.selectionEnd()
if beg == end:
pos = cursor.position()
ok = cursor.movePosition(QtGui.QTextCursor.PreviousBlock, QtGui.QTextCursor.MoveAnchor)
if not ok: return
txtok = True
txt = ''
while txtok:
ok = cursor.movePosition(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor)
if not ok: break
txt2 = str(cursor.selection().toPlainText())
txtok = (txt2[-1] in ' \t')
if txtok:
txt = txt2
cursor.setPosition(pos)
ok = cursor.movePosition(QtGui.QTextCursor.PreviousBlock, QtGui.QTextCursor.MoveAnchor)
if ok:
ok = cursor.movePosition(QtGui.QTextCursor.EndOfBlock, QtGui.QTextCursor.MoveAnchor)
if ok:
txtok = True
while txtok:
ok = cursor.movePosition(QtGui.QTextCursor.PreviousCharacter, QtGui.QTextCursor.KeepAnchor)
if not ok: break
txt2 = str(cursor.selection().toPlainText())
txtok = (txt2[0] in ' \t')
if not txtok:
if txt2[0] == ':':
txt += self.indentation
cursor.setPosition(pos)
cursor.joinPreviousEditBlock()
cursor.insertText(txt)
cursor.endEditBlock()
####################################################################
#### (Un)Tab (cf lpycodeeditor)
#### TODO
####################################################################
# def tab(self, initcursor = None):
# if initcursor == False:
# initcursor = None
# cursor = self.textCursor() if initcursor is None else initcursor
# beg = cursor.selectionStart()
# end = cursor.selectionEnd()
# pos = cursor.position()
# if not initcursor : cursor.beginEditBlock()
# cursor.setPosition(beg,QtGui.QTextCursor.MoveAnchor)
# cursor.movePosition(QtGui.QTextCursor.StartOfBlock,QtGui.QTextCursor.MoveAnchor)
# while cursor.position() <= end :
# if self.replaceTab:
# cursor.insertText(self.indentation)
# end+=len(self.indentation)
# else:
# cursor.insertText('\t')
# end+=1
# oldpos = cursor.position()
# cursor.movePosition(QtGui.QTextCursor.NextBlock,QtGui.QTextCursor.MoveAnchor)
# if cursor.position() == oldpos:
# break
# if not initcursor : cursor.endEditBlock()
# cursor.setPosition(pos,QtGui.QTextCursor.MoveAnchor)
# def untab(self):
# cursor = self.textCursor()
# beg = cursor.selectionStart()
# end = cursor.selectionEnd()
# pos = cursor.position()
# cursor.beginEditBlock()
# cursor.setPosition(beg,QtGui.QTextCursor.MoveAnchor)
# cursor.movePosition(QtGui.QTextCursor.StartOfBlock,QtGui.QTextCursor.MoveAnchor)
# while cursor.position() <= end:
# cursor.movePosition(QtGui.QTextCursor.NextCharacter,QtGui.QTextCursor.KeepAnchor)
# if cursor.selectedText() == '\t':
# cursor.deleteChar()
# else:
# for i in xrange(len(self.indentation)-1):
# b = cursor.movePosition(QtGui.QTextCursor.NextCharacter,QtGui.QTextCursor.KeepAnchor)
# if not b : break
# if cursor.selectedText() == self.indentation:
# cursor.removeSelectedText()
# end-=1
# cursor.movePosition(QtGui.QTextCursor.Down,QtGui.QTextCursor.MoveAnchor)
# cursor.movePosition(QtGui.QTextCursor.StartOfBlock,QtGui.QTextCursor.MoveAnchor)
# cursor.endEditBlock()
# cursor.setPosition(pos,QtGui.QTextCursor.MoveAnchor)
####################################################################
#### (Un)Comment (cf lpycodeeditor)
####################################################################
[docs] def setCompleter(self, completer):
logger.debug("set completer " + str(completer))
if self.completer:
self.disconnect(self.completer, 0, self, 0)
if not completer:
return
completer.setWidget(self)
completer.setCompletionMode(QtGui.QCompleter.PopupCompletion)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.completer = completer
QtCore.QObject.connect(self.completer, QtCore.SIGNAL("activated(const QString&)"), self.insertCompletion)
[docs] def insertCompletion(self, completion):
logger.debug("insert completion")
tc = self.textCursor()
extra = (len(completion) -
len(self.completer.completionPrefix()))
tc.movePosition(QtGui.QTextCursor.Left)
tc.movePosition(QtGui.QTextCursor.EndOfWord)
tc.insertText(completion[-extra:])
self.setTextCursor(tc)
logger.debug("inserted completion")
[docs] def textUnderCursor(self):
tc = self.textCursor()
tc.select(QtGui.QTextCursor.WordUnderCursor)
return tc.selectedText()
[docs] def focusInEvent(self, event):
if self.completer:
self.completer.setWidget(self);
super(TextEditor, self).focusInEvent(event)
####################################################################
#### Line Number Area
####################################################################
[docs] def resizeEvent(self, event):
self.sidebar.setGeometry(0, 0, 48, self.height())
super(TextEditor, self).resizeEvent(event)
[docs] def scrollContentsBy(self, dx, dy):
self.sidebar.update()
self.sidebar.setFont(QtGui.QFont(self.currentFont()))
super(TextEditor, self).scrollContentsBy(dx, dy)
[docs] def display_line_number(self):
lineno = self.textCursor().blockNumber() + 1
columnno = self.textCursor().columnNumber()
try:
self.session.statusBar.showMessage("Cursor at line %s, column %s" % (lineno, columnno), 2000)
except:
pass
####################################################################
#### Line Number Area
####################################################################
[docs] def go_to_line(self, lineno):
cursor = self.textCursor()
cursor.setPosition(0)
cursor.movePosition(QtGui.QTextCursor.NextBlock, QtGui.QTextCursor.MoveAnchor, lineno - 1)
self.setTextCursor(cursor)
self.ensureCursorVisible()