~ubuntu-branches/ubuntu/trusty/reinteract/trusty

« back to all changes in this revision

Viewing changes to src/reinteract_wrapper_osx/pyNativeMainMenu.m

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2009-03-28 00:53:14 UTC
  • Revision ID: james.westby@ubuntu.com-20090328005314-ramzoo0q6r8rmwuc
Tags: upstream-0.5.0
ImportĀ upstreamĀ versionĀ 0.5.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: ObjC; c-basic-offset: 4; indent-tabs-mode: nil; -*-
 
2
 *
 
3
 * Copyright 2008 Owen Taylor
 
4
 *
 
5
 * This file is part of Reinteract and distributed under the terms
 
6
 * of the BSD license. See the file COPYING in the Reinteract
 
7
 * distribution for full details.
 
8
 *
 
9
 ************************************************************************/
 
10
 
 
11
#include <config.h>
 
12
 
 
13
#include "ThunkPython.h"
 
14
#include <gdk/gdk.h>
 
15
#include <gdk/gdkquartz.h>
 
16
#include <pygobject.h>
 
17
#include <dlfcn.h>
 
18
 
 
19
#import "MenuController.h"
 
20
 
 
21
/* This file implements the reinteract.native_main_menu object, which is
 
22
 * glue between the menu and the PyGTK code. The interface here is:
 
23
 *
 
24
 * class NativeMainMenu:
 
25
 *     def do_action(self, action_name):
 
26
 *        """An Action was selected"""
 
27
 *        pass # Override in subclass
 
28
 *
 
29
 *     def enable_action(self, action_name):
 
30
 *        """Enable the action with the specified name"""
 
31
 *        [...]
 
32
 *
 
33
 *     def disable_action(self, action_name):
 
34
 *        """Disable the action with the specified name"""
 
35
 *        [...]
 
36
 *
 
37
 *     def get_action_names(self):
 
38
 *        """Return a list of all action names in the menu"""
 
39
 *        [...]
 
40
 *
 
41
 *     def handle_key_press(self, event):
 
42
 *        """Check key equivalents and activate a menu item if appropriate
 
43
 *
 
44
 *        @returns: True if a menu item was activated
 
45
 *        """
 
46
 *        [...]
 
47
 *
 
48
 * You use this class by deriving from it and instantiating a singleton
 
49
 * copy. Instantiating more than one copy of a derived class will produce
 
50
 * undefined and probably bad results.
 
51
 */
 
52
 
 
53
typedef struct {
 
54
    PyObject_HEAD
 
55
    MenuController *controller;
 
56
} pyNativeMainMenu;
 
57
 
 
58
static void
 
59
pyNativeMenu_actionCallback(NSString *actionName, void *data)
 
60
{
 
61
    pyNativeMainMenu *slf = data;
 
62
 
 
63
    PyGILState_STATE gstate;
 
64
    gstate = PyGILState_Ensure();
 
65
 
 
66
    PyObject *result = PyObject_CallMethod((PyObject *)slf, "do_action", "s", [actionName UTF8String]);
 
67
    if (result == NULL)
 
68
        PyErr_Print();
 
69
    else
 
70
        Py_DECREF(result);
 
71
 
 
72
    PyGILState_Release(gstate);
 
73
}
 
74
 
 
75
static int
 
76
pyNativeMainMenu_init(pyNativeMainMenu *slf, PyObject *args, PyObject *kwds)
 
77
{
 
78
    if (!PyArg_ParseTuple(args, ""))
 
79
        return -1;
 
80
 
 
81
    slf->controller = [[MenuController alloc] init];
 
82
    [slf->controller setActionCallback:pyNativeMenu_actionCallback callbackData:slf];
 
83
 
 
84
    // Create the MainMenu object from the NIB file and set our controller
 
85
    // object as the delegate
 
86
    NSNib *nib = [[NSNib alloc] initWithNibNamed: @"MainMenu" bundle:nil];
 
87
    if (![nib instantiateNibWithOwner:slf->controller topLevelObjects:nil]){
 
88
        PyErr_SetString(PyExc_RuntimeError, "Can't instantiate MainMenu.nib");
 
89
        return -1;
 
90
    }
 
91
 
 
92
    // Once the nib has been loaded and the objects created (the menu
 
93
    // is automatically added to the global NSApp), we don't need the data
 
94
    // of the NIB any more
 
95
    [nib release];
 
96
 
 
97
    [slf->controller addActionsFromMenu:[NSApp mainMenu]];
 
98
 
 
99
    // finishLaunching actually shows the menu. There might be some
 
100
    // justification for splitting calling this out into a separate menu
 
101
    [NSApp finishLaunching];
 
102
 
 
103
    return 0;
 
104
}
 
