ed_keyh.py
9.56 KB
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
###############################################################################
# Name: ed_keyh.py #
# Purpose: Editra's Vi Emulation Key Handler #
# Author: Cody Precord <cprecord@editra.org> #
# Copyright: (c) 2008 Cody Precord <staff@editra.org> #
# License: wxWindows License #
###############################################################################
"""
KeyHandler interface for implementing extended key action handling in Editra's
main text editting buffer.
@summary: Custom keyhandler interface
"""
__author__ = "Cody Precord <cprecord@editra.org>"
__svnid__ = "$Id: ed_keyh.py 70747 2012-02-29 01:33:35Z CJP $"
__revision__ = "$Revision: 70747 $"
#-------------------------------------------------------------------------#
# Imports
import re
import wx
import wx.stc
# Editra Libraries
import ed_event
import ed_glob
import ed_basestc
import ed_stc
import string
import ed_vim
#-------------------------------------------------------------------------#
# Use this base class to derive any new keyhandlers from. The keyhandler is
# called upon by the active buffer when ever a key press event happens. The
# handler then has the responsibility of deciding what to do with the key.
#
class KeyHandler(object):
"""KeyHandler base class"""
def __init__(self, stc):
super(KeyHandler, self).__init__()
# Attributes
self.stc = stc
self._blockmode = False
STC = property(lambda self: self.stc)
BlockMode = property(lambda self: self._blockmode,
lambda self,m:setattr(self,'_blockmode', m))
def ClearMode(self):
"""Clear any key input modes to normal input mode"""
evt = ed_event.StatusEvent(ed_event.edEVT_STATUS, self.stc.Id,
'', ed_glob.SB_BUFF)
wx.PostEvent(self.stc.TopLevelParent, evt)
def GetHandlerName(self):
"""Get the name of this handler
@return: string
"""
return u'NULL'
def PreProcessKey(self, key_code, ctrldown=False,
cmddown=False, shiftdown=False, altdown=False):
"""Pre process any keys before they get to the char handler
@param key_code: Raw keycode
@keyword ctrldown: Is the control key down
@keyword cmddown: Is the Command key down (Mac osx)
@keyword shiftdown: Is the Shift key down
@keyword altdown: Is the Alt key down
@return: bool
"""
return False
def ProcessKey(self, key_code, ctrldown=False,
cmddown=False, shiftdown=False, altdown=False):
"""Process the key and return True if it was processed and
false if it was not. The key is recieved at EVT_CHAR.
@param key_code: Raw keycode
@keyword ctrldown: Is the control key down
@keyword cmddown: Is the Command key down (Mac osx)
@keyword shiftdown: Is the Shift key down
@keyword altdown: Is the Alt key down
@return: bool
"""
return False
#-------------------------------------------------------------------------#
class ViKeyHandler(KeyHandler):
"""Defines a key handler for Vi emulation
@summary: Handles key presses according to Vi emulation.
"""
# Vi Mode declarations
NORMAL, \
INSERT, \
VISUAL, \
= range(3)
def __init__(self, stc, use_normal_default=False):
super(ViKeyHandler, self).__init__(stc)
# Attributes
self.mode = 0
self.last = u''
self.last_find = u''
self.commander = ed_vim.EditraCommander(self)
self.buffer = u''
# Insert mode by default
if use_normal_default:
self.NormalMode()
else:
self.InsertMode()
def ClearMode(self):
"""Clear the mode back to default input mode"""
# TODO:CJP when newer scintilla is available in 2.9 use
# blockcaret methods.
self.STC.SetLineCaret()
self.BlockMode = False
self.last = self.cmdcache = u''
super(ViKeyHandler, self).ClearMode()
def GetHandlerName(self):
"""Get the name of this handler"""
return u'VI'
def _SetMode(self, newmode, msg):
"""Set the keyhandlers mode
@param newmode: New mode name to change to
"""
self.buffer = u'' # Clear buffer from last mode
self.mode = newmode
# Update status bar
evt = ed_event.StatusEvent(ed_event.edEVT_STATUS, self.stc.GetId(),
msg, ed_glob.SB_BUFF)
wx.PostEvent(self.stc.GetTopLevelParent(), evt)
def InsertMode(self):
"""Change to insert mode"""
self.stc.SetLineCaret()
self.stc.SetOvertype(False)
self.BlockMode = False
self._SetMode(ViKeyHandler.INSERT, u"INSERT")
def ReplaceMode(self):
"""Change to replace mode
This really just insert mode with overtype set to true
"""
self.stc.SetLineCaret()
self.stc.SetOvertype(True)
self._SetMode(ViKeyHandler.INSERT, u"REPLACE")
def NormalMode(self):
"""Change to normal (command) mode"""
if self.IsInsertMode():
self.commander.SetLastInsertedText(self.buffer)
self.stc.SetOvertype(False)
self.stc.SetBlockCaret()
self.BlockMode = True
self.commander.Deselect()
self.commander.InsertRepetition()
self._SetMode(ViKeyHandler.NORMAL, u'NORMAL')
def VisualMode(self):
"""Change to visual (selection) mode"""
self.stc.SetBlockCaret()
self.BlockMode = True
self.stc.SetOvertype(False)
self._SetMode(ViKeyHandler.VISUAL, u'VISUAL')
self.commander.StartSelection()
def IsInsertMode(self):
"""Test if we are in insert mode"""
return self.mode == ViKeyHandler.INSERT
def IsNormalMode(self):
"""Test if we are in normal mode"""
return self.mode == ViKeyHandler.NORMAL
def IsVisualMode(self):
"""Test if we are in visual mode"""
return self.mode == ViKeyHandler.VISUAL
def PreProcessKey(self, key_code, ctrldown=False,
cmddown=False, shiftdown=False, altdown=False):
"""Pre process any keys before they get to the char handler
@param key_code: Raw keycode
@keyword ctrldown: Is the control key down
@keyword cmddown: Is the Command key down (Mac osx)
@keyword shiftdown: Is the Shift key down
@keyword altdown: Is the Alt key down
@return: bool
"""
if not shiftdown and key_code == wx.WXK_ESCAPE:
# If Vi emulation is active go into Normal mode and
# pass the key event to the char handler by not processing
# the key.
self.NormalMode()
return False
elif (ctrldown or cmddown) and key_code == ord('['):
self.NormalMode()
return True
elif key_code in (wx.WXK_RETURN, wx.WXK_BACK,
wx.WXK_RIGHT, wx.WXK_LEFT) and \
not self.IsInsertMode():
# swallow enter key in normal and visual modes
# HACK: we have to do it form here because ProcessKey
# is only called on Char events, not Key events,
# and the Enter key does not generate a Char event.
self.ProcessKey(key_code)
return True
else:
return False
def ProcessKey(self, key_code, ctrldown=False,
cmddown=False, shiftdown=False, altdown=False):
"""Processes keys and decided whether to interpret them as vim commands
or normal insert text
@param key_code: Raw key code
@keyword cmddown: Command/Ctrl key is down
@keyword shiftdown: Shift Key is down
@keyword altdown : Alt key is down
"""
if ctrldown or cmddown or altdown:
return False
# Mode may change after processing the key, so we need to remember
# whether this was a command or not
f_cmd = self.IsNormalMode() or self.IsVisualMode()
self._ProcessKey(key_code)
# Update status bar
if self.IsNormalMode():
if self.stc.GetTopLevelParent():
evt = ed_event.StatusEvent(ed_event.edEVT_STATUS,
self.stc.GetId(),
u"NORMAL %s" % self.buffer,
ed_glob.SB_BUFF)
wx.PostEvent(self.stc.GetTopLevelParent(), evt)
if f_cmd:
return True
else:
# If we're in insert mode we must return False
# so the text gets inserted into the editor
return False
def _ProcessKey(self, key_code):
"""The real processing of keys"""
char = unichr(key_code)
if self.IsNormalMode() or self.IsVisualMode():
self.buffer += char
if ed_vim.Parse(self.buffer, self.commander):
# command was handled (or invalid) so clear buffer
self.buffer = u''
if self.IsVisualMode():
self.commander.ExtendSelection()
elif self.IsInsertMode():
self.buffer += char
def InsertText(self, pos, text):
"""Insert text and store it in the buffer if we're in insert mode
i.e. as if it was typed in
"""
self.stc.InsertText(pos, text)
if self.IsInsertMode():
self.buffer += text