예제 #1
0
파일: editor.py 프로젝트: ekimdev/edis
    def __init__(self, nombre_archivo, ext=''):
        super(Editor, self).__init__()
        self.__nombre = ""
        self.texto_modificado = False
        self.es_nuevo = True
        self.guardado_actualmente = False
        # Actualiza flags (espacios en blanco, cursor, sidebar, etc)
        self.actualizar()
        # Lexer
        self._lexer = None
        self.cargar_lexer(ext)
        # Autocompletado
        #FIXME:
        api = QsciAPIs(self._lexer)
        for palabra in keywords.keywords:
            api.add(palabra)
        api.prepare()
        self.setAutoCompletionThreshold(1)
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        # Indentación
        self._indentacion = ESettings.get('editor/indentacionAncho')
        self.send("sci_settabwidth", self._indentacion)
        # Minimapa
        self.minimapa = MiniMapa(self)
        self.connect(self, SIGNAL("selectionChanged()"), self.minimapa.area)
        self.connect(self, SIGNAL("textChanged()"),
                     self.minimapa.actualizar_codigo)
        # Thread ocurrencias
        self.hilo_ocurrencias = ThreadBusqueda()
        self.connect(self.hilo_ocurrencias,
                     SIGNAL("ocurrenciasThread(PyQt_PyObject)"),
                     self.marcar_palabras)
        # Analizador de estilo de código
        self.checker = checker.Checker(self)
        self.connect(self.checker, SIGNAL("finished()"), self._show_violations)
        #self.checker.errores.connect(self._marcar_errores)
        # Fuente
        fuente = ESettings.get('editor/fuente')
        tam_fuente = ESettings.get('editor/fuenteTam')
        self.cargar_fuente(fuente, tam_fuente)
        self.setMarginsBackgroundColor(QColor(self._tema['sidebar-fondo']))
        self.setMarginsForegroundColor(QColor(self._tema['sidebar-fore']))

        # Línea actual, cursor
        self.caret_line(self._tema['caret-background'],
                        self._tema['caret-line'], self._tema['caret-opacidad'])
        # Márgen
        if ESettings.get('editor/margen'):
            self.actualizar_margen()

        # Brace matching
        self.match_braces(Base.SloppyBraceMatch)
        self.match_braces_color(self._tema['brace-background'],
                                self._tema['brace-foreground'])
        self.unmatch_braces_color(self._tema['brace-unbackground'],
                                  self._tema['brace-unforeground'])
예제 #2
0
class RevsetEntry(QsciScintilla):

    returnPressed = pyqtSignal()

    def __init__(self, parent=None):
        super(RevsetEntry, self).__init__(parent)
        self.setMarginWidth(1, 0)
        self.setReadOnly(False)
        self.setUtf8(True)
        self.setCaretWidth(10)
        self.setCaretLineBackgroundColor(QColor("#e6fff0"))
        self.setCaretLineVisible(True)
        self.setAutoIndent(True)
        self.setMatchedBraceBackgroundColor(Qt.yellow)
        self.setIndentationsUseTabs(False)
        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        self.setWrapMode(QsciScintilla.WrapWord)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        sp = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        sp.setHorizontalStretch(1)
        sp.setVerticalStretch(0)
        self.setSizePolicy(sp)

        self.setAutoCompletionThreshold(2)
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        self.setAutoCompletionFillupsEnabled(True)
        self.setLexer(QsciLexerPython(self))
        self.lexer().setFont(qtlib.getfont('fontcomment').font())
        self.apis = QsciAPIs(self.lexer())

    def addCompletions(self, *lists):
        for list in lists:
            for x, y in list:
                self.apis.add(x)
        self.apis.prepare()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            event.ignore()
            return
        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            if not self.isListActive():
                event.ignore()
                self.returnPressed.emit()
                return
        super(RevsetEntry, self).keyPressEvent(event)

    def sizeHint(self):
        return QSize(10, self.fontMetrics().height())
예제 #3
0
class RevsetEntry(QsciScintilla):

    returnPressed = pyqtSignal()

    def __init__(self, parent=None):
        super(RevsetEntry, self).__init__(parent)
        self.setMarginWidth(1, 0)
        self.setReadOnly(False)
        self.setUtf8(True)
        self.setCaretWidth(10)
        self.setCaretLineBackgroundColor(QColor("#e6fff0"))
        self.setCaretLineVisible(True)
        self.setAutoIndent(True)
        self.setMatchedBraceBackgroundColor(Qt.yellow)
        self.setIndentationsUseTabs(False)
        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        self.setWrapMode(QsciScintilla.WrapWord)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        sp = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        sp.setHorizontalStretch(1)
        sp.setVerticalStretch(0)
        self.setSizePolicy(sp)

        self.setAutoCompletionThreshold(2)
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        self.setAutoCompletionFillupsEnabled(True)
        self.setLexer(QsciLexerPython(self))
        self.lexer().setFont(qtlib.getfont('fontcomment').font())
        self.apis = QsciAPIs(self.lexer())

    def addCompletions(self, *lists):
        for list in lists:
            for x, y in list:
                self.apis.add(x)
        self.apis.prepare()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            event.ignore()
            return
        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            if not self.isListActive():
                event.ignore()
                self.returnPressed.emit()
                return
        super(RevsetEntry, self).keyPressEvent(event)

    def sizeHint(self):
        return QSize(10, self.fontMetrics().height())
예제 #4
0
파일: Editors.py 프로젝트: nmichaud/fiddle
class CSSEditor(BaseEditor):
    def __init__(self, parent=None, line_num_margin=3, autocomplete_list=None):
        super(CSSEditor, self).__init__(parent, line_num_margin, autocomplete_list)

        # Set HTML lexer
        self.lexer = QsciLexerCSS(self)
        self.lexer.setDefaultFont(self.editor_font)
        # Set auto-completion
        self.api = QsciAPIs(self.lexer)
        if autocomplete_list is not None:
            # Add additional completion strings
            for i in autocomplete_list:
                self.api.add(i)
        self.api.prepare()
        self.setAutoCompletionThreshold(3)
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        self.setLexer(self.lexer)
예제 #5
0
파일: Editors.py 프로젝트: akehrer/fiddle
class HTMLEditor(BaseEditor):
    def __init__(self, parent=None, line_num_margin=3, autocomplete_list=None):
        super(HTMLEditor, self).__init__(parent, line_num_margin, autocomplete_list)

        # Set HTML lexer
        self.lexer = QsciLexerHTML(self)
        self.lexer.setDefaultFont(self.editor_font)
        self.lexer.setFont(self.editor_font, QsciLexerHTML.Default)  # Text between tags
        self.lexer.setFont(self.editor_font, QsciLexerHTML.JavaScriptWord)  # Text between script tags
        # Set auto-completion
        self.api = QsciAPIs(self.lexer)
        if autocomplete_list is not None:
            # Add additional completion strings
            for i in autocomplete_list:
                self.api.add(i)
        self.api.prepare()
        self.setAutoCompletionThreshold(3)
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        self.setAutoCompletionUseSingle(QsciScintilla.AcusExplicit)
        self.setLexer(self.lexer)
  def initCompleter(self):
    dictionary = None
    if self.db:
        dictionary = self.db.connector.getSqlDictionary()
    if not dictionary:
        # use the generic sql dictionary
        from .sql_dictionary import getSqlDictionary
        dictionary = getSqlDictionary()

    wordlist = []
    for name, value in dictionary.iteritems():
        wordlist += value   # concat lists
    wordlist = list(set(wordlist))  # remove duplicates

    api = QsciAPIs(self.editSql.lexer())
    for word in wordlist:
        api.add(word)

    api.prepare()
    self.editSql.lexer().setAPIs(api)
예제 #7
0
파일: Editors.py 프로젝트: akehrer/fiddle
class PythonEditor(BaseEditor):
    def __init__(self, parent=None, line_num_margin=3, autocomplete_list=None):
        super(PythonEditor, self).__init__(parent, line_num_margin, autocomplete_list)

        # Set Python lexer
        self.lexer = QsciLexerPython(self)
        self.lexer.setDefaultFont(self.editor_font)
        self.lexer.setFont(self.editor_font, QsciLexerPython.Comment)
        # Indentation warning ("The indentation is inconsistent when compared to the previous line")
        self.lexer.setIndentationWarning(QsciLexerPython.Inconsistent)
        # Set auto-completion
        self.api = QsciAPIs(self.lexer)
        if autocomplete_list is not None:
            # Add additional completion strings
            for i in autocomplete_list:
                self.api.add(i)
        self.api.prepare()
        self.setAutoCompletionThreshold(3)
        self.setAutoCompletionSource(QsciScintilla.AcsAll)
        self.setAutoCompletionUseSingle(QsciScintilla.AcusExplicit)
        self.setLexer(self.lexer)

        # PEP8 tabs
        self.setIndentationsUseTabs(False)
        self.setAutoIndent(True)
        self.setIndentationGuides(True)
        
        # PEP8 edge column line
        self.edgecol = 80

        # Linters
        self.linter = 'internal'

    def clean_code(self):
        self.setText(autopep8.fix_code(self.text(), options={'aggressive': 2}))

    def check_code(self, path):
        self._clear_all_margin_markers()
        self.lint_data = {}
        try:
            warnings = linter.check(self.text(), os.path.basename(path))
            for w in warnings:
                w.type = 'warning'
                key = w.lineno - 1
                if key in self.lint_data.keys():
                    self.lint_data[key].append(w)
                else:
                    self.lint_data[key] = [w]
                self._add_warn_margin_marker(w.lineno - 1)
        except linter.LinterSyntaxError as e:
            e.type = 'error'
            self.lint_data[e.lineno - 1] = e
            self._add_error_margin_marker(e.lineno - 1)
        except linter.LinterUnexpectedError as e:
            print(e)

    def margin_clicked(self, marnum, linenum, modifiers):
        if self._margin_popup.isVisible():
            self._hide_margin_popup()
        else:
            try:
                warnings = self.lint_data[linenum]
                desc = ''
                if isinstance(warnings, linter.LinterError):
                    desc = warnings.message
                    self._show_margin_popup(desc.strip(), bg_color=self.colorErrorBackground)
                else:
                    for warning in warnings:
                        desc += warning.message % warning.message_args + '\n'
                    self._show_margin_popup(desc.strip(), bg_color=self.colorWarnBackground)
            except KeyError:
                pass
        return True
