blob: e06302df15526c431ac1936ce41354b5db95ddee [file] [log] [blame]
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +00001/*
2 * Interface to the ncurses panel library
3 *
4 * Original version by Thomas Gellekum
5 */
6
7/* Release Number */
8
9static char *PyCursesVersion = "2.1";
10
11/* Includes */
12
13#include "Python.h"
14
15#include "py_curses.h"
16
17#include <panel.h>
18
19static PyObject *PyCursesError;
20
21
22/* Utility Functions */
23
24/*
25 * Check the return code from a curses function and return None
26 * or raise an exception as appropriate.
27 */
28
29static PyObject *
30PyCursesCheckERR(int code, char *fname)
31{
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);
40 }
41 return NULL;
42 }
43}
44
45/*****************************************************************************
46 The Panel Object
47******************************************************************************/
48
49/* Definition of the panel object and panel type */
50
51typedef struct {
52 PyObject_HEAD
53 PANEL *pan;
54 PyCursesWindowObject *wo; /* for reference counts */
55} PyCursesPanelObject;
56
57PyTypeObject PyCursesPanel_Type;
58
59#define PyCursesPanel_Check(v) ((v)->ob_type == &PyCursesPanel_Type)
60
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
67
68 win = newwin(...)
69 pan = win.panel()
70 win.addstr(some_string)
71 pan.window().addstr(other_string)
72
73 will fail. */
74
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. */
78typedef struct _list_of_panels {
79 PyCursesPanelObject *po;
80 struct _list_of_panels *next;
81} list_of_panels;
82
83/* list anchor */
84static list_of_panels *lop;
85
86/* Insert a new panel object into lop */
87static int
88insert_lop(PyCursesPanelObject *po)
89{
90 list_of_panels *new;
91
92 if ((new = (list_of_panels *)malloc(sizeof(list_of_panels))) == NULL) {
93 PyErr_NoMemory();
94 return -1;
95 }
96 new->po = po;
97 new->next = lop;
98 lop = new;
99 return 0;
100}
101
102/* Remove the panel object from lop */
103static void
104remove_lop(PyCursesPanelObject *po)
105{
106 list_of_panels *temp, *n;
107
108 temp = lop;
109 if (temp->po == po) {
110 lop = temp->next;
111 free(temp);
112 return;
113 }
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;
119 }
120 n = temp->next->next;
121 free(temp->next);
122 temp->next = n;
123 return;
124}
125
126/* Return the panel object that corresponds to pan */
127static PyCursesPanelObject *
128find_po(PANEL *pan)
129{
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;
134}
135
136/* Function Prototype Macros - They are ugly but very, very useful. ;-)
137
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 */
142
143#define Panel_NoArgNoReturnFunction(X) \
Fred Drake4e36d582000-12-23 05:46:23 +0000144static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000145{ if (!PyArg_NoArgs(args)) return NULL; \
146 return PyCursesCheckERR(X(self->pan), # X); }
147
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000148#define Panel_NoArgTrueFalseFunction(X) \
Fred Drake4e36d582000-12-23 05:46:23 +0000149static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000150{ \
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; } }
154
155#define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
Fred Drake4e36d582000-12-23 05:46:23 +0000156static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000157{ \
158 TYPE arg1, arg2; \
159 if (!PyArg_Parse(args,PARSESTR, &arg1, &arg2)) return NULL; \
160 return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
161
162/* ------------- PANEL routines --------------- */
163
164Panel_NoArgNoReturnFunction(bottom_panel)
165Panel_NoArgNoReturnFunction(hide_panel)
166Panel_NoArgNoReturnFunction(show_panel)
167Panel_NoArgNoReturnFunction(top_panel)
168Panel_NoArgTrueFalseFunction(panel_hidden)
169Panel_TwoArgNoReturnFunction(move_panel, int, "(ii);y,x")
170
171/* Allocation and deallocation of Panel Objects */
172
173static PyObject *
174PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
175{
176 PyCursesPanelObject *po;
177
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;
186 }
187 return (PyObject *)po;
188}
189
190static void
191PyCursesPanel_Dealloc(PyCursesPanelObject *po)
192{
193 (void)del_panel(po->pan);
194 Py_DECREF(po->wo);
195 remove_lop(po);
196 PyMem_DEL(po);
197}
198
199/* panel_above(NULL) returns the bottom panel in the stack. To get
200 this behaviour we use curses.panel.bottom_panel(). */
201static PyObject *
202PyCursesPanel_above(PyCursesPanelObject *self, PyObject *args)
203{
204 PANEL *pan;
205 PyCursesPanelObject *po;
206
207 if (!PyArg_NoArgs(args)) return NULL;
208
209 pan = panel_above(self->pan);
210
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;
215 }
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;
221 }
222 Py_INCREF(po);
223 return (PyObject *)po;
224}
225
226/* panel_below(NULL) returns the top panel in the stack. To get
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000227 this behaviour we use curses.panel.top_panel(). */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000228static PyObject *
229PyCursesPanel_below(PyCursesPanelObject *self, PyObject *args)
230{
231 PANEL *pan;
232 PyCursesPanelObject *po;
233
234 if (!PyArg_NoArgs(args)) return NULL;
235
236 pan = panel_below(self->pan);
237
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;
242 }
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;
248 }
249 Py_INCREF(po);
250 return (PyObject *)po;
251}
252
253static PyObject *
254PyCursesPanel_window(PyCursesPanelObject *self, PyObject *args)
255{
256 if (!PyArg_NoArgs(args)) return NULL;
257
258 Py_INCREF(self->wo);
259 return (PyObject *)self->wo;
260}
261
262static PyObject *
263PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
264{
265 PyCursesPanelObject *po;
266 PyCursesWindowObject *temp;
267 int rtn;
268
269 if (ARG_COUNT(args) != 1) {
270 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
271 return NULL;
272 }
273 if (!PyArg_ParseTuple(args, "O!;window object",
274 &PyCursesWindow_Type, &temp))
275 return NULL;
276
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;
282 }
283
284 rtn = replace_panel(self->pan, temp->win);
285 if (rtn == ERR) {
286 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
287 return NULL;
288 }
289 Py_DECREF(po->wo);
290 po->wo = temp;
291 Py_INCREF(po->wo);
292 Py_INCREF(Py_None);
293 return Py_None;
294}
295
296static PyObject *
297PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *args)
298{
299 PyObject *obj;
300
301 if (ARG_COUNT(args) != 1) {
302 PyErr_SetString(PyExc_TypeError, "set_userptr requires one argument");
303 return NULL;
304 }
305 obj = PyTuple_GetItem(args, 0);
306 Py_INCREF(obj);
Fred Drake4e36d582000-12-23 05:46:23 +0000307 return PyCursesCheckERR(set_panel_userptr(self->pan, obj),
308 "set_panel_userptr");
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000309}
310
311static PyObject *PyCursesPanel_userptr
312(PyCursesPanelObject *self, PyObject *args)
313{
314 PyObject *obj;
315 PyCursesInitialised;
Fred Drake4e36d582000-12-23 05:46:23 +0000316 if (!PyArg_NoArgs(args))
317 return NULL;
318 obj = (PyObject *) panel_userptr(self->pan);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000319 Py_INCREF(obj);
320 return obj;
321}
322
323
324/* Module interface */
325
326static 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},
Andrew M. Kuchlingd7d2e192000-12-22 22:03:15 +0000333 {"replace", (PyCFunction)PyCursesPanel_replace_panel,
334 METH_VARARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000335 {"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 */
342};
343
344static PyObject *
345PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
346{
347 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
348}
349
350/* -------------------------------------------------------*/
351
352PyTypeObject PyCursesPanel_Type = {
353 PyObject_HEAD_INIT(&PyType_Type)
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*/
369};
370
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. */
375static PyObject *
376PyCurses_bottom_panel(PyObject *self, PyObject *args)
377{
378 PANEL *pan;
379 PyCursesPanelObject *po;
380
381 PyCursesInitialised;
382
383 if (!PyArg_NoArgs(args)) return NULL;
384
385 pan = panel_above(NULL);
386
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000387 if (pan == NULL) { /* valid output, it means
388 there's no panel at all */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000389 Py_INCREF(Py_None);
390 return Py_None;
391 }
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;
397 }
398 Py_INCREF(po);
399 return (PyObject *)po;
400}
401
402static PyObject *
403PyCurses_new_panel(PyObject *self, PyObject *args)
404{
405 PyCursesWindowObject *win;
406 PANEL *pan;
407
Fred Drake4e36d582000-12-23 05:46:23 +0000408 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
409 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000410 pan = new_panel(win->win);
411 if (pan == NULL) {
412 PyErr_SetString(PyCursesError, catchall_NULL);
413 return NULL;
414 }
415 return (PyObject *)PyCursesPanel_New(pan, win);
416}
417
418
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. */
423static PyObject *
424PyCurses_top_panel(PyObject *self, PyObject *args)
425{
426 PANEL *pan;
427 PyCursesPanelObject *po;
428
429 PyCursesInitialised;
430
431 if (!PyArg_NoArgs(args)) return NULL;
432
433 pan = panel_below(NULL);
434
435 if (pan == NULL) { /* valid output, it means
436 there's no panel at all */
437 Py_INCREF(Py_None);
438 return Py_None;
439 }
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;
445 }
446 Py_INCREF(po);
447 return (PyObject *)po;
448}
449
450static PyObject *PyCurses_update_panels(PyObject *self, PyObject *args)
451{
452 PyCursesInitialised;
453 if (!PyArg_NoArgs(args)) return NULL;
454 update_panels();
455 Py_INCREF(Py_None);
456 return Py_None;
457}
458
459
460/* List of functions defined in the module */
461
462static 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 */
468};
469
470/* Initialization function for the module */
471
472void
473init_curses_panel(void)
474{
475 PyObject *m, *d, *v;
476
477 import_curses();
478
479 /* Create the module and add the functions */
480 m = Py_InitModule("_curses_panel", PyCurses_methods);
481 d = PyModule_GetDict(m);
482
483 /* For exception _curses_panel.error */
484 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
485 PyDict_SetItemString(d, "error", PyCursesError);
486
487 /* Make the version available */
488 v = PyString_FromString(PyCursesVersion);
489 PyDict_SetItemString(d, "version", v);
490 PyDict_SetItemString(d, "__version__", v);
491 Py_DECREF(v);
492
493}
494