Merged release21-maint changes.
[python/dscho.git] / Modules / _curses_panel.c
blob42abc2ae3cb848ace6b2989ee5ad8de798542402
1 /*
2 * Interface to the ncurses panel library
4 * Original version by Thomas Gellekum
5 */
7 /* Release Number */
9 static char *PyCursesVersion = "2.1";
11 /* Includes */
13 #include "Python.h"
15 #include "py_curses.h"
17 #include <panel.h>
19 static PyObject *PyCursesError;
22 /* Utility Functions */
25 * Check the return code from a curses function and return None
26 * or raise an exception as appropriate.
29 static PyObject *
30 PyCursesCheckERR(int code, char *fname)
32 if (code != ERR) {
33 Py_INCREF(Py_None);
34 return Py_None;
35 } else {
36 if (fname == NULL) {
37 PyErr_SetString(PyCursesError, catchall_ERR);
38 } else {
39 PyErr_Format(PyCursesError, "%s() returned ERR", fname);
41 return NULL;
45 /*****************************************************************************
46 The Panel Object
47 ******************************************************************************/
49 /* Definition of the panel object and panel type */
51 typedef struct {
52 PyObject_HEAD
53 PANEL *pan;
54 PyCursesWindowObject *wo; /* for reference counts */
55 } PyCursesPanelObject;
57 PyTypeObject PyCursesPanel_Type;
59 #define PyCursesPanel_Check(v) ((v)->ob_type == &PyCursesPanel_Type)
61 /* Some helper functions. The problem is that there's always a window
62 associated with a panel. To ensure that Python's GC doesn't pull
63 this window from under our feet we need to keep track of references
64 to the corresponding window object within Python. We can't use
65 dupwin(oldwin) to keep a copy of the curses WINDOW because the
66 contents of oldwin is copied only once; code like
68 win = newwin(...)
69 pan = win.panel()
70 win.addstr(some_string)
71 pan.window().addstr(other_string)
73 will fail. */
75 /* We keep a linked list of PyCursesPanelObjects, lop. A list should
76 suffice, I don't expect more than a handful or at most a few
77 dozens of panel objects within a typical program. */
78 typedef struct _list_of_panels {
79 PyCursesPanelObject *po;
80 struct _list_of_panels *next;
81 } list_of_panels;
83 /* list anchor */
84 static list_of_panels *lop;
86 /* Insert a new panel object into lop */
87 static int
88 insert_lop(PyCursesPanelObject *po)
90 list_of_panels *new;
92 if ((new = (list_of_panels *)malloc(sizeof(list_of_panels))) == NULL) {
93 PyErr_NoMemory();
94 return -1;
96 new->po = po;
97 new->next = lop;
98 lop = new;
99 return 0;
102 /* Remove the panel object from lop */
103 static void
104 remove_lop(PyCursesPanelObject *po)
106 list_of_panels *temp, *n;
108 temp = lop;
109 if (temp->po == po) {
110 lop = temp->next;
111 free(temp);
112 return;
114 while (temp->next->po != po) {
115 if (temp->next == NULL)
116 PyErr_SetString(PyExc_RuntimeError,
117 "remove_lop: can't find Panel Object");
118 temp = temp->next;
120 n = temp->next->next;
121 free(temp->next);
122 temp->next = n;
123 return;
126 /* Return the panel object that corresponds to pan */
127 static PyCursesPanelObject *
128 find_po(PANEL *pan)
130 list_of_panels *temp;
131 for (temp = lop; temp->po->pan != pan; temp = temp->next)
132 if (temp->next == NULL) return NULL; /* not found!? */
133 return temp->po;
136 /* Function Prototype Macros - They are ugly but very, very useful. ;-)
138 X - function name
139 TYPE - parameter Type
140 ERGSTR - format string for construction of the return value
141 PARSESTR - format string for argument parsing */
143 #define Panel_NoArgNoReturnFunction(X) \
144 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
145 { if (!PyArg_NoArgs(args)) return NULL; \
146 return PyCursesCheckERR(X(self->pan), # X); }
148 #define Panel_NoArgTrueFalseFunction(X) \
149 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
151 if (!PyArg_NoArgs(args)) return NULL; \
152 if (X (self->pan) == FALSE) { Py_INCREF(Py_False); return Py_False; } \
153 else { Py_INCREF(Py_True); return Py_True; } }
155 #define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
156 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
158 TYPE arg1, arg2; \
159 if (!PyArg_Parse(args,PARSESTR, &arg1, &arg2)) return NULL; \
160 return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
162 /* ------------- PANEL routines --------------- */
164 Panel_NoArgNoReturnFunction(bottom_panel)
165 Panel_NoArgNoReturnFunction(hide_panel)
166 Panel_NoArgNoReturnFunction(show_panel)
167 Panel_NoArgNoReturnFunction(top_panel)
168 Panel_NoArgTrueFalseFunction(panel_hidden)
169 Panel_TwoArgNoReturnFunction(move_panel, int, "(ii);y,x")
171 /* Allocation and deallocation of Panel Objects */
173 static PyObject *
174 PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
176 PyCursesPanelObject *po;
178 po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type);
179 if (po == NULL) return NULL;
180 po->pan = pan;
181 po->wo = wo;
182 Py_INCREF(wo);
183 if (insert_lop(po) < 0) {
184 PyObject_DEL(po);
185 return NULL;
187 return (PyObject *)po;
190 static void
191 PyCursesPanel_Dealloc(PyCursesPanelObject *po)
193 (void)del_panel(po->pan);
194 Py_DECREF(po->wo);
195 remove_lop(po);
196 PyMem_DEL(po);
199 /* panel_above(NULL) returns the bottom panel in the stack. To get
200 this behaviour we use curses.panel.bottom_panel(). */
201 static PyObject *
202 PyCursesPanel_above(PyCursesPanelObject *self, PyObject *args)
204 PANEL *pan;
205 PyCursesPanelObject *po;
207 if (!PyArg_NoArgs(args)) return NULL;
209 pan = panel_above(self->pan);
211 if (pan == NULL) { /* valid output, it means the calling panel
212 is on top of the stack */
213 Py_INCREF(Py_None);
214 return Py_None;
216 po = find_po(pan);
217 if (po == NULL) {
218 PyErr_SetString(PyExc_RuntimeError,
219 "panel_above: can't find Panel Object");
220 return NULL;
222 Py_INCREF(po);
223 return (PyObject *)po;
226 /* panel_below(NULL) returns the top panel in the stack. To get
227 this behaviour we use curses.panel.top_panel(). */
228 static PyObject *
229 PyCursesPanel_below(PyCursesPanelObject *self, PyObject *args)
231 PANEL *pan;
232 PyCursesPanelObject *po;
234 if (!PyArg_NoArgs(args)) return NULL;
236 pan = panel_below(self->pan);
238 if (pan == NULL) { /* valid output, it means the calling panel
239 is on the bottom of the stack */
240 Py_INCREF(Py_None);
241 return Py_None;
243 po = find_po(pan);
244 if (po == NULL) {
245 PyErr_SetString(PyExc_RuntimeError,
246 "panel_below: can't find Panel Object");
247 return NULL;
249 Py_INCREF(po);
250 return (PyObject *)po;
253 static PyObject *
254 PyCursesPanel_window(PyCursesPanelObject *self, PyObject *args)
256 if (!PyArg_NoArgs(args)) return NULL;
258 Py_INCREF(self->wo);
259 return (PyObject *)self->wo;
262 static PyObject *
263 PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
265 PyCursesPanelObject *po;
266 PyCursesWindowObject *temp;
267 int rtn;
269 if (ARG_COUNT(args) != 1) {
270 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
271 return NULL;
273 if (!PyArg_ParseTuple(args, "O!;window object",
274 &PyCursesWindow_Type, &temp))
275 return NULL;
277 po = find_po(self->pan);
278 if (po == NULL) {
279 PyErr_SetString(PyExc_RuntimeError,
280 "replace_panel: can't find Panel Object");
281 return NULL;
284 rtn = replace_panel(self->pan, temp->win);
285 if (rtn == ERR) {
286 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
287 return NULL;
289 Py_DECREF(po->wo);
290 po->wo = temp;
291 Py_INCREF(po->wo);
292 Py_INCREF(Py_None);
293 return Py_None;
296 static PyObject *
297 PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *args)
299 PyObject *obj;
301 if (ARG_COUNT(args) != 1) {
302 PyErr_SetString(PyExc_TypeError, "set_userptr requires one argument");
303 return NULL;
305 obj = PyTuple_GetItem(args, 0);
306 Py_INCREF(obj);
307 return PyCursesCheckERR(set_panel_userptr(self->pan, obj),
308 "set_panel_userptr");
311 static PyObject *PyCursesPanel_userptr
312 (PyCursesPanelObject *self, PyObject *args)
314 PyObject *obj;
315 PyCursesInitialised;
316 if (!PyArg_NoArgs(args))
317 return NULL;
318 obj = (PyObject *) panel_userptr(self->pan);
319 Py_INCREF(obj);
320 return obj;
324 /* Module interface */
326 static PyMethodDef PyCursesPanel_Methods[] = {
327 {"above", (PyCFunction)PyCursesPanel_above},
328 {"below", (PyCFunction)PyCursesPanel_below},
329 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel},
330 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden},
331 {"hide", (PyCFunction)PyCursesPanel_hide_panel},
332 {"move", (PyCFunction)PyCursesPanel_move_panel},
333 {"replace", (PyCFunction)PyCursesPanel_replace_panel,
334 METH_VARARGS},
335 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr,
336 METH_VARARGS},
337 {"show", (PyCFunction)PyCursesPanel_show_panel},
338 {"top", (PyCFunction)PyCursesPanel_top_panel},
339 {"userptr", (PyCFunction)PyCursesPanel_userptr},
340 {"window", (PyCFunction)PyCursesPanel_window},
341 {NULL, NULL} /* sentinel */
344 static PyObject *
345 PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
347 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
350 /* -------------------------------------------------------*/
352 PyTypeObject PyCursesPanel_Type = {
353 PyObject_HEAD_INIT(NULL)
354 0, /*ob_size*/
355 "curses panel", /*tp_name*/
356 sizeof(PyCursesPanelObject), /*tp_basicsize*/
357 0, /*tp_itemsize*/
358 /* methods */
359 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
360 0, /*tp_print*/
361 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
362 (setattrfunc)0, /*tp_setattr*/
363 0, /*tp_compare*/
364 0, /*tp_repr*/
365 0, /*tp_as_number*/
366 0, /*tp_as_sequence*/
367 0, /*tp_as_mapping*/
368 0, /*tp_hash*/
371 /* Wrapper for panel_above(NULL). This function returns the bottom
372 panel of the stack, so it's renamed to bottom_panel().
373 panel.above() *requires* a panel object in the first place which
374 may be undesirable. */
375 static PyObject *
376 PyCurses_bottom_panel(PyObject *self, PyObject *args)
378 PANEL *pan;
379 PyCursesPanelObject *po;
381 PyCursesInitialised;
383 if (!PyArg_NoArgs(args)) return NULL;
385 pan = panel_above(NULL);
387 if (pan == NULL) { /* valid output, it means
388 there's no panel at all */
389 Py_INCREF(Py_None);
390 return Py_None;
392 po = find_po(pan);
393 if (po == NULL) {
394 PyErr_SetString(PyExc_RuntimeError,
395 "panel_above: can't find Panel Object");
396 return NULL;
398 Py_INCREF(po);
399 return (PyObject *)po;
402 static PyObject *
403 PyCurses_new_panel(PyObject *self, PyObject *args)
405 PyCursesWindowObject *win;
406 PANEL *pan;
408 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
409 return NULL;
410 pan = new_panel(win->win);
411 if (pan == NULL) {
412 PyErr_SetString(PyCursesError, catchall_NULL);
413 return NULL;
415 return (PyObject *)PyCursesPanel_New(pan, win);
419 /* Wrapper for panel_below(NULL). This function returns the top panel
420 of the stack, so it's renamed to top_panel(). panel.below()
421 *requires* a panel object in the first place which may be
422 undesirable. */
423 static PyObject *
424 PyCurses_top_panel(PyObject *self, PyObject *args)
426 PANEL *pan;
427 PyCursesPanelObject *po;
429 PyCursesInitialised;
431 if (!PyArg_NoArgs(args)) return NULL;
433 pan = panel_below(NULL);
435 if (pan == NULL) { /* valid output, it means
436 there's no panel at all */
437 Py_INCREF(Py_None);
438 return Py_None;
440 po = find_po(pan);
441 if (po == NULL) {
442 PyErr_SetString(PyExc_RuntimeError,
443 "panel_below: can't find Panel Object");
444 return NULL;
446 Py_INCREF(po);
447 return (PyObject *)po;
450 static PyObject *PyCurses_update_panels(PyObject *self, PyObject *args)
452 PyCursesInitialised;
453 if (!PyArg_NoArgs(args)) return NULL;
454 update_panels();
455 Py_INCREF(Py_None);
456 return Py_None;
460 /* List of functions defined in the module */
462 static PyMethodDef PyCurses_methods[] = {
463 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel},
464 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
465 {"top_panel", (PyCFunction)PyCurses_top_panel},
466 {"update_panels", (PyCFunction)PyCurses_update_panels},
467 {NULL, NULL} /* sentinel */
470 /* Initialization function for the module */
472 DL_EXPORT(void)
473 init_curses_panel(void)
475 PyObject *m, *d, *v;
477 /* Initialize object type */
478 PyCursesPanel_Type.ob_type = &PyType_Type;
480 import_curses();
482 /* Create the module and add the functions */
483 m = Py_InitModule("_curses_panel", PyCurses_methods);
484 d = PyModule_GetDict(m);
486 /* For exception _curses_panel.error */
487 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
488 PyDict_SetItemString(d, "error", PyCursesError);
490 /* Make the version available */
491 v = PyString_FromString(PyCursesVersion);
492 PyDict_SetItemString(d, "version", v);
493 PyDict_SetItemString(d, "__version__", v);
494 Py_DECREF(v);