예제 #8
0
class CppEditor(QsciScintilla):
    '''
    classdocs
    '''
    def __init__(self, parent=None, fileName=None, readOnlyFiles=[]):
        '''
        Constructor
        '''
        super(CppEditor, self).__init__(parent)
        self.parent = parent
        self.roFiles = readOnlyFiles

        self.setAcceptDrops(False)  # drag&drop is on its parent

        # Set the default font
        font = QtGui.QFont()
        font.setFamily('Courier')
        font.setFixedPitch(True)
        font.setPointSize(10)
        self.setFont(font)
        self.setMarginsFont(font)

        # C/C++ lexer
        self.lexer = QsciLexerCPP(self, True)
        self.lexer.setDefaultFont(font)
        self.libraryAPIs = QsciAPIs(QsciLexerCPP(self, True))
        self.setLexer(self.lexer)
        self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, 'Courier')

        # Auto-indent
        self.setTabWidth(4)
        #self.setIndentationsUseTabs(False)
        self.setAutoIndent(True)

        # Current line visible with special background color
        self.setCaretLineVisible(True)
        self.setCaretLineBackgroundColor(QtGui.QColor("#ffe4e4"))

        # Enable brace matching
        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        # Enable folding visual- use boxes
        self.setFolding(QsciScintilla.BoxedTreeFoldStyle)

        # show line numbers
        fontmetrics = QtGui.QFontMetrics(font)
        self.setMarginsFont(font)
        self.setMarginWidth(0, fontmetrics.width("00000") + 4)
        self.setMarginLineNumbers(0, True)
        self.setMarginsBackgroundColor(QtGui.QColor("#ccccee"))

        # not too small
        self.setMinimumSize(400, 200)

        # set the length of the string before the editor tries to auto-complete
        self.setAutoCompletionThreshold(3)
        # tell the editor we are using a QsciAPI for the auto-completion
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        # removed remaining right side characters from the current cursor
        self.setAutoCompletionReplaceWord(True)

        # "CTRL+Space" autocomplete
        self.shortcut_ctrl_space = QtGui.QShortcut(
            QtGui.QKeySequence("Ctrl+Space"), self)
        self.connect(self.shortcut_ctrl_space, QtCore.SIGNAL('activated()'),
                     self.autoCompleteFromAll)

        if fileName:
            self.curFile = fileName
            self.loadFile(fileName)
            self.isUntitled = False
        else:
            self.curFile = PROJECT_NONAME + USER_CODE_EXT
            self.setText(__default_content__)
            self.isUntitled = True

        self.updateApiKeywords()
        self.isModified = False
        self.connect(self, QtCore.SIGNAL('textChanged()'), self.onTextChanged)

    def onTextChanged(self):
        self.isModified = True
        self.parent.onChildContentChanged()

    def loadFile(self, fileName):
        qfile = QtCore.QFile(fileName)
        if not qfile.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(
                self, PROJECT_ALIAS,
                "Cannot read file %s:\n%s." % (fileName, qfile.errorString()))
            return False

        ret = True
        try:
            # workaround for OS X QFile.readAll()
            f = open(qfile.fileName(), 'r')
            self.clear()
            for line in f.readlines():
                self.append(line)
        except:
            QtGui.QMessageBox.warning(self, PROJECT_ALIAS,
                                      "failed to read %s." % fileName)
            ret = False
        finally:
            f.close()

        qfile.close()
        return ret

    def saveFile(self, fileName):
        if str(fileName).find(' ') >= 0:
            QtGui.QMessageBox.warning(
                self, PROJECT_ALIAS,
                'File path "%s" contains space(s). Please save to a valid location.'
                % fileName)
            return None
        qfile = QtCore.QFile(fileName)
        if not qfile.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(
                self, PROJECT_ALIAS,
                "Cannot write file %s:\n%s." % (fileName, qfile.errorString()))
            return None

        try:
            qfile.writeData(self.text())
        except:
            QtGui.QMessageBox.warning(self, PROJECT_ALIAS,
                                      "Failed to save %s." % fileName)
            qfile.close()
            return None

        qfile.close()
        self.curFile = fileName
        self.isUntitled = False
        self.isModified = False
        return fileName

    def saveAs(self):
        fileName = QtGui.QFileDialog.getSaveFileName(
            self, "Save As", self.curFile,
            PROJECT_ALIAS + " (*" + USER_CODE_EXT + ");;" +
            "C source (*.c);;C++ source (*.cpp);;Text File (*.txt);;All files (*.*)"
        )
        if not fileName:
            return None
        return self.saveFile(fileName)

    def save(self):
        f1 = os.path.abspath(self.curFile)
        for fname in self.roFiles:
            if f1 == os.path.abspath(fname):  # same file
                if QtGui.QMessageBox.question(self.parent, "Project is read-only",
                        "This project is marked as \"read-only\".\n" + \
                        "Please click \"Cancel\" and save this to another location.\n\n" + \
                        "Continue saving the current project anyway?", "OK", "Cancel"):
                    return None
                #return self.saveAs()
                return self.saveFile(self.curFile)
        if self.isUntitled:
            return self.saveAs()
        else:
            return self.saveFile(self.curFile)

    def currentFile(self):
        return self.curFile

    def modified(self):
        return self.isModified

    def updateApiKeywords(self):
        self.libraryAPIs.clear()
        self.apiKeywords = self.parent.getDefaultKeywords()
        headerfiles = []
        for line in range(self.lines()):
            txt = str(self.text(line)).strip()
            if txt.find('int') == 0:  # e.g. reached "int main()"
                break
            elif txt.find('#include') == 0:
                txt = ''.join(txt.split())
                temp = txt[len('#includes') - 1:]
                header = temp[1:-1]  # get the header file
                hfile = os.path.join('libraries', header[:-2], header)
                if os.path.isfile(hfile):
                    if not (hfile in headerfiles): headerfiles.append(hfile)
        if len(headerfiles):
            #print 'parsing: ', headerfiles
            self.apiKeywords += getLibraryKeywords(headerfiles)
        #self.apiKeywords = list(set(self.apiKeywords)) # remove duplicates
        for keyword in self.apiKeywords:
            self.libraryAPIs.add(keyword)
        self.libraryAPIs.prepare()
        self.lexer.setAPIs(self.libraryAPIs)

    def insertIncludeDirective(self, library=''):
        directive = '#include <' + library + '.h>\r\n'
        insert_pos = 0
        found_inc = False
        for line in range(self.lines()):
            txt = str(self.text(line)).strip()
            if txt.find('int') == 0:  # e.g. reached "int main()"
                insert_pos = line - 1
                break
            elif txt.find('#include') == 0:
                found_inc = True
            elif found_inc:
                insert_pos = line
                break
        if insert_pos < 0 or insert_pos >= self.lines():
            insert_pos = 0
        self.insertAt(directive, insert_pos, 0)
        self.updateApiKeywords()
