forked from mhammond/pywin32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
win32cmd.cpp
246 lines (230 loc) · 10.8 KB
/
win32cmd.cpp
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
/*
win32 command target
Created July 1994, Mark Hammond (MHammond@skippinet.com.au)
@doc
*/
#include "stdafx.h"
CCmdTarget *GetCCmdTargetPtr(PyObject *self)
{
return (CCmdTarget *)ui_assoc_object::GetGoodCppObject(self, &PyCCmdTarget::type);
}
/////////////////////////////////////////////////////////////////////
//
// Command target object
//
//////////////////////////////////////////////////////////////////////
// @pymethod |PyCCmdTarget|BeginWaitCursor|
// Displays the cursor as an hourglass. This can be used when you expect a
// command to take a noticeable time to execute (eg, when a document
// loads or saves itself to a file.).
// <nl>The actions of BeginWaitCursor are not always effective outside of a single
// message handler as other actions, such as OnSetCursor handling, could change
// the cursor.
// <nl>Call EndWaitCursor to restore the previous cursor.
static PyObject *PyCCmdTarget_begin_wait_cursor(PyObject *self, PyObject *args)
{
CHECK_NO_ARGS(args);
CCmdTarget *pCC = GetCCmdTargetPtr(self);
if (!pCC)
return NULL;
pCC->BeginWaitCursor(); // @pyseemfc CWnd|BeginWaitCursor
RETURN_NONE;
}
// @pymethod |PyCCmdTarget|EndWaitCursor|Ends a wait cursor. Should only be called after <om PyCWnd.BeginWaitCursor>.
static PyObject *PyCCmdTarget_end_wait_cursor(PyObject *self, PyObject *args)
{
CHECK_NO_ARGS(args);
CCmdTarget *pCC = GetCCmdTargetPtr(self);
if (!pCC)
return NULL;
pCC->EndWaitCursor();
RETURN_NONE;
}
// @pymethod |PyCCmdTarget|RestoreWaitCursor|Restores the appropriate hourglass cursor after the system cursor has
// changed.
static PyObject *PyCCmdTarget_restore_wait_cursor(PyObject *self, PyObject *args)
{
// @comm Call this function to restore the appropriate hourglass cursor after
// the system cursor has changed (for example, after a message box has opened
// and then closed while in the middle of a lengthy operation).
CHECK_NO_ARGS(args);
CCmdTarget *pCC = GetCCmdTargetPtr(self);
if (!pCC)
return NULL;
pCC->RestoreWaitCursor();
RETURN_NONE;
}
// @pymethod object|PyCCmdTarget|HookOleEvent|Hook an OLE Event.
static PyObject *PyCCmdTarget_hook_ole_event(PyObject *self, PyObject *args)
{
PyCCmdTarget *s = (PyCCmdTarget *)self;
// @rdesc The return value is the previous handler, or None.
return add_hook_list(s, args, &s->pOleEventHookList);
}
// @pymethod object|PyCCmdTarget|HookCommand|Hook a windows command handler.
static PyObject *PyCCmdTarget_hook_command(PyObject *self, PyObject *args)
{
// @comm obHandler will be called as the application receives command notification messages with the specified ID.
// Command notification messages are usually sent in response to menu or toolbar commands.
// <nl>When updating a user interface element, Pythonwin will first check if a
// handler has been installed via <om PyCCmdTarget.HookCommandUpdate>. If so, this alone
// determines the state of the interface object. If no Update handler exists,
// PythonWin will automatically enable a menu/toolbar item if a command handler exists
// The handler will be called with 2 arguments
// <nl>* The command id being handled.
// <nl>* The command notification code.
// <nl>If the handler returns TRUE, then the command will be passed on to the
// default handler, otherwise the message will be consumed.
// <nl>This method is best suited to handling messages from user interface
// elements, such as menus, toolbars, etc. To handle notification messages from a control,
// you should use <om PyCCmdTarget.HookNotify>
// @pyparm object|obHandler||The handler for the command message. This must be a callable object.
// @pyparm int|id||The ID of the command to be handled, or zero to handle all command messages.
PyCCmdTarget *s = (PyCCmdTarget *)self;
// @rdesc The return value is the previous handler, or None.
return add_hook_list(s, args, &s->pCommandHookList);
}
// @pymethod object|PyCCmdTarget|HookCommandUpdate|Hook a windows command update handler.
static PyObject *PyCCmdTarget_hook_command_update(PyObject *self, PyObject *args)
{
// @comm The handler object passed will be called as
// the application updates user interface elements
// with the specified ID.
// See <om PyCCmdTarget.HookCommand> for a description
// of the rules used to determine command routing and updating.
// @pyparm object|obHandler||The handler for the command message. This must be a callable object.
// @pyparm int|id||The ID of the command to be handled.
PyCCmdTarget *s = (PyCCmdTarget *)self;
// @rdesc The return value is the previous handler, or None.
return add_hook_list(s, args, &s->pCommandUpdateHookList);
}
// @pymethod object|PyCCmdTarget|HookNotify|Hook a windows command handler.
static PyObject *PyCCmdTarget_hook_notify(PyObject *self, PyObject *args)
{
// @comm obHandler will be called as the application receives control notification messages.
// These may also be handled via <om PyCCmdTarget.HookCommand>, but this method is specific
// to control notifications, and therefore provides more information.
//
// The handler will be called with 2 arguments<nl>
// * A tuple describing standard notification information.<nl>
// * A tuple describing extra notification params, or an integer containing the address of the first byte of the
// extended information.<nl> If the handler returns TRUE, then the command will be passed on to the default handler,
// otherwise the message will be consumed.
//
// Certain notification codes are recognised internally, and these are converted to a Python tuple.
// If the extra information is not recognised, the address is passed. These addresses could be
// extracted using <om win32ui.GetBytes> and the struct module, or using
// Sam Rushing's calldll/dynwin module. (It would be possible to extend Pythonwin so a program
// can install certain knowledge about handlers, but this has not been implemented.)
// @pyparm object|obHandler||The handler for the command message. This must be a callable object.
// @pyparm int|id||The ID of the command to be handled, or zero to handle all command messages.
PyCCmdTarget *s = (PyCCmdTarget *)self;
// @rdesc The return value is the previous handler, or None.
return add_hook_list(s, args, &s->pNotifyHookList);
}
// @object PyCCmdTarget|An abstract command target class. Encapsulates an MFC <c CCmdTarget> class
static struct PyMethodDef PyCCmdTarget_methods[] = {
{"BeginWaitCursor", PyCCmdTarget_begin_wait_cursor,
1}, // @pymeth BeginWaitCursor|Displays the cursor as an hourglass.
{"EndWaitCursor", PyCCmdTarget_end_wait_cursor, 1}, // @pymeth EndWaitCursor|End a wait cursor.
{"HookCommand", PyCCmdTarget_hook_command, 1}, // @pymeth HookCommand|Hook a command handler.
{"HookCommandUpdate", PyCCmdTarget_hook_command_update,
1}, // @pymeth HookCommandUpdate|Hook a windows command update handler.
{"HookOleEvent", PyCCmdTarget_hook_ole_event, 1}, // @pymeth HookOleEvent|Hooks an OLE event.
{"HookNotify", PyCCmdTarget_hook_notify, 1}, // @pymeth HookNotify|Hook a control notification handler.
{"RestoreWaitCursor", PyCCmdTarget_restore_wait_cursor,
1}, // @pymeth RestoreWaitCursor|Restores the appropriate hourglass cursor after the system cursor has changed.
{NULL, NULL}};
ui_type_CObject PyCCmdTarget::type("PyCCmdTarget", &ui_assoc_CObject::type, RUNTIME_CLASS(CCmdTarget),
sizeof(PyCCmdTarget), PYOBJ_OFFSET(PyCCmdTarget), PyCCmdTarget_methods, NULL);
PyCCmdTarget::PyCCmdTarget()
{
pOleEventHookList = NULL;
pCommandHookList = NULL;
pNotifyHookList = NULL;
pCommandUpdateHookList = NULL;
// virtuals.SetOwner(this);
}
PyCCmdTarget::~PyCCmdTarget()
{
free_hook_list(this, &pNotifyHookList);
free_hook_list(this, &pOleEventHookList);
free_hook_list(this, &pCommandHookList);
free_hook_list(this, &pCommandUpdateHookList);
}
CString PyCCmdTarget::repr()
{
CString csRet;
SSIZE_T numCmd = pCommandHookList ? pCommandHookList->GetCount() : 0;
SSIZE_T numNotify = pNotifyHookList ? pNotifyHookList->GetCount() : 0;
SSIZE_T numCmdUpdate = pCommandUpdateHookList ? pCommandUpdateHookList->GetCount() : 0;
SSIZE_T numOle = pOleEventHookList ? pOleEventHookList->GetCount() : 0;
csRet.Format(_T(", notify=%Iu,ch/u=%Iu/%Iu"), numNotify, numCmd, numCmdUpdate);
return ui_assoc_object::repr() + csRet;
}
/////////////////////////////////////////////////////////////////////
//
// add_hook_list
//
// keep a reference to the hooked object.
// Return old handler, or None
PyObject *add_hook_list(PyObject *hookedObject, PyObject *args, CMapWordToPtr **ppList)
{
CMapWordToPtr *&pList = *ppList;
if (pList == NULL)
pList = new CMapWordToPtr();
PyObject *method;
int message;
if (!PyArg_ParseTuple(args, "Oi", &method, &message))
return NULL;
if (method != Py_None && !PyCallable_Check(method))
RETURN_ERR("The parameter must be a callable object or None");
void *oldMethod = NULL;
// note I maybe decref, then maybe incref. To ensure the object will
// not be destroyed (ie, ref go to zero) between the 2 calls), I
// add a temporary reference first.
Py_INCREF(hookedObject);
if (pList->Lookup(message, oldMethod)) {
pList->RemoveKey(message);
// oldMethod is returned - don't drop its reference.
Py_DECREF(hookedObject);
}
if (method != Py_None) {
Py_INCREF(method);
pList->SetAt(message, method);
Py_INCREF(hookedObject);
}
Py_DECREF(hookedObject); // remove temp reference added above.
if (oldMethod)
return (PyObject *)oldMethod;
else
RETURN_NONE;
// RETURN_NONE;
}
//
// free_hook_list
//
// this is a bit nasty! This function is called when the window itself
// is closed. As all the hooks into the window are decref'd, it is possible
// (actually, likely!) that one of the member Py_DECREFS will also cause the
// window object itself to destruct (as the member function in my list was the
// last remaining (indirect) reference to the window) which also calls this.
// Therefore I set the list value to NULL before freeing the members, so
// the recursive call is not harmful.
void free_hook_list(PyObject *hookedObject, CMapWordToPtr **ppList)
{
CMapWordToPtr *pList = *ppList;
if (pList == NULL)
return; // nothing to do.
*ppList = NULL;
POSITION pos;
void *method;
WORD message;
// Iterate through the entire map
for (pos = pList->GetStartPosition(); pos != NULL;) {
pList->GetNextAssoc(pos, message, method);
Py_XDECREF((PyObject *)method);
Py_XDECREF(hookedObject);
}
delete pList;
}