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) (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
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
== NULL
|| temp
->next
->po
!= po
) {
115 if (temp
->next
== NULL
) {
116 PyErr_SetString(PyExc_RuntimeError
,
117 "remove_lop: can't find Panel Object");
122 n
= temp
->next
->next
;
128 /* Return the panel object that corresponds to pan */
129 static PyCursesPanelObject
*
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!? */
138 /* Function Prototype Macros - They are ugly but very, very useful. ;-)
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) \
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 */
174 PyCursesPanel_New(PANEL
*pan
, PyCursesWindowObject
*wo
)
176 PyCursesPanelObject
*po
;
178 po
= PyObject_NEW(PyCursesPanelObject
, &PyCursesPanel_Type
);
179 if (po
== NULL
) return NULL
;
183 if (insert_lop(po
) < 0) {
187 return (PyObject
*)po
;
191 PyCursesPanel_Dealloc(PyCursesPanelObject
*po
)
193 (void)del_panel(po
->pan
);
199 /* panel_above(NULL) returns the bottom panel in the stack. To get
200 this behaviour we use curses.panel.bottom_panel(). */
202 PyCursesPanel_above(PyCursesPanelObject
*self
)
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 */
216 PyErr_SetString(PyExc_RuntimeError
,
217 "panel_above: can't find Panel Object");
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(). */
227 PyCursesPanel_below(PyCursesPanelObject
*self
)
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 */
241 PyErr_SetString(PyExc_RuntimeError
,
242 "panel_below: can't find Panel Object");
246 return (PyObject
*)po
;
250 PyCursesPanel_window(PyCursesPanelObject
*self
)
253 return (PyObject
*)self
->wo
;
257 PyCursesPanel_replace_panel(PyCursesPanelObject
*self
, PyObject
*args
)
259 PyCursesPanelObject
*po
;
260 PyCursesWindowObject
*temp
;
263 if (PyTuple_Size(args
) != 1) {
264 PyErr_SetString(PyExc_TypeError
, "replace requires one argument");
267 if (!PyArg_ParseTuple(args
, "O!;window object",
268 &PyCursesWindow_Type
, &temp
))
271 po
= find_po(self
->pan
);
273 PyErr_SetString(PyExc_RuntimeError
,
274 "replace_panel: can't find Panel Object");
278 rtn
= replace_panel(self
->pan
, temp
->win
);
280 PyErr_SetString(PyCursesError
, "replace_panel() returned ERR");
291 PyCursesPanel_set_panel_userptr(PyCursesPanelObject
*self
, PyObject
*obj
)
294 return PyCursesCheckERR(set_panel_userptr(self
->pan
, (void*)obj
),
295 "set_panel_userptr");
299 PyCursesPanel_userptr(PyCursesPanelObject
*self
)
303 obj
= (PyObject
*) panel_userptr(self
->pan
);
305 PyErr_SetString(PyCursesError
, "no userptr set");
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 */
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*/
346 (destructor
)PyCursesPanel_Dealloc
, /*tp_dealloc*/
348 (getattrfunc
)PyCursesPanel_GetAttr
, /*tp_getattr*/
349 (setattrfunc
)0, /*tp_setattr*/
353 0, /*tp_as_sequence*/
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. */
363 PyCurses_bottom_panel(PyObject
*self
)
366 PyCursesPanelObject
*po
;
370 pan
= panel_above(NULL
);
372 if (pan
== NULL
) { /* valid output, it means
373 there's no panel at all */
379 PyErr_SetString(PyExc_RuntimeError
,
380 "panel_above: can't find Panel Object");
384 return (PyObject
*)po
;
388 PyCurses_new_panel(PyObject
*self
, PyObject
*args
)
390 PyCursesWindowObject
*win
;
393 if (!PyArg_ParseTuple(args
, "O!", &PyCursesWindow_Type
, &win
))
395 pan
= new_panel(win
->win
);
397 PyErr_SetString(PyCursesError
, catchall_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
409 PyCurses_top_panel(PyObject
*self
)
412 PyCursesPanelObject
*po
;
416 pan
= panel_below(NULL
);
418 if (pan
== NULL
) { /* valid output, it means
419 there's no panel at all */
425 PyErr_SetString(PyExc_RuntimeError
,
426 "panel_below: can't find Panel Object");
430 return (PyObject
*)po
;
433 static PyObject
*PyCurses_update_panels(PyObject
*self
)
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 */
455 init_curses_panel(void)
459 /* Initialize object type */
460 Py_TYPE(&PyCursesPanel_Type
) = &PyType_Type
;
464 /* Create the module and add the functions */
465 m
= Py_InitModule("_curses_panel", PyCurses_methods
);
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
);