예제 #9
0
class CommandShell(QsciScintilla):
    def __init__(self, parent=None):
        super(CommandShell, self).__init__(parent)
        self.setMinimumHeight(50)
        self.setMaximumHeight(50)
        self.settings = QSettings()
        self.lex = QsciLexerPython(self)
        self.apis = QsciAPIs(self.lex)
        self.lex.setAPIs(self.apis)
        self.setLexers()
        self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)
        self.SendScintilla(QsciScintilla.SCI_SETVSCROLLBAR, 0)
        self.setFolding(0)
        self._start_prompt = _start_prompt
        self.prompt = self._start_prompt
        self.currentfunction = None
        self.setAutoCompletionSource(self.AcsAPIs)
        self.setAutoCompletionThreshold(1)
        self.setAutoCompletionReplaceWord(True)
        self.setCallTipsStyle(QsciScintilla.CallTipsNoContext)
        self.parent().installEventFilter(self)
        self.textChanged.connect(self.text_changed)
        self._lastcompletions = None

    def text_changed(self):
        if not self.get_data().strip():
            return

        try:
            completions = command.completions_for_line(self.get_data())
            if completions == self._lastcompletions:
                self.autoCompleteFromAPIs()
                return

            self._lastcompletions = completions

            self.apis.cancelPreparation()
            self.apis.clear()
            for value in completions:
                data = u"{}".format(value)
                self.apis.add(data)

            self.apis.prepare()
        except command.NoFunction:
            return

    def end(self):
        self.parent().removeEventFilter(self)
        self.close()

    def eventFilter(self, object, event):
        if event.type() == QEvent.Resize:
            self.adjust_size()
        return QWidget.eventFilter(self, object, event)

    def keyPressEvent(self, e):
        if e.key() in (Qt.Key_Return, Qt.Key_Enter):
            self.entered()
        elif e.key() == Qt.Key_Escape:
            self.close()
        elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete):
            _, newindex = self.getCursorPosition()
            if newindex > len(self.prompt):
                QsciScintilla.keyPressEvent(self, e)
        else:
            QsciScintilla.keyPressEvent(self, e)

    def show_prompt(self, prompt=_start_prompt, data=None):
        self.clear()
        if not prompt == _start_prompt:
            prompt += ":"

        text = prompt

        if data:
            text = prompt + str(data)

        self.setText(text)
        self.prompt = prompt
        self.move_cursor_to_end()

    def get_end_pos(self):
        """Return (line, index) position of the last character"""
        line = self.lines() - 1
        return (line, len(self.text(line)))

    def move_cursor_to_end(self):
        """Move cursor to end of text"""
        line, index = self.get_end_pos()
        self.setCursorPosition(line, index)
        self.ensureCursorVisible()
        self.ensureLineVisible(line)

    def get_data(self):
        line = self.text()
        line = line[len(self.prompt):]
        return line

    def entered(self):
        line = self.get_data()
        if not self.currentfunction:
            try:
                gen = command.parse_line_data(line)
            except command.NoFunction:
                return

            if gen:
                self.currentfunction = gen
                line = None
            else:
                self.currentfunction = None
                self.show_prompt()
                return

        try:
            prompt, data = self.currentfunction.send(line)
            self.show_prompt(prompt, data)
        except StopIteration:
            self.currentfunction = None
            self.show_prompt()

    def setLexers(self):
        loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace")
        fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int)

        font = QFont(loadFont)
        font.setFixedPitch(True)
        font.setPointSize(fontSize)
        font.setStyleHint(QFont.TypeWriter)
        font.setStretch(QFont.SemiCondensed)
        font.setLetterSpacing(QFont.PercentageSpacing, 87.0)
        font.setBold(False)

        self.lex.setFont(font)
        self.lex.setDefaultFont(font)
        self.setLexer(self.lex)

    def adjust_size(self):
        fm = QFontMetrics(self.font())
        self.setMaximumHeight(20)
        self.resize(self.parent().width(), 20)
        self.move(0, self.parent().height() - self.height())

    def showEvent(self, event):
        self.adjust_size()
        self.show_prompt()
        self.setFocus()

    def activated(self):
        visible = self.isVisible()
        self.setVisible(not visible)
예제 #10
0
class MultipleCppEditor(QtGui.QTabWidget):
    '''
    classdocs
    '''
    def __init__(self, parent=None):
        '''
        Constructor
        '''
        super(MultipleCppEditor, self).__init__(parent)
        
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        
        self.prepareLibraryAPIs()
        
        self.findDlg = FindDialog(self)
        
        self.setAcceptDrops(True)
        #self.setTabShape(QtGui.QTabWidget.Triangular)
        self.setMovable(True)
        self.setTabsClosable(True)
        
        self.connect(self, QtCore.SIGNAL('tabCloseRequested(int)'), self.closeFile)
        
        self.sampleProjects = []
        try:
            for group in getExampleProjects(scanFirmwareLibs()):
                for fname in group[1]:
                    self.sampleProjects.append(fname)
            # print self.sampleProjects
        except:
            pass
        
        if self.count()==0:
            self.newFile()
        
    def newFile(self):
        child = CppEditor(self, None, self.sampleProjects)
        self.addTab(child, PROJECT_NONAME + " * ")
        self.setCurrentIndex(self.count()-1)
        self.setTabToolTip(self.currentIndex(), child.currentFile())
        
    def openFile(self, fileName=None):
        if fileName == None: # prompt open dialog if filename is not specified
            fileName = QtGui.QFileDialog.getOpenFileName(
                                        self, self.tr("Open Source File"),
                                        "", PROJECT_ALIAS + " (*" + PROJECT_EXT + ");;" 
                                        "C Source File (*.c);;Text File (*.txt);;All files (*.*)" )
        if fileName == "":
            return False
        #check if it's already opened
        for i in range(self.count()):
            child = self.widget(i)
            if fileName == child.currentFile(): # file already opened
                self.setCurrentIndex(i)
                return True
        child = CppEditor(self, fileName, self.sampleProjects)
        tabtext = os.path.basename( str(fileName) )
        if tabtext.lower().find(PROJECT_EXT) == len(tabtext) - len(PROJECT_EXT):
            tabtext = tabtext[:tabtext.lower().find(PROJECT_EXT)]
        self.addTab(child, tabtext)
        self.setCurrentIndex(self.count()-1)
        self.setTabToolTip(self.currentIndex(), child.currentFile())
        return True
    
    def saveFile(self):
        child = self.currentWidget()
        if child == None:
            return None
        rc = child.save()
        if rc:
            fileName = child.currentFile()
            tabtext = os.path.basename( str(fileName) )
            if tabtext.lower().find(PROJECT_EXT) == len(tabtext) - len(PROJECT_EXT):
                tabtext = tabtext[:tabtext.lower().find(PROJECT_EXT)]
            self.setTabText(self.currentIndex(), tabtext)
            self.setTabToolTip(self.currentIndex(), fileName)
            return True
        return False
    
    def saveFileAs(self):
        child = self.currentWidget()
        rc = child.saveAs()
        if rc:
            fileName = child.currentFile()
            tabtext = os.path.basename( str(fileName) )
            if tabtext.lower().find(PROJECT_EXT) == len(tabtext) - len(PROJECT_EXT):
                tabtext = tabtext[:tabtext.lower().find(PROJECT_EXT)]
            self.setTabText(self.currentIndex(), tabtext)
            self.setTabToolTip(self.currentIndex(), fileName)
            return True
        return False
    
    def closeFile(self, idx = 0):
        if self.count()==0:
            return True# nothing to close
        # check if the file has changed before closing
        child = self.widget(idx)
        if child.isModified:
            result = QtGui.QMessageBox.question(self, "Modified",
                         'Save changes on "' + child.currentFile() + '" ?',
                         QtGui.QMessageBox.Yes, QtGui.QMessageBox.No, QtGui.QMessageBox.Cancel)
            if result == QtGui.QMessageBox.Cancel:
                return False
            elif result == QtGui.QMessageBox.Yes:
                if child.save() == None: # file was not save after
                    return False
        self.removeTab(idx)
        child.setParent(None)
        child.close()
        return True
    
    def closeCurrentFile(self):
        return self.closeFile(self.currentIndex())
        
    def getCurrentFile(self):
        child = self.currentWidget()
        if child:
            return child.currentFile()
        return None
    
    def isCurrentFileModified(self):
        child = self.currentWidget()
        if child:
            return child.modified()
        return False
    
    def editUndo(self):
        child = self.currentWidget()
        if child: child.undo()
            
    def editRedo(self):
        child = self.currentWidget()
        if child: child.redo()
    
    def editCut(self):
        child = self.currentWidget()
        if child:
            if child.hasSelectedText(): child.cut()
            
    def editCopy(self):
        child = self.currentWidget()
        if child: child.copy()
            
    def editPaste(self):
        child = self.currentWidget()
        if child: child.paste()
        
    def editSelectAll(self):
        child = self.currentWidget()
        if child: child.selectAll()
        
    def editClear(self):
        child = self.currentWidget()
        if child: child.clear()
    
    def showFindDialog(self):
        child = self.currentWidget()
        if child:
            if child.hasSelectedText():
                self.findDlg.setFindText(child.selectedText ())
        self.findDlg.show()
        
    def findChildText(self, text=None, forwardDirection=True, caseSensitive=False,
                      wrapSearch=True, wholeWord=False, regExp=False):
        if text:
            child = self.currentWidget()
            if child:
                #print "fw = ", forwardDirection, " cs = ", caseSensitive, " wrap = ", wrapSearch
                result = child.findFirst( text,
                            regExp, # regular expression
                            caseSensitive, # case sensitive
                            wholeWord, # whole word matches only
                            wrapSearch,  # wraps around
                            forwardDirection) # forward
                return result
        return False
    
    def replaceChildText(self, newText=None):
        child = self.currentWidget()
        if child and child.hasSelectedText():
            child.replaceSelectedText(newText) # returns None
            return True                
        return False
    
    def replaceFindChildText(self, oldText=None, newText=None, forwardDirection=True,
                             caseSensitive=False, wrapSearch=True, wholeWord=False, regExp=False):
        if oldText:
            child = self.currentWidget()
            if child:
                child.replace(newText)
                result = child.findFirst( oldText,
                            regExp, # regular expression
                            caseSensitive, # case sensitive
                            wholeWord, # whole word matches only
                            wrapSearch,  # wraps around
                            forwardDirection) # forward
                return result               
        return False
    
    def replaceAllChildText(self, oldText=None, newText=None, caseSensitive=False,
                            wholeWord=False, regExp=False):
        if oldText:
            if (not caseSensitive) and (oldText.lower() == newText.lower()):
                print "nothing to replace"
                return False
            child = self.currentWidget()
            if child:
                result = child.findFirst( oldText,
                            regExp, # regular expression
                            caseSensitive, # case sensitive
                            wholeWord, # whole word matches only
                            True,  # wraps around
                            True) # forward
                if result:
                    cnt = 0
                    while cnt < 4096: # limit loop
                        child.replace(newText)
                        cnt += 1
                        if not child.findNext(): break
                    return True      
        return False
                
    def onChildContentChanged(self):
        title = self.tabText(self.currentIndex())
        if not title.contains('*', QtCore.Qt.CaseInsensitive):
            self.setTabText(self.currentIndex(), title + " * ")
        
    def prepareLibraryAPIs(self):
        self.LibraryAPIs = QsciAPIs(QsciLexerCPP(self,True))
        keywords = getLibraryKeywords()
        for keyword in keywords:
            self.LibraryAPIs.add( keyword )
        self.LibraryAPIs.prepare()
        
    def getLibraryAPIs(self):
        return self.LibraryAPIs
    
    def importFirmwareLib(self, library=None):        
        child = self.currentWidget()
        if child:
            directive =  '#include <' + library + '.h>\r\n'
            child.insertAt(directive, 0, 0) # insert at first line
    
    def dragEnterEvent(self, e):
        if e.mimeData().hasUrls():
            url = str( e.mimeData().urls()[0].toString() ).lower()
            if url.rfind(PROJECT_EXT) == len(url) - len(PROJECT_EXT):
                e.accept()
                return
        e.ignore() 
        
    def dropEvent(self, e):
        try:
            fname = str(e.mimeData().urls()[0].toLocalFile() )
            self.openFile(fname)
        except:
            print "drop error"
    
    def closeAllTabs(self):
        for index in range(self.count()):
            self.setCurrentIndex(index)
            if not self.closeFile():
                return False
        return True

        
