File size: 6,001 Bytes
d12bc25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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)