105
 
 
106
static PyObject *
 
107
pyNativeMainMenu_enable_action(pyNativeMainMenu *slf, PyObject *args)
 
108
{
 
109
    const char *action_name;
 
110
 
 
111
    if (!PyArg_ParseTuple(args, "s", &action_name))
 
112
        return NULL;
 
113
 
 
114
    [slf->controller enableAction:[NSString stringWithUTF8String:action_name]];
 
115
 
 
116
    Py_RETURN_NONE;
 
117
}
 
118
 
 
119
static PyObject *
 
120
pyNativeMainMenu_disable_action(pyNativeMainMenu *slf, PyObject *args)
 
121
{
 
122
    const char *action_name;
 
123
 
 
124
    if (!PyArg_ParseTuple(args, "s", &action_name))
 
125
        return NULL;
 
126
 
 
127
    [slf->controller disableAction:[NSString stringWithUTF8String:action_name]];
 
128
 
 
129
    Py_RETURN_NONE;
 
130
}
 
131
 
 
132
static PyObject *
 
133
pyNativeMainMenu_get_action_names(pyNativeMainMenu *slf)
 
134
{
 
135
    NSArray *names = [slf->controller actionNames];
 
136
    PyObject *result = PyList_New([names count]);
 
137
    int i;
 
138
 
 
139
    if (!result)
 
140
        return NULL;
 
141
 
 
142
    for (i = 0; i < [names count]; i++) {
 
143
        const char *name = [[names objectAtIndex:i] UTF8String];
 
144
        PyObject *py_name = PyString_FromString(name);
 
145
        if (!py_name) {
 
146
            Py_DECREF(result);
 
147
            return NULL;
 
148
        }
 
149
 
 
150
        PyList_SetItem(result, i, py_name); // Steals reference to py_name
 
151
    }
 
152
 
 
153
    return result;
 
154
}
 
155
 
 
156
static NSEvent *
 
157
event_get_nsevent(GdkEvent *event)
 
158
{
 
159
    static NSEvent *(*ptr_gdk_quartz_event_get_nsevent) (GdkEvent*);
 
160
    if (!ptr_gdk_quartz_event_get_nsevent) {
 
161
        ptr_gdk_quartz_event_get_nsevent = dlsym(RTLD_DEFAULT, "gdk_quartz_event_get_nsevent");
 
162
        if (!ptr_gdk_quartz_event_get_nsevent) {
 
163
            fprintf(stderr, "Can't get pointer to gdk_quartz_event_get_nsevent()");
 
164
            return NULL;
 
165
        }
 
166
    }
 
167
 
 
168
    return (*ptr_gdk_quartz_event_get_nsevent) (event);
 
169
}
 
170
 
 
171
/* All key events get intercepted by the gtk-quartz event loop before
 
172
 * normal processing by NSApplication can deliver them to the menu bar.
 
173
 * For this reason, we have to pass key events received on our toplevel
 
174
 * windows to the menu bar ourselves.
 
175
 *
 
176
 * Luckily the original NSEvent can be retrieved from the GdkEvent so
 
177
 * we don't have to synthesize a new event ourselves.
 
178
 */
 
179
static PyObject *
 
180
pyNativeMainMenu_handle_key_press(pyNativeMainMenu *slf, PyObject *args)
 
181
{
 
182
    PyObject *py_event;
 
183
 
 
184
    if (!PyArg_ParseTuple(args, "O", &py_event))
 
185
        return NULL;
 
186
 
 
187
#if 0
 
188
    /* Doing it this way would require us to call pygobject_init() first
 
189
     * which is possible but requires us to thunk more Python API to make
 
190
     * that (inline) functin happy. pyg_boxed_get() is just casting
 
191
     * and structure access, so doesn't require pygobject_init()
 
192
     */
 
193
    if (!pyg_boxed_check(py_event, GDK_TYPE_EVENT)) {
 
194
        PyErr_SetString(PyExc_TypeError, "Argument must be a GdkEvent");
 
195
        return NULL;
 
196
    }
 
197
#else
 
198
    PyObject *module = PyImport_ImportModule("gtk.gdk");
 
199
    PyObject *attribute = PyObject_GetAttrString(module, "Event");
 
200
    if (!PyObject_TypeCheck(py_event, (PyTypeObject *)attribute)) {
 
201
        PyErr_SetString(PyExc_TypeError, "Argument must be a GdkEvent");
 
202
        return NULL;
 
203
    }
 
204
#endif
 
205
 
 
206
    NSEvent *event = event_get_nsevent(pyg_boxed_get(py_event, GdkEvent));
 
207
 
 
208
    if ([[NSApp mainMenu] performKeyEquivalent:event])
 
209
        Py_RETURN_TRUE;
 
210
    else
 
211
        Py_RETURN_FALSE;
 
212
}
 