예제 #11
0
class CppEditor(QsciScintilla):
    '''
    classdocs
    '''
    def __init__(self, parent=None, fileName=None, readOnlyFiles=[]):
        '''
        Constructor
        '''
        super(CppEditor, self).__init__(parent)
        self.parent = parent
        self.roFiles = readOnlyFiles

        self.setAcceptDrops(False) # drag&drop is on its parent

        # Set the default font
        font = QtGui.QFont()
        font.setFamily('Courier')
        font.setFixedPitch(True)
        font.setPointSize(10)
        self.setFont(font)
        self.setMarginsFont(font)

        # C/C++ lexer
        self.lexer = QsciLexerCPP(self,  True)
        self.lexer.setDefaultFont(font)
        self.libraryAPIs = QsciAPIs(QsciLexerCPP(self,True))
        self.setLexer(self.lexer)
        self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, 'Courier')

        # Auto-indent
        self.setTabWidth(4)
        #self.setIndentationsUseTabs(False)
        self.setAutoIndent(True)

        # Current line visible with special background color
        self.setCaretLineVisible(True)
        self.setCaretLineBackgroundColor(QtGui.QColor("#ffe4e4"))

        # Enable brace matching
        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        # Enable folding visual- use boxes
        self.setFolding(QsciScintilla.BoxedTreeFoldStyle)

        # show line numbers
        fontmetrics = QtGui.QFontMetrics(font)
        self.setMarginsFont(font)
        self.setMarginWidth(0, fontmetrics.width("00000") + 4)
        self.setMarginLineNumbers(0, True)
        self.setMarginsBackgroundColor(QtGui.QColor("#ccccee"))

        # not too small
        self.setMinimumSize(400, 200)

        # set the length of the string before the editor tries to auto-complete
        self.setAutoCompletionThreshold(3)
        # tell the editor we are using a QsciAPI for the auto-completion
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        # removed remaining right side characters from the current cursor
        self.setAutoCompletionReplaceWord(True)

        # "CTRL+Space" autocomplete
        self.shortcut_ctrl_space = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+Space"), self)
        self.connect(self.shortcut_ctrl_space,
            QtCore.SIGNAL('activated()'), self.autoCompleteFromAll)

        if fileName:
            self.curFile = fileName
            self.loadFile(fileName)
            self.isUntitled = False
        else:
            self.curFile = PROJECT_NONAME + USER_CODE_EXT
            self.setText( __default_content__ )
            self.isUntitled = True

        self.updateApiKeywords()
        self.isModified = False
        self.connect(self, QtCore.SIGNAL('textChanged()'), self.onTextChanged )

    def onTextChanged(self):
        self.isModified = True
        self.parent.onChildContentChanged()

    def loadFile(self, fileName):
        qfile = QtCore.QFile(fileName)
        if not qfile.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(self, PROJECT_ALIAS,
                    "Cannot read file %s:\n%s." % (fileName, qfile.errorString()))
            return False

        ret = True
        try:
            # workaround for OS X QFile.readAll()
            f = open(qfile.fileName(), 'r')
            self.clear()
            for line in f.readlines():
                self.append(line)
        except:
            QtGui.QMessageBox.warning(self, PROJECT_ALIAS,
                    "failed to read %s." % fileName )
            ret = False
        finally:
            f.close()

        qfile.close()
        return ret

    def saveFile(self, fileName):
        if str(fileName).find(' ')>=0:
            QtGui.QMessageBox.warning(self, PROJECT_ALIAS,
                    'File path "%s" contains space(s). Please save to a valid location.'%fileName)
            return None
        qfile = QtCore.QFile(fileName)
        if not qfile.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(self, PROJECT_ALIAS,
                    "Cannot write file %s:\n%s." % (fileName, qfile.errorString()))
            return None

        try:
            qfile.writeData(self.text())
        except:
            QtGui.QMessageBox.warning(self, PROJECT_ALIAS,
                    "Failed to save %s." % fileName )
            qfile.close()
            return None

        qfile.close()
        self.curFile = fileName
        self.isUntitled = False
        self.isModified = False
        return fileName

    def saveAs(self):
        fileName = QtGui.QFileDialog.getSaveFileName(self, "Save As",
                self.curFile, PROJECT_ALIAS + " (*" + USER_CODE_EXT + ");;" +
                    "C source (*.c);;C++ source (*.cpp);;Text File (*.txt);;All files (*.*)" )
        if not fileName:
            return None
        return self.saveFile(fileName)

    def save(self):
        f1 = os.path.abspath(self.curFile)
        for fname in self.roFiles:
            if f1 == os.path.abspath(fname): # same file
                if QtGui.QMessageBox.question(self.parent, "Project is read-only",
                        "This project is marked as \"read-only\".\n" + \
                        "Please click \"Cancel\" and save this to another location.\n\n" + \
                        "Continue saving the current project anyway?", "OK", "Cancel"):
                    return None
                #return self.saveAs()
                return self.saveFile(self.curFile)
        if self.isUntitled:
            return self.saveAs()
        else:
            return self.saveFile(self.curFile)

    def currentFile(self):
        return self.curFile

    def modified(self):
        return self.isModified

    def updateApiKeywords(self):
        self.libraryAPIs.clear()
        self.apiKeywords = self.parent.getDefaultKeywords()
        headerfiles = []
        for line in range(self.lines()):
            txt = str(self.text(line)).strip()
            if txt.find('int') == 0: # e.g. reached "int main()"
                break
            elif txt.find('#include') == 0:
                txt = ''.join(txt.split())
                temp = txt[len('#includes')-1 : ]
                header = temp[1:-1] # get the header file
                hfile = os.path.join('libraries', header[:-2], header)
                if os.path.isfile(hfile):
                    if not (hfile in headerfiles): headerfiles.append( hfile )
        if len( headerfiles ):
            #print 'parsing: ', headerfiles
            self.apiKeywords += getLibraryKeywords( headerfiles )
        #self.apiKeywords = list(set(self.apiKeywords)) # remove duplicates
        for keyword in self.apiKeywords:
            self.libraryAPIs.add( keyword )
        self.libraryAPIs.prepare()
        self.lexer.setAPIs(self.libraryAPIs)

    def insertIncludeDirective(self, library=''):
        directive =  '#include <' + library + '.h>\r\n'
        insert_pos = 0
        found_inc = False
        for line in range(self.lines()):
            txt = str(self.text(line)).strip()
            if txt.find('int') == 0: # e.g. reached "int main()"
                insert_pos = line - 1
                break
            elif txt.find('#include') == 0:
                found_inc = True
            elif found_inc:
                insert_pos = line
                break
        if insert_pos < 0 or insert_pos >= self.lines():
            insert_pos = 0
        self.insertAt(directive, insert_pos, 0)
        self.updateApiKeywords()
