179 lines
5.9 KiB
Python
179 lines
5.9 KiB
Python
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 of integer IDs to event names.
|
|
command_to_events = {} # dict of event names to int IDs
|
|
|
|
|
|
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 = 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.keys():
|
|
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 = "Can not find event '%s' in IDLE extension '%s'" % (handler, 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 = (
|
|
keyState | win32con.RIGHT_CTRL_PRESSED | win32con.LEFT_CTRL_PRESSED
|
|
)
|
|
if win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000:
|
|
keyState = keyState | win32con.SHIFT_PRESSED
|
|
if win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
|
|
keyState = 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)
|