forked from mhammond/pywin32
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbindings.py
More file actions
178 lines (148 loc) · 5.83 KB
/
bindings.py
File metadata and controls
178 lines (148 loc) · 5.83 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
from __future__ import annotations
import traceback
import win32api
import win32con
import win32ui
from . import IDLEenvironment, keycodes
HANDLER_ARGS_GUESS = 0
HANDLER_ARGS_NATIVE = 1
HANDLER_ARGS_IDLE = 2
HANDLER_ARGS_EXTENSION = 3
next_id = 5000
event_to_commands: dict[str, int] = {} # dict of event names to IDs
command_to_events: dict[int, str] = {} # dict of IDs to event names
def assign_command_id(event, id=0):
global next_id
if id == 0:
id = event_to_commands.get(event, 0)
if id == 0:
id = next_id
next_id += 1
# Only map the ones we allocated - specified ones are assumed to have a handler
command_to_events[id] = event
event_to_commands[event] = id
return id
class SendCommandHandler:
def __init__(self, cmd):
self.cmd = cmd
def __call__(self, *args):
win32ui.GetMainFrame().SendMessage(win32con.WM_COMMAND, self.cmd)
class Binding:
def __init__(self, handler, handler_args_type):
self.handler = handler
self.handler_args_type = handler_args_type
class BindingsManager:
def __init__(self, parent_view):
self.parent_view = parent_view
self.bindings = {} # dict of Binding instances.
self.keymap = {}
def prepare_configure(self):
self.keymap = {}
def complete_configure(self):
for id in command_to_events:
self.parent_view.HookCommand(self._OnCommand, id)
def close(self):
self.parent_view = self.bindings = self.keymap = None
def report_error(self, problem):
try:
win32ui.SetStatusText(problem, 1)
except win32ui.error:
# No status bar!
print(problem)
def update_keymap(self, keymap):
self.keymap.update(keymap)
def bind(self, event, handler, handler_args_type=HANDLER_ARGS_GUESS, cid=0):
if handler is None:
handler = SendCommandHandler(cid)
self.bindings[event] = self._new_binding(handler, handler_args_type)
self.bind_command(event, cid)
def bind_command(self, event, id=0):
"Binds an event to a Windows control/command ID"
id = assign_command_id(event, id)
return id
def get_command_id(self, event):
id = event_to_commands.get(event)
if id is None:
# See if we even have an event of that name!?
if event not in self.bindings:
return None
id = self.bind_command(event)
return id
def _OnCommand(self, id, code):
event = command_to_events.get(id)
if event is None:
self.report_error("No event associated with event ID %d" % id)
return 1
return self.fire(event)
def _new_binding(self, event, handler_args_type):
return Binding(event, handler_args_type)
def _get_IDLE_handler(self, ext, handler):
try:
instance = self.parent_view.idle.IDLEExtension(ext)
name = handler.replace("-", "_") + "_event"
return getattr(instance, name)
except (ImportError, AttributeError):
msg = f"Can not find event '{handler}' in IDLE extension '{ext}'"
self.report_error(msg)
return None
def fire(self, event, event_param=None):
# Fire the specified event. Result is native Pythonwin result
# (ie, 1==pass one, 0 or None==handled)
# First look up the event directly - if there, we are set.
binding = self.bindings.get(event)
if binding is None:
# If possible, find it!
# A native method name
handler = getattr(self.parent_view, event + "Event", None)
if handler is None:
# Can't decide if I should report an error??
self.report_error("The event name '%s' can not be found." % event)
# Either way, just let the default handlers grab it.
return 1
binding = self._new_binding(handler, HANDLER_ARGS_NATIVE)
# Cache it.
self.bindings[event] = binding
handler_args_type = binding.handler_args_type
# Now actually fire it.
if handler_args_type == HANDLER_ARGS_GUESS:
# Can't be native, as natives are never added with "guess".
# Must be extension or IDLE.
if event[0] == "<":
handler_args_type = HANDLER_ARGS_IDLE
else:
handler_args_type = HANDLER_ARGS_EXTENSION
try:
if handler_args_type == HANDLER_ARGS_EXTENSION:
args = self.parent_view.idle, event_param
else:
args = (event_param,)
rc = binding.handler(*args)
if handler_args_type == HANDLER_ARGS_IDLE:
# Convert to our return code.
if rc in (None, "break"):
rc = 0
else:
rc = 1
except:
message = "Firing event '%s' failed." % event
print(message)
traceback.print_exc()
self.report_error(message)
rc = 1 # Let any default handlers have a go!
return rc
def fire_key_event(self, msg):
key = msg[2]
keyState = 0
if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
keyState |= win32con.RIGHT_CTRL_PRESSED | win32con.LEFT_CTRL_PRESSED
if win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000:
keyState |= win32con.SHIFT_PRESSED
if win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
keyState |= win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
keyinfo = key, keyState
# Special hacks for the dead-char key on non-US keyboards.
# (XXX - which do not work :-(
event = self.keymap.get(keyinfo)
if event is None:
return 1
return self.fire(event, None)