예제 #12
0
파일: editor.py 프로젝트: Garjy/edis
class Editor(base.Base):

    # Braces
    BRACE = {
        '(': ')',
        '[': ']'
        }

    # Quotes
    QUOTE = {
        "'": "'",
        '"': '"'
        }

    # Marcadores
    MARKER_MODIFIED = 3
    MARKER_SAVED = 4

    # Indicadores
    WORD_INDICATOR = 0
    WARNING_INDICATOR = 1

    def __init__(self, obj_file):
        super(Editor, self).__init__()
        self.obj_file = obj_file  # Asociación con el objeto EdisFile
        self._font = None
        # Configuración
        use_tabs = settings.get_setting('editor/usetabs')
        self.setIndentationsUseTabs(use_tabs)
        self.setAutoIndent(settings.get_setting('editor/indent'))
        self.setBackspaceUnindents(True)
        # Quita el scrollbar
        self.send("sci_sethscrollbar", 0)
        # Configuración de indicadores
        self.send("sci_indicsetstyle", Editor.WORD_INDICATOR, "indic_box")
        self.send("sci_indicsetfore", Editor.WORD_INDICATOR, QColor("#FF0000"))
        self.send("sci_indicsetstyle",
                  Editor.WARNING_INDICATOR, "indic_squiggle")
        self.send("sci_indicsetfore",
                  Editor.WARNING_INDICATOR, QColor("#0000FF"))
        # Scheme
        self.scheme = editor_scheme.get_scheme(
            settings.get_setting('editor/scheme'))
        # Folding
        self.setFolding(QsciScintilla.PlainFoldStyle)  # en márgen 2
        self.setMarginWidth(3, 5)  # 5px de espacios en márgen 3
        self.send("sci_markersetfore",
            QsciScintilla.SC_MARKNUM_FOLDER,
            QColor(self.scheme['FoldMarkerFore']))
        self.send("sci_markersetback",
            QsciScintilla.SC_MARKNUM_FOLDER,
            QColor(self.scheme['FoldMarkerBack']))
        self.setFoldMarginColors(QColor(self.scheme['FoldMarginBack']),
                                 QColor(self.scheme['FoldMarginFore']))
        # Marcadores
        self.markerDefine(QsciScintilla.SC_MARK_LEFTRECT,
                          Editor.MARKER_MODIFIED)
        self.setMarkerBackgroundColor(
            QColor(226, 255, 141), Editor.MARKER_MODIFIED)
        self.markerDefine(QsciScintilla.SC_MARK_LEFTRECT, Editor.MARKER_SAVED)
        self.setMarkerBackgroundColor(QColor(55, 142, 103),
                                      Editor.MARKER_SAVED)

        # Actualiza flags (espacios en blanco, cursor, sidebar, etc)
        self.update_options()
        # Lexer
        self._lexer = lexer.Lexer()
        self.setLexer(self._lexer)
        # Autocompletado
        self.api = None
        if settings.get_setting('editor/completion'):
            self.active_code_completion()
        # Indentación
        self._indentation = settings.get_setting('editor/width-indent')
        self.send("sci_settabwidth", self._indentation)
        # Minimapa
        self.minimap = None
        if settings.get_setting('editor/minimap'):
            self.minimap = minimap.Minimap(self)
            self.connect(self, SIGNAL("textChanged()"),
                         self.minimap.update_code)
        # Thread que busca palabras
        self.search_thread = editor_helpers.SearchThread()
        self.connect(self.search_thread,
                     SIGNAL("foundWords(PyQt_PyObject)"), self.mark_words)
        # Analizador de estilo de código
        self.checker = None
        if settings.get_setting('editor/style-checker'):
            self.load_checker()
        # Fuente
        font = settings.get_setting('editor/font')
        font_size = settings.get_setting('editor/size-font')
        self.load_font(font, font_size)
        self.setMarginsBackgroundColor(QColor(self.scheme['SidebarBack']))
        self.setMarginsForegroundColor(QColor(self.scheme['SidebarFore']))
        # Línea actual
        self.send("sci_setcaretlinevisible",
                  settings.get_setting('editor/show-caret-line'))
        self.send("sci_setcaretlineback", QColor(self.scheme['CaretLineBack']))
        self.send("sci_setcaretfore", QColor(self.scheme['CaretLineFore']))
        self.send("sci_setcaretlinebackalpha", self.scheme['CaretLineAlpha'])
        # Cursor
        caret_period = settings.get_setting('editor/cursor-period')
        self.send("sci_setcaretperiod", caret_period)
        # Márgen
        if settings.get_setting('editor/show-margin'):
            self.update_margin()
        # Brace matching
        self.setBraceMatching(int(settings.get_setting('editor/match-brace')))
        self.setMatchedBraceBackgroundColor(QColor(
            self.scheme['MatchedBraceBack']))
        self.setMatchedBraceForegroundColor(QColor(
            self.scheme['MatchedBraceFore']))
        self.setUnmatchedBraceBackgroundColor(QColor(
            self.scheme['UnmatchedBraceBack']))
        self.setUnmatchedBraceForegroundColor(QColor(
            self.scheme['UnmatchedBraceFore']))
        # Selecciones Múltiples
        self.send("sci_setadditionalcaretfore", QColor(157, 64, 40))
        self.send("sci_setadditionalcaretsblink", 1)
        self.send("sci_setadditionalselalpha", 100)
        self.send("sci_setmultipleselection", 1)
        self.send("sci_setadditionalselectiontyping", 1)
        # Conexiones
        self.connect(self, SIGNAL("linesChanged()"), self.update_sidebar)
        self.connect(self, SIGNAL("textChanged()"), self._add_marker_modified)

    @property
    def filename(self):
        return self.obj_file.filename

    @property
    def is_modified(self):
        return self.isModified()

    def load_font(self, fuente, tam):
        self._font = QFont(fuente, tam)
        if self._lexer is None:
            self.setFont(self._font)
        else:
            self._lexer.setFont(self._font)
        self.setMarginsFont(self._font)

    def load_checker(self, activated=True):
        if activated and self.checker is not None:
            return
        if not activated:
            self.checker = None
            self.clear_indicators(Editor.WARNING_INDICATOR)
        else:
            self.checker = checker.Checker(self)
            self.connect(self.checker, SIGNAL("finished()"),
                         self._show_violations)

    def set_brace_matching(self):
        match_brace = int(settings.get_setting('editor/match-brace'))
        self.setBraceMatching(match_brace)

    def update_options(self):
        """ Actualiza las opciones del editor """

        if settings.get_setting('editor/show-tabs-spaces'):
            self.setWhitespaceVisibility(self.WsVisible)
        else:
            self.setWhitespaceVisibility(self.WsInvisible)
        self.setIndentationGuides(settings.get_setting('editor/show-guides'))
        if settings.get_setting('editor/wrap-mode'):
            self.setWrapMode(self.WrapWord)
        else:
            self.setWrapMode(self.WrapNone)
        self.send("sci_setcaretstyle", settings.get_setting('editor/cursor'))
        self.setCaretWidth(settings.get_setting('editor/caret-width'))
        self.setAutoIndent(settings.get_setting('editor/indent'))
        self.send("sci_setcaretperiod",
                  settings.get_setting('editor/cursor-period'))
        current_line = settings.get_setting('editor/show-caret-line')
        self.send("sci_setcaretlinevisible", current_line)
        self.setEolVisibility(settings.get_setting('editor/eof'))

    def update_sidebar(self):
        """ Ajusta el ancho del sidebar """

        fmetrics = QFontMetrics(self._font)
        lines = str(self.lines()) + '0'
        line_number = settings.get_setting('editor/show-line-number')
        width = fmetrics.width(lines) if line_number else 0
        self.setMarginWidth(0, width)

    def show_line_numbers(self):
        line_number = settings.get_setting('editor/show-line-number')
        self.setMarginLineNumbers(0, line_number)
        self.update_sidebar()

    def update_margin(self):
        """ Actualiza el ancho del márgen de línea """

        if settings.get_setting('editor/show-margin'):
            self.setEdgeMode(QsciScintilla.EdgeLine)
            ancho = settings.get_setting('editor/width-margin')
            self.setEdgeColumn(ancho)
            self.setEdgeColor(QColor(self.scheme['Margin']))
        else:
            self.setEdgeMode(QsciScintilla.EdgeNone)

    def update_indentation(self):
        ancho = settings.get_setting('editor/width-indent')
        self.send("sci_settabwidth", ancho)
        self._indentation = ancho

    def mark_words(self, palabras):
        self.clear_indicators(Editor.WORD_INDICATOR)
        for p in palabras:
            self.fillIndicatorRange(p[0], p[1], p[0], p[2],
                                    Editor.WORD_INDICATOR)

    def active_code_completion(self, enabled=True):
        if self.api is not None and enabled:
            return
        if enabled:
            self.api = QsciAPIs(self._lexer)
            if settings.get_setting('editor/completion-keywords'):
                for keyword in keywords.keywords:
                    self.api.add(keyword)
                self.api.prepare()
                source = QsciScintilla.AcsAPIs
                if settings.get_setting('editor/completion-document'):
                    source = QsciScintilla.AcsAll
            elif settings.get_setting('editor/completion-document'):
                source = QsciScintilla.AcsDocument
            else:
                source = QsciScintilla.AcsNone
            threshold = settings.get_setting('editor/completion-threshold')
            self.setAutoCompletionThreshold(threshold)
            self.setAutoCompletionSource(source)
            cs = settings.get_setting('editor/completion-cs')
            self.setAutoCompletionCaseSensitivity(cs)
            repl_word = settings.get_setting('editor/completion-replace-word')
            self.setAutoCompletionReplaceWord(repl_word)
            show_single = settings.get_setting('editor/completion-single')
            use_single = 2 if show_single else 0
            self.setAutoCompletionUseSingle(use_single)
        else:
            self.api = None
            self.setAutoCompletionSource(0)

    def _add_marker_modified(self):
        """ Agrega el marcador cuando el texto cambia """

        if not settings.get_setting('editor/mark-change'):
            return
        nline, _ = self.getCursorPosition()
        if self.markersAtLine(nline):
            self.markerDelete(nline)
        self.markerAdd(nline, Editor.MARKER_MODIFIED)

    def _show_violations(self):
        data = self.checker.data
        self.clear_indicators(Editor.WARNING_INDICATOR)
        for line, message in list(data.items()):
            line = int(line) - 1
            self.fillIndicatorRange(line, 0, line, self.lineLength(line),
                                    Editor.WARNING_INDICATOR)

    def _text_under_cursor(self):
        """ Texto seleccionado con el cursor """

        line, index = self.getCursorPosition()  # Posición del cursor
        word = self.wordAtLineIndex(line, index)  # Palabra en esa pos
        return word

    def mouseReleaseEvent(self, e):
        super(Editor, self).mouseReleaseEvent(e)
        if e.button() == Qt.LeftButton:
            word = self._text_under_cursor()
            if not word:
                self.clear_indicators(Editor.WORD_INDICATOR)
                return
            self.search_thread.find(word, self.text())

    def mouseMoveEvent(self, event):
        super(Editor, self).mouseMoveEvent(event)
        if self.checker is None:
            return
        position = event.pos()
        line = str(self.lineAt(position) + 1)
        message = self.checker.data.get(line, None)
        if message is not None:
            QToolTip.showText(self.mapToGlobal(position), message)

    def keyReleaseEvent(self, event):
        super(Editor, self).keyReleaseEvent(event)
        line, _ = self.getCursorPosition()
        self.emit(SIGNAL("linesChanged(int)"), line)

    def keyPressEvent(self, event):
        super(Editor, self).keyPressEvent(event)
        key = event.key()
        # Borra indicador de palabra
        if key == Qt.Key_Escape:
            self.clear_indicators(Editor.WORD_INDICATOR)
            return
        # Completado de comillas
        if key == Qt.Key_Apostrophe:
            if settings.get_setting('editor/complete-single-quote'):
                self._complete_quote(event)
            return
        if key == Qt.Key_QuoteDbl:
            if settings.get_setting('editor/complete-double-quote'):
                self._complete_quote(event)
            return
        # Completado de llaves
        if key == Qt.Key_BraceLeft:
            if settings.get_setting('editor/complete-brace'):
                self._complete_key(event)
        if key in (Qt.Key_BracketLeft,
                   Qt.Key_ParenLeft,
                   Qt.Key_BracketRight,
                   Qt.Key_ParenRight):
            self._complete_brace(event)

    def _complete_quote(self, event):
        """ Autocompleta comillas simples y dobles, si existe el complementario
            el cursor se mueve un espacio.
        """

        complementary = Editor.QUOTE.get(event.text())
        line, index = self.getCursorPosition()
        text_line = self.text(line)
        portion = text_line[index - 1:index + 1]
        if portion in settings.QUOTES:
            self.setSelection(line, index, line, index + 1)
            self.replaceSelectedText('')
        else:
            self.insertAt(complementary, line, index)

    def _complete_brace(self, event):
        """ Autocompleta un brace cuando es abierto '(, ['.
            Si existe el complementario '), ]' el índice del cursor se
            mueve un espacio.
        """

        brace_close = settings.BRACES.get(event.text(), None)
        braces_open = list(Editor.BRACE.keys())
        line, index = self.getCursorPosition()
        if event.text() in settings.BRACES.values():
            text_line = self.text(line)
            found = 0
            # Busco un brace cerrado en el texto
            for token in text_line:
                if token in settings.BRACES.values():
                    found += 1
            try:
                # Brace abierto
                brace_open = text_line[index]
            except:
                brace_open = ''
            if found > 1 and brace_open not in braces_open:
                # Reemplazo el brace sobrante por un string vacío
                self.setSelection(line, index, line, index + 1)
                self.replaceSelectedText('')
            return
        elif event.text() in settings.BRACES.keys() and brace_close is not None:
            # Inserto el brace complementario
            self.insertAt(brace_close, line, index)

    def _complete_key(self, event):
        """ Autocompleta llaves en funciones y estructuras.
            Si se usa typedef en la definición de la estructura se completa
            la variable.
         """

        line, index = self.getCursorPosition()
        text_line = self.text(line)
        # Estructura
        if REGEX_STRUCT.match(text_line):
            if text_line.split()[0] == 'typedef':
                # Obtengo la variable
                split_line = text_line.split()
                if split_line[-1] == '{':
                    typedef = split_line[-2]
                else:
                    typedef = split_line[-1][:-1]
                text = "}%s;" % typedef.title()
            else:
                text = "};"
            self.insert("\n")
            self.insertAt(text, line + 1, 0)
            return
        # Función
        portion = text_line[index - 3: index]
        if ')' in portion:
            self.insert("\n")
            current_indentation = self.indentation(line)
            self.setIndentation(line + 1, current_indentation)
            self.insertAt('}', line + 1, current_indentation)

    def resizeEvent(self, e):
        super(Editor, self).resizeEvent(e)
        if self.minimap is not None:
            self.minimap.update_geometry()

    def comment(self):
        # FIXME: tener en cuenta /* */
        # FIXME: no funciona si el comentario no inicia en el índice 0
        if self.hasSelectedText():
            line_from, _, line_to, _ = self.getSelection()

            # Iterar todas las líneas seleccionadas
            self.send("sci_beginundoaction")
            for line in range(line_from, line_to + 1):
                self.insertAt('//', line, 0)
            self.send("sci_endundoaction")
        else:
            line, _ = self.getCursorPosition()
            self.insertAt('//', line, 0)

    def uncomment(self):
        if self.hasSelectedText():
            line_from, _, line_to, _ = self.getSelection()
            self.send("sci_beginundoaction")
            for line in range(line_from, line_to + 1):
                self.setSelection(line, 0, line, 2)
                if not self.text(line).startswith('//'):
                    continue
                self.removeSelectedText()
            self.send("sci_endundoaction")
        else:
            line, _ = self.getCursorPosition()
            if not self.text(line).startswith('//'):
                return
            self.setSelection(line, 0, line, 2)
            self.removeSelectedText()

    def to_lowercase(self):
        if self.hasSelectedText():
            text = self.selectedText().lower()
        else:
            line, _ = self.getCursorPosition()
            text = self.text(line).lower()
            self.setSelection(line, 0, line, len(text))
        self.replaceSelectedText(text)

    def to_uppercase(self):
        if self.hasSelectedText():
            text = self.selectedText().upper()
        else:
            line, _ = self.getCursorPosition()
            text = self.text(line).upper()
            self.setSelection(line, 0, line, len(text))
        self.replaceSelectedText(text)

    def to_title(self):
        if self.hasSelectedText():
            text = self.selectedText().title()
        else:
            line, _ = self.getCursorPosition()
            text = self.text(line).title()
            self.setSelection(line, 0, line, len(text))
        self.replaceSelectedText(text)

    def duplicate_line(self):
        self.send("sci_lineduplicate")

    def delete_line(self):
        if self.hasSelectedText():
            self.send("sci_beginundoaction")
            desde, desde_indice, hasta, _ = self.getSelection()
            self.setCursorPosition(desde, desde_indice)
            while desde != hasta:
                self.send("sci_linedelete")
                desde += 1
            self.send("sci_endundoaction")
        else:
            self.send("sci_linedelete")

    def indent_more(self):
        if self.hasSelectedText():
            self.send("sci_beginundoaction")
            desde, _, hasta, _ = self.getSelection()
            for linea in range(desde, hasta + 1):
                self.indent(linea)
            self.send("sci_endundoaction")
        else:
            linea, _ = self.getCursorPosition()
            self.indent(linea)

    def indent_less(self):
        if self.hasSelectedText():
            self.send("sci_beginundoaction")
            desde, _, hasta, _ = self.getSelection()
            for linea in range(desde, hasta + 1):
                self.unindent(linea)
            self.send("sci_endundoaction")
        else:
            linea, _ = self.getCursorPosition()
            self.unindent(linea)

    def move_down(self):
        self.send("sci_moveselectedlinesdown")

    def move_up(self):
        self.send("sci_moveselectedlinesup")

    def reemplazar_tabs_por_espacios(self):
        codigo = self.text()
        codigo_sin_tabs = codigo.replace('\t', ' ' * self._indentation)
        self.setText(codigo_sin_tabs)

    def saved(self):
        # Esta señal sirve para actualizar el árbol de símbolos
        self.emit(SIGNAL("fileSaved(QString)"), self.obj_file.filename)
        self.setModified(False)
        # Itera todas las líneas y si existe un _marker_modified agrega
        # un _marker_save
        for nline in range(self.lines()):
            if self.markersAtLine(nline):
                self.markerAdd(nline, Editor.MARKER_SAVED)

    def dropEvent(self, event):
        self.emit(SIGNAL("dropEvent(PyQt_PyObject)"), event)