213
 
 
214
static PyMethodDef pyNativeMainMenu_methods[] = {
 
215
    {"enable_action", (PyCFunction)pyNativeMainMenu_enable_action, METH_VARARGS,
 
216
     "Enable the menu item with the specified action name"
 
217
    },
 
218
    {"disable_action", (PyCFunction)pyNativeMainMenu_disable_action, METH_VARARGS,
 
219
     "Disable the menu item with the specified action name"
 
220
    },
 
221
    {"get_action_names", (PyCFunction)pyNativeMainMenu_get_action_names, METH_NOARGS,
 
222
     "Return a list of all action names"
 
223
    },
 
224
    {"handle_key_press", (PyCFunction)pyNativeMainMenu_handle_key_press, METH_VARARGS,
 
225
     "Perform any key equivalents for the given key event"
 
226
    },
 
227
    {NULL}  /* Sentinel */
 
228
};
 
229
 
 
230
static PyTypeObject pyNativeMainMenuType = {
 
231
    PyObject_HEAD_INIT(NULL)
 
232
    0,                                 /*ob_size*/
 
233
    "reinteract.native_main_menu.NativeMainMenu", /*tp_name*/
 
234
    sizeof(pyNativeMainMenu),            /*tp_basicsize*/
 
235
    0,                                 /*tp_itemsize*/
 
236
    0,                                 /*tp_dealloc*/
 
237
    0,                                 /*tp_print*/
 
238
    0,                                 /*tp_getattr*/
 
239
    0,                                 /*tp_setattr*/
 
240
    0,                                 /*tp_compare*/
 
241
    0,                                 /*tp_repr*/
 
242
    0,                                 /*tp_as_number*/
 
243
    0,                                 /*tp_as_sequence*/
 
244
    0,                                 /*tp_as_mapping*/
 
245
    0,                                 /*tp_hash */
 
246
    0,                                 /*tp_call*/
 
247
    0,                                 /*tp_str*/
 
248
    0,                                 /*tp_getattro*/
 
249
    0,                                 /*tp_setattro*/
 
250
    0,                                 /*tp_as_buffer*/
 
251
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
 
252
    "Native Interface to the main menu", /* tp_doc */
 
253
    0,                                 /* tp_traverse */
 
254
    0,                                 /* tp_clear */
 
255
    0,                                 /* tp_richcompare */
 
256
    0,                                 /* tp_weaklistoffset */
 
257
    0,                                 /* tp_iter */
 
258
    0,                                 /* tp_iternext */
 
259
    pyNativeMainMenu_methods,            /* tp_methods */
 
260
    0,                                 /* tp_members */
 
261
    0,                                 /* tp_getset */
 
262
    0,                                 /* tp_base */
 
263
    0,                                 /* tp_dict */
 
264
    0,                                 /* tp_descr_get */
 
265
    0,                                 /* tp_descr_set */
 
266
    0,                                 /* tp_dictoffset */
 
267
    (initproc)pyNativeMainMenu_init,   /* tp_init */
 
268
    0,                                 /* tp_alloc */
 
269
    0,                                 /* tp_new */
 
270
};
 
271
 
 
272
static PyMethodDef native_main_menu_methods[] = {
 
273
    { NULL } /* No module level methods */
 
274
};
 
275
 
 
276
PyMODINIT_FUNC
 
277
init_py_native_main_menu(void)
 
278
{
 
279
    PyObject *m;
 
280
 
 
281
    if (PyType_Ready(&pyNativeMainMenuType) < 0)
 
282
        return;
 
283
 
 
284
    // We need to load the reinteract package so we have the namespace
 
285
    // for reinteract.native_main_menu. We don't actually need anything
 
286
    // from reinteract.__init__.py, which is empty.
 
287
 
 
288
    PyObject *tmp = PyImport_ImportModule("reinteract");
 
289
    if (tmp)
 
290
        Py_DECREF(tmp);
 
291
    else {
 
292
        PyErr_Print();
 
293
        return;
 
294
    }
 
295
 
 
296
    m = Py_InitModule("reinteract.native_main_menu", native_main_menu_methods);
 
297
 
 
298
    pyNativeMainMenuType.tp_new = PyType_GenericNew;
 
299
 
 
300
    Py_INCREF(&pyNativeMainMenuType);
 
301
    PyModule_AddObject(m, "NativeMainMenu", (PyObject *)&pyNativeMainMenuType);
 
302
}