Index: Demo/curses/ncurses.py =================================================================== --- Demo/curses/ncurses.py (revision 55454) +++ Demo/curses/ncurses.py (working copy) @@ -7,6 +7,7 @@ import curses from curses import panel +from curses import menu def wGetchar(win = None): if win == None: win = stdscr @@ -64,6 +65,24 @@ win.move(y, x) win.addch(num) +def demo_menus(win): + global stdscr, nap_msec, mod + stdscr = win + nap_msec = 1 + option = -1 + menu1 = menu.new_menu(5) + + menu1.new_item("option 1", "first option") + menu1.new_item("option 2", "second option") + menu1.new_item("option 3", "third option") + menu1.new_item("option 4", "fourth option") + menu1.new_item("option 5", "fifth option") + option = menu1.release() + saywhat ("Option choisie :" + str(option+1)) + + retfree = menu1.free() + wait_a_while() + def demo_panels(win): global stdscr, nap_msec, mod stdscr = win @@ -71,7 +90,7 @@ mod = ["test", "TEST", "(**)", "*()*", "<-->", "LAST"] stdscr.refresh() - + for y in range(0, curses.LINES - 1): for x in range(0, curses.COLS): stdscr.addstr("%d" % ((y + x) % 10)) @@ -267,7 +286,5 @@ break nap_msec = 100 -# -# one fine day there'll be the menu at this place -# curses.wrapper(demo_panels) +curses.wrapper(demo_menus) Index: setup.py =================================================================== --- setup.py (revision 55454) +++ setup.py (working copy) @@ -981,11 +981,13 @@ # Curses support, requiring the System V version of curses, often # provided by the ncurses library. panel_library = 'panel' + menu_library = 'menu' if (self.compiler.find_library_file(lib_dirs, 'ncursesw')): curses_libs = ['ncursesw'] # Bug 1464056: If _curses.so links with ncursesw, # _curses_panel.so must link with panelw. panel_library = 'panelw' + menu_library = 'menuw' exts.append( Extension('_curses', ['_cursesmodule.c'], libraries = curses_libs) ) elif (self.compiler.find_library_file(lib_dirs, 'ncurses')): @@ -1016,6 +1018,15 @@ else: missing.append('_curses_panel') + # If the curses module is enabled, check for the menu module + if (module_enabled(exts, '_curses') and + self.compiler.find_library_file(lib_dirs, menu_library)): + exts.append( Extension('_curses_menu', ['_curses_menu.c'], + libraries = [menu_library] + curses_libs) ) + else: + missing.append('_curses_menu') + + # Andrew Kuchling's zlib module. Note that some versions of zlib # 1.1.3 have security problems. See CERT Advisory CA-2002-07: # http://www.cert.org/advisories/CA-2002-07.html Index: Modules/_curses_panel.c =================================================================== --- Modules/_curses_panel.c (revision 55454) +++ Modules/_curses_panel.c (working copy) @@ -439,7 +439,6 @@ return Py_None; } - /* List of functions defined in the module */ static PyMethodDef PyCurses_methods[] = { Index: Modules/_curses_menu.c =================================================================== --- Modules/_curses_menu.c (revision 0) +++ Modules/_curses_menu.c (revision 0) @@ -0,0 +1,267 @@ +/* + * Interface to the ncurses menu library + * + * based on _curses_panel by Thomas Gellekum + * + * 05/2007 Fabian Frederick + * first version : menu & items + * + */ + +static char *PyCursesVersion = "2.1"; + +#include "Python.h" +#include "py_curses.h" +#include + +static PyObject *PyCursesError; + +static PyObject * +PyCursesCheckERR(int code, char *fname) +{ + if (code != ERR) { + Py_INCREF(Py_None); + return Py_None; + } else { + if (fname == NULL) { + PyErr_SetString(PyCursesError, catchall_ERR); + } else { + PyErr_Format(PyCursesError, "%s() returned ERR", fname); + } + return NULL; + } +} + +typedef struct { + PyObject_HEAD + MENU *menu; + ITEM **items; + int maxitems; + int currentitems; +} PyCursesMenuObject; + +PyTypeObject PyCursesMenu_Type; + +typedef struct _list_of_menus { + PyCursesMenuObject *po; + struct _list_of_menus *next; +} list_of_menus; + +/* list anchor */ +static list_of_menus *lom; + +static int +insert_lom(PyCursesMenuObject *po) +{ + list_of_menus *new; + + if ((new = (list_of_menus *)malloc(sizeof(list_of_menus))) == NULL) { + PyErr_NoMemory(); + return -1; + } + new->po = po; + new->next = lom; + lom = new; + return 0; +} + +static void +remove_lom(PyCursesMenuObject *po) +{ + list_of_menus *temp, *n; + + temp = lom; + if (temp->po == po) { + lom = temp->next; + free(temp); + return; + } + while (temp->next == NULL || temp->next->po != po) { + if (temp->next == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "remove_lom: can't find Menu Object"); + return; + } + temp = temp->next; + } + n = temp->next->next; + free(temp->next); + temp->next = n; + return; +} + +static PyCursesMenuObject * +find_po(MENU *menu) +{ + list_of_menus *temp; + for (temp = lom; temp->po->menu != menu; temp = temp->next) + if (temp->next == NULL) return NULL; /* not found!? */ + return temp->po; +} + +static PyObject * +PyCursesMenu_New(ITEM **items, int maxitems) +{ + PyCursesMenuObject *po; + + po = PyObject_NEW(PyCursesMenuObject, &PyCursesMenu_Type); + if (po == NULL) return NULL; + po->menu = NULL; + po->items = items; + po->maxitems = maxitems; + po->currentitems = 0; + + if (insert_lom(po) < 0) { + PyObject_DEL(po); + return NULL; + } + return (PyObject *)po; +} +static void +PyCursesMenu_Dealloc(PyCursesMenuObject *po) +{ + remove_lom(po); + PyObject_DEL(po); +} + +/*Init menu items (before new_menu)*/ +static PyObject * +PyCursesMenu_new_item(PyCursesMenuObject *self, PyObject *args) +{ + char *itemname; + char *itemdesc; + if (!PyArg_ParseTuple(args, "ss", &itemname, &itemdesc)) + return -1; + self->items[self->currentitems++]=new_item(itemname, itemdesc); + return PyInt_FromLong(1); +} + +/*Init menu items (before new_menu)*/ +static PyObject * +PyCursesMenu_release(PyCursesMenuObject *self) +{ + int key; + + self->menu = new_menu((ITEM **)self->items); + post_menu(self->menu); + refresh(); + while((key = getch()) != KEY_F(1)){ + switch(key){ + case KEY_DOWN: + menu_driver(self->menu, REQ_DOWN_ITEM); + break; + case KEY_UP: + menu_driver(self->menu, REQ_UP_ITEM); + break; + case 0xA: + return (PyInt_FromLong(item_index(current_item(self->menu)))); + } + } + return -1; +} + +static PyObject * +PyCursesMenu_free(PyCursesMenuObject *self) +{ + PyObject *result; + result=PyInt_FromLong((long)self->currentitems); + return result; + /*return self->currentitems;*/ +/* + for (i = 0;i < self->currentitems;i++) + free_item(self->items[i]); +*/ +/* free_menu(self->menu);*/ +} + +/*Class functions ie myclass.new_item*/ +static PyMethodDef PyCursesMenu_Methods[] = { + {"new_item", (PyCFunction)PyCursesMenu_new_item, METH_VARARGS}, + {"release", (PyCFunction)PyCursesMenu_release, METH_NOARGS}, + {"free", (PyCFunction)PyCursesMenu_free, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +PyCursesMenu_GetAttr(PyCursesMenuObject *self, char *name) +{ + return Py_FindMethod(PyCursesMenu_Methods, (PyObject *)self, name); +} + +/* -------------------------------------------------------*/ + +PyTypeObject PyCursesMenu_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_curses_menu.curses menu", /*tp_name*/ + sizeof(PyCursesMenuObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyCursesMenu_Dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PyCursesMenu_GetAttr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +/*New_menu creates python structure and prepares lom item*/ +static PyObject * +PyCurses_new_menu(PyObject *self, PyObject *args) +{ + MENU *menu; + ITEM **items; + int maxitems; + + if (PyTuple_Size(args) != 1) { + PyErr_SetString(PyExc_TypeError, "new_menu requires maximum items as argument"); + return NULL; + } + if (!PyArg_ParseTuple(args, "i", &maxitems)) + return NULL; + items = (ITEM **)calloc(maxitems+1, sizeof(ITEM *)); + items[maxitems] = (ITEM *)NULL; + + return (PyObject *)PyCursesMenu_New(items, maxitems); +} + + +/*Methods for client*/ +static PyMethodDef PyCurses_methods[] = { + {"new_menu", (PyCFunction)PyCurses_new_menu, METH_VARARGS}, + {"new_item", (PyCFunction)PyCursesMenu_new_item, METH_VARARGS}, + {"release", (PyCFunction)PyCursesMenu_release, METH_NOARGS}, + {"free", (PyCFunction)PyCursesMenu_free, METH_NOARGS}, + {NULL, NULL} +}; + +PyMODINIT_FUNC +init_curses_menu(void) +{ + PyObject *m, *d, *v; + + /* Initialize object type */ + PyCursesMenu_Type.ob_type = &PyType_Type; + + import_curses(); + + /* Create the module and add the functions */ + m = Py_InitModule("_curses_menu", PyCurses_methods); + if (m == NULL) + return; + d = PyModule_GetDict(m); + + /* For exception _curses_menu.error */ + PyCursesError = PyErr_NewException("_curses_menu.error", NULL, NULL); + PyDict_SetItemString(d, "error", PyCursesError); + + /* Make the version available */ + v = PyString_FromString(PyCursesVersion); + PyDict_SetItemString(d, "version", v); + PyDict_SetItemString(d, "__version__", v); + Py_DECREF(v); +}