예제 #13
0
    editor.setCaretLineVisible(True)
    editor.setCaretLineBackgroundColor(QtGui.QColor("#CDA869"))

    ## Margins colors
    # line numbers margin
    editor.setMarginsBackgroundColor(QtGui.QColor("#333333"))
    editor.setMarginsForegroundColor(QtGui.QColor("#CCCCCC"))

    # folding margin colors (foreground,background)
    editor.setFoldMarginColors(QtGui.QColor("#99CC66"),
                               QtGui.QColor("#333300"))

    ## Choose a lexer
    lexer = QsciLexerPython1()

    lexer.setDefaultFont(font)
    editor.setLexer(lexer)
    myApis = QsciAPIs(lexer)
    myApis.add(QtCore.QString("SDR"))
    myApis.add(QtCore.QString("sdr"))
    myApis.prepare()
    editor.setAutoCompletionSource(QsciScintilla.AcsAll)
    editor.setAutoCompletionCaseSensitivity(False)
    editor.setAutoCompletionThreshold(1)

    ## Render on screen
    editor.show()

    ## Show this file in the editor
    editor.setText(open("chardet.txt").read())
    sys.exit(app.exec_())
예제 #14
0
class Editor(base.Base):

    # Braces
    BRACE = {'(': ')', '[': ']'}

    # Quotes
    QUOTE = {"'": "'", '"': '"'}

    # Marcadores
    MARKER_MODIFIED = 3
    MARKER_SAVED = 4

    # Indicadores
    WORD_INDICATOR = 0
    WARNING_INDICATOR = 1

    def __init__(self, obj_file):
        super(Editor, self).__init__()
        self.obj_file = obj_file  # Asociación con el objeto EdisFile
        self._font = None
        # Configuración
        use_tabs = settings.get_setting('editor/usetabs')
        self.setIndentationsUseTabs(use_tabs)
        self.setAutoIndent(settings.get_setting('editor/indent'))
        self.setBackspaceUnindents(True)
        # Quita el scrollbar
        self.send("sci_sethscrollbar", 0)
        # Configuración de indicadores
        self.send("sci_indicsetstyle", Editor.WORD_INDICATOR, "indic_box")
        self.send("sci_indicsetfore", Editor.WORD_INDICATOR, QColor("#FF0000"))
        self.send("sci_indicsetstyle", Editor.WARNING_INDICATOR,
                  "indic_squiggle")
        self.send("sci_indicsetfore", Editor.WARNING_INDICATOR,
                  QColor("#0000FF"))
        # Scheme
        self.scheme = editor_scheme.get_scheme(
            settings.get_setting('editor/scheme'))
        # Folding
        self.setFolding(QsciScintilla.PlainFoldStyle)  # en márgen 2
        self.setMarginWidth(3, 5)  # 5px de espacios en márgen 3
        self.send("sci_markersetfore", QsciScintilla.SC_MARKNUM_FOLDER,
                  QColor(self.scheme['FoldMarkerFore']))
        self.send("sci_markersetback", QsciScintilla.SC_MARKNUM_FOLDER,
                  QColor(self.scheme['FoldMarkerBack']))
        self.setFoldMarginColors(QColor(self.scheme['FoldMarginBack']),
                                 QColor(self.scheme['FoldMarginFore']))
        # Marcadores
        self.markerDefine(QsciScintilla.SC_MARK_LEFTRECT,
                          Editor.MARKER_MODIFIED)
        self.setMarkerBackgroundColor(QColor(226, 255, 141),
                                      Editor.MARKER_MODIFIED)
        self.markerDefine(QsciScintilla.SC_MARK_LEFTRECT, Editor.MARKER_SAVED)
        self.setMarkerBackgroundColor(QColor(55, 142, 103),
                                      Editor.MARKER_SAVED)

        # Actualiza flags (espacios en blanco, cursor, sidebar, etc)
        self.update_options()
        # Lexer
        self._lexer = lexer.Lexer()
        self.setLexer(self._lexer)
        # Autocompletado
        self.api = None
        if settings.get_setting('editor/completion'):
            self.active_code_completion()
        # Indentación
        self._indentation = settings.get_setting('editor/width-indent')
        self.send("sci_settabwidth", self._indentation)
        # Minimapa
        self.minimap = None
        if settings.get_setting('editor/minimap'):
            self.minimap = minimap.Minimap(self)
            self.connect(self, SIGNAL("textChanged()"),
                         self.minimap.update_code)
        # Thread que busca palabras
        self.search_thread = editor_helpers.SearchThread()
        self.connect(self.search_thread, SIGNAL("foundWords(PyQt_PyObject)"),
                     self.mark_words)
        # Analizador de estilo de código
        self.checker = None
        if settings.get_setting('editor/style-checker'):
            self.load_checker()
        # Fuente
        font = settings.get_setting('editor/font')
        font_size = settings.get_setting('editor/size-font')
        self.load_font(font, font_size)
        self.setMarginsBackgroundColor(QColor(self.scheme['SidebarBack']))
        self.setMarginsForegroundColor(QColor(self.scheme['SidebarFore']))
        # Línea actual
        self.send("sci_setcaretlinevisible",
                  settings.get_setting('editor/show-caret-line'))
        self.send("sci_setcaretlineback", QColor(self.scheme['CaretLineBack']))
        self.send("sci_setcaretfore", QColor(self.scheme['CaretLineFore']))
        self.send("sci_setcaretlinebackalpha", self.scheme['CaretLineAlpha'])
        # Cursor
        caret_period = settings.get_setting('editor/cursor-period')
        self.send("sci_setcaretperiod", caret_period)
        # Márgen
        if settings.get_setting('editor/show-margin'):
            self.update_margin()
        # Brace matching
        self.setBraceMatching(int(settings.get_setting('editor/match-brace')))
        self.setMatchedBraceBackgroundColor(
            QColor(self.scheme['MatchedBraceBack']))
        self.setMatchedBraceForegroundColor(
            QColor(self.scheme['MatchedBraceFore']))
        self.setUnmatchedBraceBackgroundColor(
            QColor(self.scheme['UnmatchedBraceBack']))
        self.setUnmatchedBraceForegroundColor(
            QColor(self.scheme['UnmatchedBraceFore']))
        # Selecciones Múltiples
        self.send("sci_setadditionalcaretfore", QColor(157, 64, 40))
        self.send("sci_setadditionalcaretsblink", 1)
        self.send("sci_setadditionalselalpha", 100)
        self.send("sci_setmultipleselection", 1)
        self.send("sci_setadditionalselectiontyping", 1)
        # Conexiones
        self.connect(self, SIGNAL("linesChanged()"), self.update_sidebar)
        self.connect(self, SIGNAL("textChanged()"), self._add_marker_modified)

    @property
    def filename(self):
        return self.obj_file.filename

    @property
    def is_modified(self):
        return self.isModified()

    def load_font(self, fuente, tam):
        self._font = QFont(fuente, tam)
        if self._lexer is None:
            self.setFont(self._font)
        else:
            self._lexer.setFont(self._font)
        self.setMarginsFont(self._font)

    def load_checker(self, activated=True):
        if activated and self.checker is not None:
            return
        if not activated:
            self.checker = None
            self.clear_indicators(Editor.WARNING_INDICATOR)
        else:
            self.checker = checker.Checker(self)
            self.connect(self.checker, SIGNAL("finished()"),
                         self._show_violations)

    def set_brace_matching(self):
        match_brace = int(settings.get_setting('editor/match-brace'))
        self.setBraceMatching(match_brace)

    def update_options(self):
        """ Actualiza las opciones del editor """

        if settings.get_setting('editor/show-tabs-spaces'):
            self.setWhitespaceVisibility(self.WsVisible)
        else:
            self.setWhitespaceVisibility(self.WsInvisible)
        self.setIndentationGuides(settings.get_setting('editor/show-guides'))
        if settings.get_setting('editor/wrap-mode'):
            self.setWrapMode(self.WrapWord)
        else:
            self.setWrapMode(self.WrapNone)
        self.send("sci_setcaretstyle", settings.get_setting('editor/cursor'))
        self.setCaretWidth(settings.get_setting('editor/caret-width'))
        self.setAutoIndent(settings.get_setting('editor/indent'))
        self.send("sci_setcaretperiod",
                  settings.get_setting('editor/cursor-period'))
        current_line = settings.get_setting('editor/show-caret-line')
        self.send("sci_setcaretlinevisible", current_line)
        self.setEolVisibility(settings.get_setting('editor/eof'))

    def update_sidebar(self):
        """ Ajusta el ancho del sidebar """

        fmetrics = QFontMetrics(self._font)
        lines = str(self.lines()) + '0'
        line_number = settings.get_setting('editor/show-line-number')
        width = fmetrics.width(lines) if line_number else 0
        self.setMarginWidth(0, width)

    def show_line_numbers(self):
        line_number = settings.get_setting('editor/show-line-number')
        self.setMarginLineNumbers(0, line_number)
        self.update_sidebar()

    def update_margin(self):
        """ Actualiza el ancho del márgen de línea """

        if settings.get_setting('editor/show-margin'):
            self.setEdgeMode(QsciScintilla.EdgeLine)
            ancho = settings.get_setting('editor/width-margin')
            self.setEdgeColumn(ancho)
            self.setEdgeColor(QColor(self.scheme['Margin']))
        else:
            self.setEdgeMode(QsciScintilla.EdgeNone)

    def update_indentation(self):
        ancho = settings.get_setting('editor/width-indent')
        self.send("sci_settabwidth", ancho)
        self._indentation = ancho

    def mark_words(self, palabras):
        self.clear_indicators(Editor.WORD_INDICATOR)
        for p in palabras:
            self.fillIndicatorRange(p[0], p[1], p[0], p[2],
                                    Editor.WORD_INDICATOR)

    def active_code_completion(self, enabled=True):
        if self.api is not None and enabled:
            return
        if enabled:
            self.api = QsciAPIs(self._lexer)
            if settings.get_setting('editor/completion-keywords'):
                for keyword in keywords.keywords:
                    self.api.add(keyword)
                self.api.prepare()
                source = QsciScintilla.AcsAPIs
                if settings.get_setting('editor/completion-document'):
                    source = QsciScintilla.AcsAll
            elif settings.get_setting('editor/completion-document'):
                source = QsciScintilla.AcsDocument
            else:
                source = QsciScintilla.AcsNone
            threshold = settings.get_setting('editor/completion-threshold')
            self.setAutoCompletionThreshold(threshold)
            self.setAutoCompletionSource(source)
            cs = settings.get_setting('editor/completion-cs')
            self.setAutoCompletionCaseSensitivity(cs)
            repl_word = settings.get_setting('editor/completion-replace-word')
            self.setAutoCompletionReplaceWord(repl_word)
            show_single = settings.get_setting('editor/completion-single')
            use_single = 2 if show_single else 0
            self.setAutoCompletionUseSingle(use_single)
        else:
            self.api = None
            self.setAutoCompletionSource(0)

    def _add_marker_modified(self):
        """ Agrega el marcador cuando el texto cambia """

        if not settings.get_setting('editor/mark-change'):
            return
        nline, _ = self.getCursorPosition()
        if self.markersAtLine(nline):
            self.markerDelete(nline)
        self.markerAdd(nline, Editor.MARKER_MODIFIED)

    def _show_violations(self):
        data = self.checker.data
        self.clear_indicators(Editor.WARNING_INDICATOR)
        for line, message in list(data.items()):
            line = int(line) - 1
            self.fillIndicatorRange(line, 0, line, self.lineLength(line),
                                    Editor.WARNING_INDICATOR)

    def _text_under_cursor(self):
        """ Texto seleccionado con el cursor """

        line, index = self.getCursorPosition()  # Posición del cursor
        word = self.wordAtLineIndex(line, index)  # Palabra en esa pos
        return word

    def mouseReleaseEvent(self, e):
        super(Editor, self).mouseReleaseEvent(e)
        if e.button() == Qt.LeftButton:
            word = self._text_under_cursor()
            if not word:
                self.clear_indicators(Editor.WORD_INDICATOR)
                return
            self.search_thread.find(word, self.text())

    def mouseMoveEvent(self, event):
        super(Editor, self).mouseMoveEvent(event)
        if self.checker is None:
            return
        position = event.pos()
        line = str(self.lineAt(position) + 1)
        message = self.checker.data.get(line, None)
        if message is not None:
            QToolTip.showText(self.mapToGlobal(position), message)

    def keyReleaseEvent(self, event):
        super(Editor, self).keyReleaseEvent(event)
        line, _ = self.getCursorPosition()
        self.emit(SIGNAL("linesChanged(int)"), line)

    def keyPressEvent(self, event):
        super(Editor, self).keyPressEvent(event)
        key = event.key()
        # Borra indicador de palabra
        if key == Qt.Key_Escape:
            self.clear_indicators(Editor.WORD_INDICATOR)
            return
        # Completado de comillas
        if key == Qt.Key_Apostrophe:
            if settings.get_setting('editor/complete-single-quote'):
                self._complete_quote(event)
            return
        if key == Qt.Key_QuoteDbl:
            if settings.get_setting('editor/complete-double-quote'):
                self._complete_quote(event)
            return
        # Completado de llaves
        if key == Qt.Key_BraceLeft:
            if settings.get_setting('editor/complete-brace'):
                self._complete_key(event)
        if key in (Qt.Key_BracketLeft, Qt.Key_ParenLeft, Qt.Key_BracketRight,
                   Qt.Key_ParenRight):
            self._complete_brace(event)

    def _complete_quote(self, event):
        """ Autocompleta comillas simples y dobles, si existe el complementario
            el cursor se mueve un espacio.
        """

        complementary = Editor.QUOTE.get(event.text())
        line, index = self.getCursorPosition()
        text_line = self.text(line)
        portion = text_line[index - 1:index + 1]
        if portion in settings.QUOTES:
            self.setSelection(line, index, line, index + 1)
            self.replaceSelectedText('')
        else:
            self.insertAt(complementary, line, index)

    def _complete_brace(self, event):
        """ Autocompleta un brace cuando es abierto '(, ['.
            Si existe el complementario '), ]' el índice del cursor se
            mueve un espacio.
        """

        brace_close = settings.BRACES.get(event.text(), None)
        braces_open = list(Editor.BRACE.keys())
        line, index = self.getCursorPosition()
        if event.text() in settings.BRACES.values():
            text_line = self.text(line)
            found = 0
            # Busco un brace cerrado en el texto
            for token in text_line:
                if token in settings.BRACES.values():
                    found += 1
            try:
                # Brace abierto
                brace_open = text_line[index]
            except:
                brace_open = ''
            if found > 1 and brace_open not in braces_open:
                # Reemplazo el brace sobrante por un string vacío
                self.setSelection(line, index, line, index + 1)
                self.replaceSelectedText('')
            return
        elif event.text() in settings.BRACES.keys(
        ) and brace_close is not None:
            # Inserto el brace complementario
            self.insertAt(brace_close, line, index)

    def _complete_key(self, event):
        """ Autocompleta llaves en funciones y estructuras.
            Si se usa typedef en la definición de la estructura se completa
            la variable.
         """

        line, index = self.getCursorPosition()
        text_line = self.text(line)
        # Estructura
        if REGEX_STRUCT.match(text_line):
            if text_line.split()[0] == 'typedef':
                # Obtengo la variable
                split_line = text_line.split()
                if split_line[-1] == '{':
                    typedef = split_line[-2]
                else:
                    typedef = split_line[-1][:-1]
                text = "}%s;" % typedef.title()
            else:
                text = "};"
            self.insert("\n")
            self.insertAt(text, line + 1, 0)
            return
        # Función
        portion = text_line[index - 3:index]
        if ')' in portion:
            self.insert("\n")
            current_indentation = self.indentation(line)
            self.setIndentation(line + 1, current_indentation)
            self.insertAt('}', line + 1, current_indentation)

    def resizeEvent(self, e):
        super(Editor, self).resizeEvent(e)
        if self.minimap is not None:
            self.minimap.update_geometry()

    def comment(self):
        # FIXME: tener en cuenta /* */
        # FIXME: no funciona si el comentario no inicia en el índice 0
        if self.hasSelectedText():
            line_from, _, line_to, _ = self.getSelection()

            # Iterar todas las líneas seleccionadas
            self.send("sci_beginundoaction")
            for line in range(line_from, line_to + 1):
                self.insertAt('//', line, 0)
            self.send("sci_endundoaction")
        else:
            line, _ = self.getCursorPosition()
            self.insertAt('//', line, 0)

    def uncomment(self):
        if self.hasSelectedText():
            line_from, _, line_to, _ = self.getSelection()
            self.send("sci_beginundoaction")
            for line in range(line_from, line_to + 1):
                self.setSelection(line, 0, line, 2)
                if not self.text(line).startswith('//'):
                    continue
                self.removeSelectedText()
            self.send("sci_endundoaction")
        else:
            line, _ = self.getCursorPosition()
            if not self.text(line).startswith('//'):
                return
            self.setSelection(line, 0, line, 2)
            self.removeSelectedText()

    def to_lowercase(self):
        if self.hasSelectedText():
            text = self.selectedText().lower()
        else:
            line, _ = self.getCursorPosition()
            text = self.text(line).lower()
            self.setSelection(line, 0, line, len(text))
        self.replaceSelectedText(text)

    def to_uppercase(self):
        if self.hasSelectedText():
            text = self.selectedText().upper()
        else:
            line, _ = self.getCursorPosition()
            text = self.text(line).upper()
            self.setSelection(line, 0, line, len(text))
        self.replaceSelectedText(text)

    def to_title(self):
        if self.hasSelectedText():
            text = self.selectedText().title()
        else:
            line, _ = self.getCursorPosition()
            text = self.text(line).title()
            self.setSelection(line, 0, line, len(text))
        self.replaceSelectedText(text)

    def duplicate_line(self):
        self.send("sci_lineduplicate")

    def delete_line(self):
        if self.hasSelectedText():
            self.send("sci_beginundoaction")
            desde, desde_indice, hasta, _ = self.getSelection()
            self.setCursorPosition(desde, desde_indice)
            while desde != hasta:
                self.send("sci_linedelete")
                desde += 1
            self.send("sci_endundoaction")
        else:
            self.send("sci_linedelete")

    def indent_more(self):
        if self.hasSelectedText():
            self.send("sci_beginundoaction")
            desde, _, hasta, _ = self.getSelection()
            for linea in range(desde, hasta + 1):
                self.indent(linea)
            self.send("sci_endundoaction")
        else:
            linea, _ = self.getCursorPosition()
            self.indent(linea)

    def indent_less(self):
        if self.hasSelectedText():
            self.send("sci_beginundoaction")
            desde, _, hasta, _ = self.getSelection()
            for linea in range(desde, hasta + 1):
                self.unindent(linea)
            self.send("sci_endundoaction")
        else:
            linea, _ = self.getCursorPosition()
            self.unindent(linea)

    def move_down(self):
        self.send("sci_moveselectedlinesdown")

    def move_up(self):
        self.send("sci_moveselectedlinesup")

    def reemplazar_tabs_por_espacios(self):
        codigo = self.text()
        codigo_sin_tabs = codigo.replace('\t', ' ' * self._indentation)
        self.setText(codigo_sin_tabs)

    def saved(self):
        # Esta señal sirve para actualizar el árbol de símbolos
        self.emit(SIGNAL("fileSaved(QString)"), self.obj_file.filename)
        self.setModified(False)
        # Itera todas las líneas y si existe un _marker_modified agrega
        # un _marker_save
        for nline in range(self.lines()):
            if self.markersAtLine(nline):
                self.markerAdd(nline, Editor.MARKER_SAVED)

    def dropEvent(self, event):
        self.emit(SIGNAL("dropEvent(PyQt_PyObject)"), event)