File: __init__.py

package info (click to toggle)
frescobaldi 3.0.0~git20161001.0.eec60717%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 19,792 kB
  • ctags: 5,843
  • sloc: python: 37,853; sh: 180; makefile: 69
file content (161 lines) | stat: -rw-r--r-- 5,187 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# vimode -- Vi Mode package for QPlainTextEdit
#
# Copyright (c) 2012 by Wilbert Berendsen
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# See http://www.gnu.org/licenses/ for more information.

"""
ViMode implements a Vi-like mode for QPlainTextEdit.
"""


from PyQt5.QtCore import QEvent, QObject, Qt
from PyQt5.QtGui import QFont, QPalette
from PyQt5.QtWidgets import QApplication, QTextEdit

# the Vi modes
NORMAL = 0
VISUAL = 1
INSERT = 2
REPLACE = 3


class ViMode(QObject):
    """Handles a Vi-like mode for a QPlainTextEdit."""
    def __init__(self, textedit=None):
        QObject.__init__(self)
        
        # init all internal variables
        self._mode = None
        self._handlers = [None] * 4
        self._textedit = None
        self._originalCursorWidth = 1
        
        self.setTextEdit(textedit)
    
    def setTextEdit(self, edit):
        old = self._textedit
        if edit is old:
            return
        if old:
            # disconnect old textedit
            self.clearCursor()
            old.removeEventFilter(self)
            old.cursorPositionChanged.disconnect(self.updateCursorPosition)
            old.selectionChanged.disconnect(self.updateCursorPosition)
            old.setCursorWidth(self._originalCursorWidth)
        self._textedit = edit
        if edit:
            # connect new textedit
            edit.installEventFilter(self)
            edit.cursorPositionChanged.connect(self.updateCursorPosition)
            edit.selectionChanged.connect(self.updateCursorPosition)
            self._originalCursorWidth = edit.cursorWidth()
        self.setMode(NORMAL)
        if edit:
            self.updateCursorPosition()

    def textEdit(self):
        return self._textedit
    
    def setMode(self, mode):
        """Sets the mode (NORMAL, VISUAL, INSERT or REPLACE)."""
        if mode is self._mode:
            return
        assert mode in (NORMAL, VISUAL, INSERT, REPLACE)
        if self._mode is not None and self.handler():
            self.handler().leave()
        self._mode = mode
        if self._handlers[mode] is None:
            self._handlers[mode] = self.createModeHandler(mode)
        self.handler().enter()
        self.updateCursorPosition()
    
    def mode(self):
        """Return the current mode (NORMAL, VISUAL, INSERT or REPLACE)."""
        return self._mode
    
    def isNormal(self):
        return self._mode is NORMAL
    
    def isVisual(self):
        return self._mode is VISUAL
    
    def isInsert(self):
        return self._mode is INSERT
    
    def isReplace(self):
        return self._mode is REPLACE
    
    def createModeHandler(self, mode):
        """Returns a Handler for the specified mode."""
        if mode == NORMAL:
            from . import normal
            return normal.NormalMode(self)
        elif mode == VISUAL:
            from . import visual
            return visual.VisualMode(self)
        elif mode == INSERT:
            from . import insert
            return insert.InsertMode(self)
        elif mode == REPLACE:
            from . import replace
            return replace.ReplaceMode(self)
    
    def handler(self):
        """Returns the current mode handler."""
        return self._handlers[self._mode]
        
    def updateCursorPosition(self):
        """If in command mode, shows a square cursor on the right spot."""
        self.handler().updateCursorPosition()
    
    def drawCursor(self, cursor):
        """Draws the cursor position as the selection of the specified cursor."""
        es = QTextEdit.ExtraSelection()
        es.format.setBackground(self.textEdit().palette().color(QPalette.Text))
        es.format.setForeground(self.textEdit().palette().color(QPalette.Base))
        es.cursor = cursor
        self.textEdit().setExtraSelections([es])

    def clearCursor(self):
        """Removes the drawn cursor position."""
        self.textEdit().setExtraSelections([])

    def eventFilter(self, obj, ev):
        if (ev.type() in (QEvent.KeyPress, QEvent.KeyRelease) and
            ev.key() in (Qt.Key_Shift, Qt.Key_Control, Qt.Key_Alt, Qt.Key_Meta)):
            return False
        if ev.type() == QEvent.KeyPress:
            return self.handler().handleKeyPress(ev) or False
        return False


            
def test():
    a = QApplication([])
    e = QTextEdit()
    e.setCursorWidth(2)
    e.setFont(QFont("Monospace"))
    e.setPlainText("""\
some text
here another line
and here yet another
""")
    e.show()
    v = ViMode(e)
    a.exec_()