2 * Interface to the ncurses panel library
4 * Original version by Thomas Gellekum
9 static char *PyCursesVersion
= "2.1";
15 #include "py_curses.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.
30 PyCursesCheckERR(int code
, char *fname
)
37 PyErr_SetString(PyCursesError
, catchall_ERR
);
39 PyErr_Format(PyCursesError
, "%s() returned ERR", fname
);
45 /*****************************************************************************
47 ******************************************************************************/
49 /* Definition of the panel object and panel type */
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
70 win.addstr(some_string)
71 pan.window().addstr(other_string)
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
;
84 static list_of_panels
*lop
;
86 /* Insert a new panel object into lop */
88 insert_lop(PyCursesPanelObject
*po
)
92 if ((new = (list_of_panels
*)malloc(sizeof(list_of_panels
))) == NULL
) {
102 /* Remove the panel object from lop */
104 remove_lop(PyCursesPanelObject
*po
)
106 list_of_panels
*temp
, *n
;
109 if (temp
->po
== po
) {
114 while (temp
->next
->po
!= po
) {
115 if (temp
->next
== NULL
)
116 PyErr_SetString(PyExc_RuntimeError
,
117 "remove_lop: can't find Panel Object");
120 n
= temp
->next
->next
;
126 /* Return the panel object that corresponds to pan */
127 static PyCursesPanelObject
*
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!? */
136 /* Function Prototype Macros - They are ugly but very, very useful. ;-)
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) \
145 { return PyCursesCheckERR(X(self->pan), # X); }
147 #define Panel_NoArgTrueFalseFunction(X) \
148 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
150 if (X (self->pan) == FALSE) { Py_INCREF(Py_False); return Py_False; } \
151 else { Py_INCREF(Py_True); return Py_True; } }
153 #define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
154 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
157 if (!PyArg_ParseTuple(args, PARSESTR, &arg1, &arg2)) return NULL; \
158 return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
160 /* ------------- PANEL routines --------------- */
162 Panel_NoArgNoReturnFunction(bottom_panel
)
163 Panel_NoArgNoReturnFunction(hide_panel
)
164 Panel_NoArgNoReturnFunction(show_panel
)
165 Panel_NoArgNoReturnFunction(top_panel
)
166 Panel_NoArgTrueFalseFunction(panel_hidden
)
167 Panel_TwoArgNoReturnFunction(move_panel
, int, "ii;y,x")
169 /* Allocation and deallocation of Panel Objects */
172 PyCursesPanel_New(PANEL
*pan
, PyCursesWindowObject
*wo
)
174 PyCursesPanelObject
*po
;
176 po
= PyObject_NEW(PyCursesPanelObject
, &PyCursesPanel_Type
);
177 if (po
== NULL
) return NULL
;
181 if (insert_lop(po
) < 0) {
185 return (PyObject
*)po
;
189 PyCursesPanel_Dealloc(PyCursesPanelObject
*po
)
191 (void)del_panel(po
->pan
);
197 /* panel_above(NULL) returns the bottom panel in the stack. To get
198 this behaviour we use curses.panel.bottom_panel(). */
200 PyCursesPanel_above(PyCursesPanelObject
*self
)
203 PyCursesPanelObject
*po
;
205 pan
= panel_above(self
->pan
);
207 if (pan
== NULL
) { /* valid output, it means the calling panel
208 is on top of the stack */
214 PyErr_SetString(PyExc_RuntimeError
,
215 "panel_above: can't find Panel Object");
219 return (PyObject
*)po
;
222 /* panel_below(NULL) returns the top panel in the stack. To get
223 this behaviour we use curses.panel.top_panel(). */
225 PyCursesPanel_below(PyCursesPanelObject
*self
)
228 PyCursesPanelObject
*po
;
230 pan
= panel_below(self
->pan
);
232 if (pan
== NULL
) { /* valid output, it means the calling panel
233 is on the bottom of the stack */
239 PyErr_SetString(PyExc_RuntimeError
,
240 "panel_below: can't find Panel Object");
244 return (PyObject
*)po
;
248 PyCursesPanel_window(PyCursesPanelObject
*self
)
251 return (PyObject
*)self
->wo
;
255 PyCursesPanel_replace_panel(PyCursesPanelObject
*self
, PyObject
*args
)
257 PyCursesPanelObject
*po
;
258 PyCursesWindowObject
*temp
;
261 if (PyTuple_Size(args
) != 1) {
262 PyErr_SetString(PyExc_TypeError
, "replace requires one argument");
265 if (!PyArg_ParseTuple(args
, "O!;window object",
266 &PyCursesWindow_Type
, &temp
))
269 po
= find_po(self
->pan
);
271 PyErr_SetString(PyExc_RuntimeError
,
272 "replace_panel: can't find Panel Object");
276 rtn
= replace_panel(self
->pan
, temp
->win
);
278 PyErr_SetString(PyCursesError
, "replace_panel() returned ERR");
289 PyCursesPanel_set_panel_userptr(PyCursesPanelObject
*self
, PyObject
*obj
)
292 return PyCursesCheckERR(set_panel_userptr(self
->pan
, (void*)obj
),
293 "set_panel_userptr");
297 PyCursesPanel_userptr(PyCursesPanelObject
*self
)
301 obj
= (PyObject
*) panel_userptr(self
->pan
);
303 PyErr_SetString(PyCursesError
, "no userptr set");
312 /* Module interface */
314 static PyMethodDef PyCursesPanel_Methods
[] = {
315 {"above", (PyCFunction
)PyCursesPanel_above
, METH_NOARGS
},
316 {"below", (PyCFunction
)PyCursesPanel_below
, METH_NOARGS
},
317 {"bottom", (PyCFunction
)PyCursesPanel_bottom_panel
, METH_NOARGS
},
318 {"hidden", (PyCFunction
)PyCursesPanel_panel_hidden
, METH_NOARGS
},
319 {"hide", (PyCFunction
)PyCursesPanel_hide_panel
, METH_NOARGS
},
320 {"move", (PyCFunction
)PyCursesPanel_move_panel
, METH_VARARGS
},
321 {"replace", (PyCFunction
)PyCursesPanel_replace_panel
, METH_VARARGS
},
322 {"set_userptr", (PyCFunction
)PyCursesPanel_set_panel_userptr
, METH_O
},
323 {"show", (PyCFunction
)PyCursesPanel_show_panel
, METH_NOARGS
},
324 {"top", (PyCFunction
)PyCursesPanel_top_panel
, METH_NOARGS
},
325 {"userptr", (PyCFunction
)PyCursesPanel_userptr
, METH_NOARGS
},
326 {"window", (PyCFunction
)PyCursesPanel_window
, METH_NOARGS
},
327 {NULL
, NULL
} /* sentinel */
331 PyCursesPanel_GetAttr(PyCursesPanelObject
*self
, char *name
)
333 return Py_FindMethod(PyCursesPanel_Methods
, (PyObject
*)self
, name
);
336 /* -------------------------------------------------------*/
338 PyTypeObject PyCursesPanel_Type
= {
339 PyObject_HEAD_INIT(NULL
)
341 "_curses_panel.curses panel", /*tp_name*/
342 sizeof(PyCursesPanelObject
), /*tp_basicsize*/
345 (destructor
)PyCursesPanel_Dealloc
, /*tp_dealloc*/
347 (getattrfunc
)PyCursesPanel_GetAttr
, /*tp_getattr*/
348 (setattrfunc
)0, /*tp_setattr*/
352 0, /*tp_as_sequence*/
357 /* Wrapper for panel_above(NULL). This function returns the bottom
358 panel of the stack, so it's renamed to bottom_panel().
359 panel.above() *requires* a panel object in the first place which
360 may be undesirable. */
362 PyCurses_bottom_panel(PyObject
*self
)
365 PyCursesPanelObject
*po
;
369 pan
= panel_above(NULL
);
371 if (pan
== NULL
) { /* valid output, it means
372 there's no panel at all */
378 PyErr_SetString(PyExc_RuntimeError
,
379 "panel_above: can't find Panel Object");
383 return (PyObject
*)po
;
387 PyCurses_new_panel(PyObject
*self
, PyObject
*args
)
389 PyCursesWindowObject
*win
;
392 if (!PyArg_ParseTuple(args
, "O!", &PyCursesWindow_Type
, &win
))
394 pan
= new_panel(win
->win
);
396 PyErr_SetString(PyCursesError
, catchall_NULL
);
399 return (PyObject
*)PyCursesPanel_New(pan
, win
);
403 /* Wrapper for panel_below(NULL). This function returns the top panel
404 of the stack, so it's renamed to top_panel(). panel.below()
405 *requires* a panel object in the first place which may be
408 PyCurses_top_panel(PyObject
*self
)
411 PyCursesPanelObject
*po
;
415 pan
= panel_below(NULL
);
417 if (pan
== NULL
) { /* valid output, it means
418 there's no panel at all */
424 PyErr_SetString(PyExc_RuntimeError
,
425 "panel_below: can't find Panel Object");
429 return (PyObject
*)po
;
432 static PyObject
*PyCurses_update_panels(PyObject
*self
)
441 /* List of functions defined in the module */
443 static PyMethodDef PyCurses_methods
[] = {
444 {"bottom_panel", (PyCFunction
)PyCurses_bottom_panel
, METH_NOARGS
},
445 {"new_panel", (PyCFunction
)PyCurses_new_panel
, METH_VARARGS
},
446 {"top_panel", (PyCFunction
)PyCurses_top_panel
, METH_NOARGS
},
447 {"update_panels", (PyCFunction
)PyCurses_update_panels
, METH_NOARGS
},
448 {NULL
, NULL
} /* sentinel */
451 /* Initialization function for the module */
454 init_curses_panel(void)
458 /* Initialize object type */
459 PyCursesPanel_Type
.ob_type
= &PyType_Type
;
463 /* Create the module and add the functions */
464 m
= Py_InitModule("_curses_panel", PyCurses_methods
);
465 d
= PyModule_GetDict(m
);
467 /* For exception _curses_panel.error */
468 PyCursesError
= PyErr_NewException("_curses_panel.error", NULL
, NULL
);
469 PyDict_SetItemString(d
, "error", PyCursesError
);
471 /* Make the version available */
472 v
= PyString_FromString(PyCursesVersion
);
473 PyDict_SetItemString(d
, "version", v
);
474 PyDict_SetItemString(d
, "__version__", v
);