Add better error reporting for MemoryErrors caused by str->float conversions.
[python.git] / Modules / _curses_panel.c
blob6831473b06f5a30d37ed3b8783e7c1601c38a521
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) (Py_TYPE(v) == &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 == NULL || temp->next->po != po) {
115 if (temp->next == NULL) {
116 PyErr_SetString(PyExc_RuntimeError,
117 "remove_lop: can't find Panel Object");
118 return;
120 temp = temp->next;
122 n = temp->next->next;
123 free(temp->next);
124 temp->next = n;
125 return;
128 /* Return the panel object that corresponds to pan */
129 static PyCursesPanelObject *
130 find_po(PANEL *pan)
132 list_of_panels *temp;
133 for (temp = lop; temp->po->pan != pan; temp = temp->next)
134 if (temp->next == NULL) return NULL; /* not found!? */
135 return temp->po;
138 /* Function Prototype Macros - They are ugly but very, very useful. ;-)
140 X - function name
141 TYPE - parameter Type
142 ERGSTR - format string for construction of the return value
143 PARSESTR - format string for argument parsing */
145 #define Panel_NoArgNoReturnFunction(X) \
146 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
147 { return PyCursesCheckERR(X(self->pan), # X); }
149 #define Panel_NoArgTrueFalseFunction(X) \
150 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
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_ParseTuple(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 PyObject_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)
204 PANEL *pan;
205 PyCursesPanelObject *po;
207 pan = panel_above(self->pan);
209 if (pan == NULL) { /* valid output, it means the calling panel
210 is on top of the stack */
211 Py_INCREF(Py_None);
212 return Py_None;
214 po = find_po(pan);
215 if (po == NULL) {
216 PyErr_SetString(PyExc_RuntimeError,
217 "panel_above: can't find Panel Object");
218 return NULL;
220 Py_INCREF(po);
221 return (PyObject *)po;
224 /* panel_below(NULL) returns the top panel in the stack. To get
225 this behaviour we use curses.panel.top_panel(). */
226 static PyObject *
227 PyCursesPanel_below(PyCursesPanelObject *self)
229 PANEL *pan;
230 PyCursesPanelObject *po;
232 pan = panel_below(self->pan);
234 if (pan == NULL) { /* valid output, it means the calling panel
235 is on the bottom of the stack */
236 Py_INCREF(Py_None);
237 return Py_None;
239 po = find_po(pan);
240 if (po == NULL) {
241 PyErr_SetString(PyExc_RuntimeError,
242 "panel_below: can't find Panel Object");
243 return NULL;
245 Py_INCREF(po);
246 return (PyObject *)po;
249 static PyObject *
250 PyCursesPanel_window(PyCursesPanelObject *self)
252 Py_INCREF(self->wo);
253 return (PyObject *)self->wo;
256 static PyObject *
257 PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
259 PyCursesPanelObject *po;
260 PyCursesWindowObject *temp;
261 int rtn;
263 if (PyTuple_Size(args) != 1) {
264 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
265 return NULL;
267 if (!PyArg_ParseTuple(args, "O!;window object",
268 &PyCursesWindow_Type, &temp))
269 return NULL;
271 po = find_po(self->pan);
272 if (po == NULL) {
273 PyErr_SetString(PyExc_RuntimeError,
274 "replace_panel: can't find Panel Object");
275 return NULL;
278 rtn = replace_panel(self->pan, temp->win);
279 if (rtn == ERR) {
280 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
281 return NULL;
283 Py_DECREF(po->wo);
284 po->wo = temp;
285 Py_INCREF(po->wo);
286 Py_INCREF(Py_None);
287 return Py_None;
290 static PyObject *
291 PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
293 Py_INCREF(obj);
294 return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj),
295 "set_panel_userptr");
298 static PyObject *
299 PyCursesPanel_userptr(PyCursesPanelObject *self)
301 PyObject *obj;
302 PyCursesInitialised;
303 obj = (PyObject *) panel_userptr(self->pan);
304 if (obj == NULL) {
305 PyErr_SetString(PyCursesError, "no userptr set");
306 return NULL;
309 Py_INCREF(obj);
310 return obj;
314 /* Module interface */
316 static PyMethodDef PyCursesPanel_Methods[] = {
317 {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS},
318 {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS},
319 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS},
320 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS},
321 {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS},
322 {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS},
323 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
324 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
325 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
326 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
327 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
328 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
329 {NULL, NULL} /* sentinel */
332 static PyObject *
333 PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
335 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
338 /* -------------------------------------------------------*/
340 PyTypeObject PyCursesPanel_Type = {
341 PyVarObject_HEAD_INIT(NULL, 0)
342 "_curses_panel.curses panel", /*tp_name*/
343 sizeof(PyCursesPanelObject), /*tp_basicsize*/
344 0, /*tp_itemsize*/
345 /* methods */
346 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
347 0, /*tp_print*/
348 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
349 (setattrfunc)0, /*tp_setattr*/
350 0, /*tp_compare*/
351 0, /*tp_repr*/
352 0, /*tp_as_number*/
353 0, /*tp_as_sequence*/
354 0, /*tp_as_mapping*/
355 0, /*tp_hash*/
358 /* Wrapper for panel_above(NULL). This function returns the bottom
359 panel of the stack, so it's renamed to bottom_panel().
360 panel.above() *requires* a panel object in the first place which
361 may be undesirable. */
362 static PyObject *
363 PyCurses_bottom_panel(PyObject *self)
365 PANEL *pan;
366 PyCursesPanelObject *po;
368 PyCursesInitialised;
370 pan = panel_above(NULL);
372 if (pan == NULL) { /* valid output, it means
373 there's no panel at all */
374 Py_INCREF(Py_None);
375 return Py_None;
377 po = find_po(pan);
378 if (po == NULL) {
379 PyErr_SetString(PyExc_RuntimeError,
380 "panel_above: can't find Panel Object");
381 return NULL;
383 Py_INCREF(po);
384 return (PyObject *)po;
387 static PyObject *
388 PyCurses_new_panel(PyObject *self, PyObject *args)
390 PyCursesWindowObject *win;
391 PANEL *pan;
393 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
394 return NULL;
395 pan = new_panel(win->win);
396 if (pan == NULL) {
397 PyErr_SetString(PyCursesError, catchall_NULL);
398 return NULL;
400 return (PyObject *)PyCursesPanel_New(pan, win);
404 /* Wrapper for panel_below(NULL). This function returns the top panel
405 of the stack, so it's renamed to top_panel(). panel.below()
406 *requires* a panel object in the first place which may be
407 undesirable. */
408 static PyObject *
409 PyCurses_top_panel(PyObject *self)
411 PANEL *pan;
412 PyCursesPanelObject *po;
414 PyCursesInitialised;
416 pan = panel_below(NULL);
418 if (pan == NULL) { /* valid output, it means
419 there's no panel at all */
420 Py_INCREF(Py_None);
421 return Py_None;
423 po = find_po(pan);
424 if (po == NULL) {
425 PyErr_SetString(PyExc_RuntimeError,
426 "panel_below: can't find Panel Object");
427 return NULL;
429 Py_INCREF(po);
430 return (PyObject *)po;
433 static PyObject *PyCurses_update_panels(PyObject *self)
435 PyCursesInitialised;
436 update_panels();
437 Py_INCREF(Py_None);
438 return Py_None;
442 /* List of functions defined in the module */
444 static PyMethodDef PyCurses_methods[] = {
445 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
446 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
447 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
448 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
449 {NULL, NULL} /* sentinel */
452 /* Initialization function for the module */
454 PyMODINIT_FUNC
455 init_curses_panel(void)
457 PyObject *m, *d, *v;
459 /* Initialize object type */
460 Py_TYPE(&PyCursesPanel_Type) = &PyType_Type;
462 import_curses();
464 /* Create the module and add the functions */
465 m = Py_InitModule("_curses_panel", PyCurses_methods);
466 if (m == NULL)
467 return;
468 d = PyModule_GetDict(m);
470 /* For exception _curses_panel.error */
471 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
472 PyDict_SetItemString(d, "error", PyCursesError);
474 /* Make the version available */
475 v = PyString_FromString(PyCursesVersion);
476 PyDict_SetItemString(d, "version", v);
477 PyDict_SetItemString(d, "__version__", v);
478 Py_DECREF(v);