Fixed bug in time-to-midnight calculation.
[python.git] / Modules / _curses_panel.c
blobb5f30cb85efcbbe8156fef092ac3384a1e261f73
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) \
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) \
156 TYPE arg1, arg2; \
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 */
171 static PyObject *
172 PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
174 PyCursesPanelObject *po;
176 po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type);
177 if (po == NULL) return NULL;
178 po->pan = pan;
179 po->wo = wo;
180 Py_INCREF(wo);
181 if (insert_lop(po) < 0) {
182 PyObject_DEL(po);
183 return NULL;
185 return (PyObject *)po;
188 static void
189 PyCursesPanel_Dealloc(PyCursesPanelObject *po)
191 (void)del_panel(po->pan);
192 Py_DECREF(po->wo);
193 remove_lop(po);
194 PyObject_DEL(po);
197 /* panel_above(NULL) returns the bottom panel in the stack. To get
198 this behaviour we use curses.panel.bottom_panel(). */
199 static PyObject *
200 PyCursesPanel_above(PyCursesPanelObject *self)
202 PANEL *pan;
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 */
209 Py_INCREF(Py_None);
210 return Py_None;
212 po = find_po(pan);
213 if (po == NULL) {
214 PyErr_SetString(PyExc_RuntimeError,
215 "panel_above: can't find Panel Object");
216 return NULL;
218 Py_INCREF(po);
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(). */
224 static PyObject *
225 PyCursesPanel_below(PyCursesPanelObject *self)
227 PANEL *pan;
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 */
234 Py_INCREF(Py_None);
235 return Py_None;
237 po = find_po(pan);
238 if (po == NULL) {
239 PyErr_SetString(PyExc_RuntimeError,
240 "panel_below: can't find Panel Object");
241 return NULL;
243 Py_INCREF(po);
244 return (PyObject *)po;
247 static PyObject *
248 PyCursesPanel_window(PyCursesPanelObject *self)
250 Py_INCREF(self->wo);
251 return (PyObject *)self->wo;
254 static PyObject *
255 PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
257 PyCursesPanelObject *po;
258 PyCursesWindowObject *temp;
259 int rtn;
261 if (PyTuple_Size(args) != 1) {
262 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
263 return NULL;
265 if (!PyArg_ParseTuple(args, "O!;window object",
266 &PyCursesWindow_Type, &temp))
267 return NULL;
269 po = find_po(self->pan);
270 if (po == NULL) {
271 PyErr_SetString(PyExc_RuntimeError,
272 "replace_panel: can't find Panel Object");
273 return NULL;
276 rtn = replace_panel(self->pan, temp->win);
277 if (rtn == ERR) {
278 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
279 return NULL;
281 Py_DECREF(po->wo);
282 po->wo = temp;
283 Py_INCREF(po->wo);
284 Py_INCREF(Py_None);
285 return Py_None;
288 static PyObject *
289 PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
291 Py_INCREF(obj);
292 return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj),
293 "set_panel_userptr");
296 static PyObject *
297 PyCursesPanel_userptr(PyCursesPanelObject *self)
299 PyObject *obj;
300 PyCursesInitialised;
301 obj = (PyObject *) panel_userptr(self->pan);
302 if (obj == NULL) {
303 PyErr_SetString(PyCursesError, "no userptr set");
304 return NULL;
307 Py_INCREF(obj);
308 return obj;
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 */
330 static PyObject *
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)
340 0, /*ob_size*/
341 "_curses_panel.curses panel", /*tp_name*/
342 sizeof(PyCursesPanelObject), /*tp_basicsize*/
343 0, /*tp_itemsize*/
344 /* methods */
345 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
346 0, /*tp_print*/
347 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
348 (setattrfunc)0, /*tp_setattr*/
349 0, /*tp_compare*/
350 0, /*tp_repr*/
351 0, /*tp_as_number*/
352 0, /*tp_as_sequence*/
353 0, /*tp_as_mapping*/
354 0, /*tp_hash*/
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. */
361 static PyObject *
362 PyCurses_bottom_panel(PyObject *self)
364 PANEL *pan;
365 PyCursesPanelObject *po;
367 PyCursesInitialised;
369 pan = panel_above(NULL);
371 if (pan == NULL) { /* valid output, it means
372 there's no panel at all */
373 Py_INCREF(Py_None);
374 return Py_None;
376 po = find_po(pan);
377 if (po == NULL) {
378 PyErr_SetString(PyExc_RuntimeError,
379 "panel_above: can't find Panel Object");
380 return NULL;
382 Py_INCREF(po);
383 return (PyObject *)po;
386 static PyObject *
387 PyCurses_new_panel(PyObject *self, PyObject *args)
389 PyCursesWindowObject *win;
390 PANEL *pan;
392 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
393 return NULL;
394 pan = new_panel(win->win);
395 if (pan == NULL) {
396 PyErr_SetString(PyCursesError, catchall_NULL);
397 return 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
406 undesirable. */
407 static PyObject *
408 PyCurses_top_panel(PyObject *self)
410 PANEL *pan;
411 PyCursesPanelObject *po;
413 PyCursesInitialised;
415 pan = panel_below(NULL);
417 if (pan == NULL) { /* valid output, it means
418 there's no panel at all */
419 Py_INCREF(Py_None);
420 return Py_None;
422 po = find_po(pan);
423 if (po == NULL) {
424 PyErr_SetString(PyExc_RuntimeError,
425 "panel_below: can't find Panel Object");
426 return NULL;
428 Py_INCREF(po);
429 return (PyObject *)po;
432 static PyObject *PyCurses_update_panels(PyObject *self)
434 PyCursesInitialised;
435 update_panels();
436 Py_INCREF(Py_None);
437 return Py_None;
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 */
453 PyMODINIT_FUNC
454 init_curses_panel(void)
456 PyObject *m, *d, *v;
458 /* Initialize object type */
459 PyCursesPanel_Type.ob_type = &PyType_Type;
461 import_curses();
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);
475 Py_DECREF(v);