diff options
| author | Johannes Hölzl <johannes.hoelzl@gmx.de> | 2006-04-29 18:58:20 +0000 |
|---|---|---|
| committer | Johan Dahlin <johan@src.gnome.org> | 2006-04-29 18:58:20 +0000 |
| commit | 6896d50402332dcac8d079d48d22e30c3e0f7231 (patch) | |
| tree | 626e46020f2134d9254a7f190ff7fba59310cad3 /gobject | |
| parent | cd01d8f09504748a17e1e7ae68ada9d4edaf23fe (diff) | |
| download | pygobject-6896d50402332dcac8d079d48d22e30c3e0f7231.tar.gz pygobject-6896d50402332dcac8d079d48d22e30c3e0f7231.tar.xz pygobject-6896d50402332dcac8d079d48d22e30c3e0f7231.zip | |
reviewed by: Johan Dahlin <jdahlin@async.com.br>
2006-04-29 Johannes Hölzl <johannes.hoelzl@gmx.de>
reviewed by: Johan Dahlin <jdahlin@async.com.br>
* examples/Makefile.am:
* examples/option.py:
* gobject/Makefile.am:
* gobject/gobjectmodule.c: (init_gobject):
* gobject/option.py:
* gobject/pygobject-private.h:
* gobject/pygoptioncontext.c: (pyg_option_context_init),
(pyg_option_context_dealloc), (pyg_option_context_parse),
(pyg_option_context_set_help_enabled),
(pyg_option_context_get_help_enabled),
(pyg_option_context_set_ignore_unknown_options),
(pyg_option_context_get_ignore_unknown_options),
(pyg_option_context_set_main_group),
(pyg_option_context_get_main_group),
(pyg_option_context_add_group), (pyg_option_context_compare),
(pyg_option_context_new):
* gobject/pygoptiongroup.c: (check_if_owned), (destroy_g_group),
(pyg_option_group_init), (pyg_option_group_dealloc), (arg_func),
(pyg_option_group_add_entries),
(pyg_option_group_set_translation_domain),
(pyg_option_group_compare), (pyg_option_group_transfer_group),
(pyg_option_group_new):
* tests/test_option.py:
Add support for GOption, fixes #163645
Diffstat (limited to 'gobject')
| -rw-r--r-- | gobject/Makefile.am | 6 | ||||
| -rw-r--r-- | gobject/gobjectmodule.c | 17 | ||||
| -rw-r--r-- | gobject/option.py | 300 | ||||
| -rw-r--r-- | gobject/pygobject-private.h | 23 | ||||
| -rw-r--r-- | gobject/pygoptioncontext.c | 332 | ||||
| -rw-r--r-- | gobject/pygoptiongroup.c | 344 |
6 files changed, 1020 insertions, 2 deletions
diff --git a/gobject/Makefile.am b/gobject/Makefile.am index aa7cae7..451fecb 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -9,7 +9,7 @@ pkgpyexecdir = $(pyexecdir)/gtk-2.0 # gobject python scripts pygobjectdir = $(pkgpyexecdir)/gobject -pygobject_PYTHON = __init__.py +pygobject_PYTHON = __init__.py option.py pygobject_LTLIBRARIES = _gobject.la @@ -36,7 +36,9 @@ _gobject_la_SOURCES = \ pygpointer.c \ pygiochannel.c \ pygsource.c \ - pygtype.c + pygtype.c \ + pygoptioncontext.c \ + pygoptiongroup.c if PLATFORM_WIN32 _gobject_la_CFLAGS += -DPLATFORM_WIN32 diff --git a/gobject/gobjectmodule.c b/gobject/gobjectmodule.c index c151a78..eb958b3 100644 --- a/gobject/gobjectmodule.c +++ b/gobject/gobjectmodule.c @@ -3101,6 +3101,9 @@ init_gobject(void) REGISTER_TYPE(d, PyGTimeout_Type, "Timeout"); REGISTER_TYPE(d, PyGPollFD_Type, "PollFD"); + REGISTER_TYPE(d, PyGOptionContext_Type, "OptionContext"); + REGISTER_TYPE(d, PyGOptionGroup_Type, "OptionGroup"); + /* glib version */ tuple = Py_BuildValue ("(iii)", glib_major_version, glib_minor_version, glib_micro_version); @@ -3167,7 +3170,21 @@ init_gobject(void) addint(IO_FLAG_GET_MASK); addint(IO_FLAG_SET_MASK); + addint(OPTION_FLAG_HIDDEN); + addint(OPTION_FLAG_IN_MAIN); + addint(OPTION_FLAG_REVERSE); + addint(OPTION_FLAG_NO_ARG); + addint(OPTION_FLAG_FILENAME); + addint(OPTION_FLAG_OPTIONAL_ARG); + addint(OPTION_FLAG_NOALIAS); + + addint(OPTION_ERROR_UNKNOWN_OPTION); + addint(OPTION_ERROR_BAD_VALUE); + addint(OPTION_ERROR_FAILED); + #undef addint + + PyModule_AddStringConstant(m, "OPTION_REMAINING", G_OPTION_REMAINING); PyModule_AddIntConstant(m, "SPAWN_LEAVE_DESCRIPTORS_OPEN", G_SPAWN_LEAVE_DESCRIPTORS_OPEN); diff --git a/gobject/option.py b/gobject/option.py new file mode 100644 index 0000000..4abfee4 --- /dev/null +++ b/gobject/option.py @@ -0,0 +1,300 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +# pygobject - Python bindings for the GObject library +# Copyright (C) 2006 Johannes Hoelzl +# +# gobject/option.py: GOption command line parser +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +"""GOption command line parser + +Extends optparse to use the GOptionGroup, GOptionEntry and GOptionContext +objects. So it is possible to use the gtk, gnome_program and gstreamer command +line groups and contexts. + +Use this interface instead of the raw wrappers of GOptionContext and +GOptionGroup in gobject. +""" + +import sys +import optparse +from optparse import OptionError +import _gobject as gobject + +__all__ = [ + "OptionError", + "Option", + "OptionGroup", + "OptionParser", + "make_option", +] + +class Option(optparse.Option): + """Represents a command line option + + To use the extended possibilities of the GOption API Option + (and make_option) are extended with new types and attributes. + + Types: + filename The supplied arguments are read as filename, GOption + parses this type in with the GLib filename encoding. + + Attributes: + optional_arg This does not need a arguement, but it can be supplied. + hidden The help list does not show this option + in_main This option apears in the main group, this should only + be used for backwards compatibility. + + Use Option.REMAINING as option name to get all positional arguments. + + NOTE: Every argument to an option is passed as utf-8 coded string, the only + exception are options which use the 'filename' type, its arguments + are passed as strings in the GLib filename encoding. + + For further help, see optparse.Option. + """ + TYPES = optparse.Option.TYPES + ( + 'filename', + ) + + ATTRS = optparse.Option.ATTRS + [ + 'hidden', + 'in_main', + 'optional_arg', + ] + + REMAINING = '--' + gobject.OPTION_REMAINING + + def __init__(self, *args, **kwargs): + optparse.Option.__init__(self, *args, **kwargs) + if not self._long_opts: + raise ValueError("%s at least one long option name.") + + if len(self._long_opts) < len(self._short_opts): + raise ValueError("%s at least more long option names than short option names.") + + if not self.help: + raise ValueError("%s needs a help message.", self._long_opts[0]) + + + def _set_opt_string(self, opts): + if self.REMAINING in opts: + self._long_opts.append(self.REMAINING) + optparse.Option._set_opt_string(self, opts) + if len(self._short_opts) > len(self._long_opts): + raise OptionError("goption.Option needs more long option names " + "than short option names") + + def _to_goptionentries(self): + flags = 0 + + if self.hidden: + self.flags |= gobject.OPTION_FLAG_HIDDEN + + if self.in_main: + self.flags |= gobject.OPTION_FLAG_IN_MAIN + + if self.takes_value(): + if self.optional_arg: + flags |= gobject.OPTION_FLAG_OPTIONAL_ARG + else: + flags |= gobject.OPTION_FLAG_NO_ARG + + if self.type == 'filename': + flags |= gobject.OPTION_FLAG_FILENAME + + for (long_name, short_name) in zip(self._long_opts, self._short_opts): + yield (long_name[2:], short_name[1], flags, self.help, self.metavar) + + for long_name in self._long_opts[len(self._short_opts):]: + yield (long_name[2:], '\0', flags, self.help, self.metavar) + +class OptionGroup(optparse.OptionGroup): + """A group of command line options. + + Arguements: + name: The groups name, used to create the --help-{name} option + description: Shown as title of the groups help view + help_description: Shown as help to the --help-{name} option + option_list: The options used in this group, must be option.Option() + defaults: A dicitionary of default values + translation_domain: Sets the translation domain for gettext(). + + NOTE: This OptionGroup does not exactly map the optparse.OptionGroup + interface. There is no parser object to supply, but it is possible + to set default values and option_lists. Also the default values and + values are not shared with the OptionParser. + + To pass a OptionGroup into a function which expects a GOptionGroup (e.g. + gnome_program_init() ). OptionGroup.get_option_group() can be used. + + For further help, see optparse.OptionGroup. + """ + def __init__(self, name, description, help_description="", + option_list=None, defaults=None, + translation_domain=None): + optparse.OptionContainer.__init__(self, Option, 'error', description) + self.name = name + self.parser = None + self.help_description = help_description + if defaults: + self.defaults = defaults + + self.values = None + + self.translation_domain = translation_domain + + for option in option_list: + self.add_option(option) + + def _create_option_list(self): + self.option_list = [] + self._create_option_mappings() + + def _to_goptiongroup(self, parser): + def callback(option_name, option_value, group): + if option_name.startswith('--'): + opt = self._long_opt[option_name] + else: + opt = self._short_opt[option_name] + opt.process(option_name, option_value, self.values, parser) + + group = gobject.OptionGroup(self.name, self.description, + self.help_description, callback) + if self.translation_domain: + group.set_translation_domain(self.translation_domain) + + entries = [] + for option in self.option_list: + entries.extend(option._to_goptionentries()) + group.add_entries(entries) + + return group + + def get_option_group(self, parser = None): + """ Returns the corresponding GOptionGroup object. + + Can be used as parameter for gnome_program_init(), gtk_init(). + """ + self.set_values_to_defaults() + return self._to_goptiongroup(parser) + + def set_values_to_defaults(self): + for option in self.option_list: + default = self.defaults.get(option.dest) + if isinstance(default, basestring): + opt_str = option.get_opt_string() + self.defaults[option.dest] = option.check_value(opt_str, default) + self.values = optparse.Values(self.defaults) + +class OptionParser(optparse.OptionParser): + """Command line parser with GOption support. + + NOTE: The OptionParser interface is not the exactly the same as the + optparse.OptionParser interface. Especially the usage parameter + is only used to show the metavar of the arguements. + + Attribues: + help_enabled: The --help, --help-all and --help-{group} + options are enabled (default). + ignore_unknown_options: Do not throw a exception when a option is not + knwon, the option will be in the result list. + + OptionParser.add_option_group() does not only accept OptionGroup instances + but also gobject.OptionGroup, which is returned by gtk_get_option_group(). + + Only gobject.option.OptionGroup and gobject.option.Option instances should + be passed as groups and options. + + For further help, see optparse.OptionParser. + """ + + def __init__(self, *args, **kwargs): + if 'option_class' not in kwargs: + kwargs['option_class'] = Option + self.help_enabled = kwargs.pop('help_enabled', True) + self.ignore_unknown_options = kwargs.pop('ignore_unknown_options', False) + optparse.OptionParser.__init__(self, add_help_option=False, *args, **kwargs) + + def set_usage(self, usage): + if usage is None: + self.usage = '' + elif usage.startswith("%prog"): + self.usage = usage[len("%prog"):] + else: + self.usage = usage + + def _to_goptioncontext(self, values): + if self.description: + parameter_string = self.usage + " - " + self.description + else: + parameter_string = self.usage + context = gobject.OptionContext(parameter_string) + context.set_help_enabled(self.help_enabled) + context.set_ignore_unknown_options(self.ignore_unknown_options) + + for option_group in self.option_groups: + if isinstance(option_group, gobject.OptionGroup): + g_group = option_group + else: + g_group = option_group.get_option_group(self) + context.add_group(g_group) + + def callback(option_name, option_value, group): + if option_name.startswith('--'): + opt = self._long_opt[option_name] + else: + opt = self._short_opt[option_name] + opt.process(option_name, option_value, values, self) + + main_group = gobject.OptionGroup(None, None, None, callback) + main_entries = [] + for option in self.option_list: + main_entries.extend(option._to_goptionentries()) + main_group.add_entries(main_entries) + context.set_main_group(main_group) + + return context + + def add_option_group(self, *args, **kwargs): + if isinstance(args[0], basestring): + optparse.OptionParser.add_option_group(self, + OptionGroup(self, *args, **kwargs)) + return + elif len(args) == 1 and not kwargs: + if isinstance(args[0], OptionGroup): + if not args[0].parser: + args[0].parser = self + if args[0].parser is not self: + raise ValueError("invalid OptionGroup (wrong parser)") + if isinstance(args[0], gobject.OptionGroup): + self.option_groups.append(args[0]) + return + optparse.OptionParser.add_option_group(self, *args, **kwargs) + + def _get_all_options(self): + options = self.option_list[:] + for group in self.option_groups: + if isinstance(group, optparse.OptionGroup): + options.extend(group.option_list) + return options + + def _process_args(self, largs, rargs, values): + context = self._to_goptioncontext(values) + largs.extend(context.parse([sys.argv[0]] + rargs)) + + +make_option = Option diff --git a/gobject/pygobject-private.h b/gobject/pygobject-private.h index b6f3cd2..20ed244 100644 --- a/gobject/pygobject-private.h +++ b/gobject/pygobject-private.h @@ -218,4 +218,27 @@ typedef struct } PyGPollFD; +/* pygoption.c */ +extern PyTypeObject PyGOptionContext_Type; +extern PyTypeObject PyGOptionGroup_Type; + +typedef struct { + PyObject_HEAD + GOptionGroup *group; + gboolean other_owner, is_in_context; + PyObject *callback; + GSList *strings; /* all strings added with the entries, are freed on + GOptionGroup.destroy() */ +} PyGOptionGroup; + +typedef struct { + PyObject_HEAD + PyGOptionGroup *main_group; + GOptionContext *context; +} PyGOptionContext; + +PyObject * pyg_option_context_new (GOptionContext * context); +PyObject * pyg_option_group_new (GOptionGroup * group); +GOptionGroup *pyg_option_group_transfer_group(PyGOptionGroup *self); + #endif diff --git a/gobject/pygoptioncontext.c b/gobject/pygoptioncontext.c new file mode 100644 index 0000000..6e592f6 --- /dev/null +++ b/gobject/pygoptioncontext.c @@ -0,0 +1,332 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * pygtk- Python bindings for the GTK toolkit. + * Copyright (C) 2006 Johannes Hoelzl + * + * pygoptioncontext.c: GOptionContext wrapper + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "pygobject-private.h" + +static int +pyg_option_context_init(PyGOptionContext *self, + PyObject *args, + PyObject *kwargs) +{ + char *parameter_string; + if (!PyArg_ParseTuple(args, "s:gobject.GOptionContext.__init__", + ¶meter_string)) + return -1; + + self->context = g_option_context_new(parameter_string); + return 0; +} + +static void +pyg_option_context_dealloc(PyGOptionContext *self) +{ + Py_CLEAR(self->main_group); + + if (self->context != NULL) + { + GOptionContext *tmp = self->context; + self->context = NULL; + g_option_context_free(tmp); + } + + PyObject_Del(self); +} + +static PyObject * +pyg_option_context_parse(PyGOptionContext *self, + PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = { "argv", NULL }; + PyObject *arg; + PyObject *new_argv, *argv; + gssize argv_length, pos; + char **argv_content, **original; + GError *error = NULL; + gboolean result; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:GOptionContext.parse", + kwlist, &argv)) + return NULL; + + if (!PyList_Check(argv)) + { + PyErr_SetString(PyExc_TypeError, + "GOptionContext.parse expects a list of strings."); + return NULL; + } + + argv_length = PyList_Size(argv); + if (argv_length == -1) + { + PyErr_SetString(PyExc_TypeError, + "GOptionContext.parse expects a list of strings."); + return NULL; + } + + argv_content = g_new(char*, argv_length + 1); + argv_content[argv_length] = NULL; + for (pos = 0; pos < argv_length; pos++) + { + arg = PyList_GetItem(argv, pos); + argv_content[pos] = g_strdup(PyString_AsString(arg)); + if (argv_content[pos] == NULL) + { + g_strfreev(argv_content); + return NULL; + } + } + original = g_strdupv(argv_content); + + pyg_begin_allow_threads; + result = g_option_context_parse(self->context, &argv_length, &argv_content, + &error); + pyg_end_allow_threads; + + if (!result) + { + g_free(argv_content); + g_strfreev(original); + pyg_error_check(&error); + return NULL; + } + + new_argv = PyList_New(g_strv_length(argv_content)); + for (pos = 0; pos < argv_length; pos++) + { + arg = PyString_FromString(argv_content[pos]); + PyList_SetItem(new_argv, pos, arg); + } + + g_strfreev(original); + g_free(argv_content); + return new_argv; +} + +static PyObject * +pyg_option_context_set_help_enabled(PyGOptionContext *self, + PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = { "help_enable", NULL }; + PyObject *help_enabled; + if (! PyArg_ParseTupleAndKeywords(args, kwargs, + "O:GOptionContext.set_help_enabled", + kwlist, &help_enabled)) + return NULL; + g_option_context_set_help_enabled(self->context, PyObject_IsTrue(help_enabled)); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pyg_option_context_get_help_enabled(PyGOptionContext *self) +{ + return PyBool_FromLong(g_option_context_get_help_enabled(self->context)); +} + +static PyObject * +pyg_option_context_set_ignore_unknown_options(PyGOptionContext *self, + PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = { "ignore_unknown_options", NULL }; + PyObject *ignore_unknown_options; + if (! PyArg_ParseTupleAndKeywords(args, kwargs, + "O:GOptionContext.set_ignore_unknown_options", + kwlist, &ignore_unknown_options)) + return NULL; + g_option_context_set_ignore_unknown_options(self->context, + PyObject_IsTrue(ignore_unknown_options)); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pyg_option_context_get_ignore_unknown_options(PyGOptionContext *self) +{ + return PyBool_FromLong( + g_option_context_get_ignore_unknown_options(self->context)); +} + +static PyObject * +pyg_option_context_set_main_group(PyGOptionContext *self, + PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = { "group", NULL }; + GOptionGroup *g_group; + PyObject *group; + if (! PyArg_ParseTupleAndKeywords(args, kwargs, + "O:GOptionContext.set_main_group", + kwlist, &group)) + return NULL; + if (PyObject_IsInstance(group, (PyObject*) &PyGOptionGroup_Type) != 1) { + PyErr_SetString(PyExc_TypeError, + "GOptionContext.set_main_group expects a GOptionGroup."); + return NULL; + } + g_group = pyg_option_group_transfer_group((PyGOptionGroup*) group); + if (g_group == NULL) + { + PyErr_SetString(PyExc_RuntimeError, "Group is already in a OptionContext."); + return NULL; + } + + g_option_context_set_main_group(self->context, g_group); + Py_INCREF(group); + self->main_group = (PyGOptionGroup*) group; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pyg_option_context_get_main_group(PyGOptionContext *self) +{ + if (self->main_group == NULL) + { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(self->main_group); + return (PyObject*) self->main_group; +} + +static PyObject * +pyg_option_context_add_group(PyGOptionContext *self, + PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = { "group", NULL }; + GOptionGroup *g_group; + PyObject *group; + if (! PyArg_ParseTupleAndKeywords(args, kwargs, + "O:GOptionContext.add_group", + kwlist, &group)) + return NULL; + if (PyObject_IsInstance(group, (PyObject*) &PyGOptionGroup_Type) != 1) { + PyErr_SetString(PyExc_TypeError, + "GOptionContext.add_group expects a GOptionGroup."); + return NULL; + } + g_group = pyg_option_group_transfer_group((PyGOptionGroup*) group); + if (g_group == NULL) + { + PyErr_SetString(PyExc_RuntimeError, + "Group is already in a OptionContext."); + return NULL; + } + Py_INCREF(group); + g_option_context_add_group(self->context, g_group); + + Py_INCREF(Py_None); + return Py_None; +} + +static int +pyg_option_context_compare(PyGOptionContext *self, PyGOptionContext *context) +{ + if (self->context == context->context) return 0; + if (self->context > context->context) + return 1; + return -1; +} + +static PyMethodDef pyg_option_context_methods[] = { + { "parse", (PyCFunction)pyg_option_context_parse, METH_VARARGS | METH_KEYWORDS }, + { "set_help_enabled", (PyCFunction)pyg_option_context_set_help_enabled, METH_VARARGS | METH_KEYWORDS }, + { "get_help_enabled", (PyCFunction)pyg_option_context_get_help_enabled, METH_NOARGS }, + { "set_ignore_unknown_options", (PyCFunction)pyg_option_context_set_ignore_unknown_options, METH_VARARGS | METH_KEYWORDS }, + { "get_ignore_unknown_options", (PyCFunction)pyg_option_context_get_ignore_unknown_options, METH_NOARGS }, + { "set_main_group", (PyCFunction)pyg_option_context_set_main_group, METH_VARARGS | METH_KEYWORDS }, + { "get_main_group", (PyCFunction)pyg_option_context_get_main_group, METH_NOARGS }, + { "add_group", (PyCFunction)pyg_option_context_add_group, METH_VARARGS | METH_KEYWORDS }, + { NULL, NULL, 0 }, +}; + +PyTypeObject PyGOptionContext_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "gobject.OptionContext", + sizeof(PyGMainContext), + 0, + /* methods */ + (destructor)pyg_option_context_dealloc, + (printfunc)0, + (getattrfunc)0, + (setattrfunc)0, + (cmpfunc)pyg_option_context_compare, + (reprfunc)0, + 0, + 0, + 0, + (hashfunc)0, + (ternaryfunc)0, + (reprfunc)0, + (getattrofunc)0, + (setattrofunc)0, + 0, + Py_TPFLAGS_DEFAULT, + NULL, + (traverseproc)0, + (inquiry)0, + (richcmpfunc)0, + 0, + (getiterfunc)0, + (iternextfunc)0, + pyg_option_context_methods, + 0, + 0, + NULL, + NULL, + (descrgetfunc)0, + (descrsetfunc)0, + 0, + (initproc)pyg_option_context_init, +}; + +/** + * pyg_option_context_new: + * @context: a GOptionContext + * + * Returns: A new GOptionContext wrapper. + */ +PyObject * +pyg_option_context_new (GOptionContext *context) +{ + PyGOptionContext *self; + + self = (PyGOptionContext *)PyObject_NEW(PyGOptionContext, + &PyGOptionContext_Type); + if (self == NULL) + return NULL; + + self->context = context; + self->main_group = NULL; + + return (PyObject *)self; +} diff --git a/gobject/pygoptiongroup.c b/gobject/pygoptiongroup.c new file mode 100644 index 0000000..4b284c6 --- /dev/null +++ b/gobject/pygoptiongroup.c @@ -0,0 +1,344 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * pygtk- Python bindings for the GTK toolkit. + * Copyright (C) 2006 Johannes Hoelzl + * + * pygoptiongroup.c: GOptionContext and GOptionGroup wrapper + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "pygobject-private.h" + +static gboolean +check_if_owned(PyGOptionGroup *self) +{ + if (self->other_owner) + { + PyErr_SetString(PyExc_ValueError, "The GOptionGroup was not created by " + "gobject.OptionGroup(), so operation is not possible."); + return TRUE; + } + return FALSE; +} + + +static void destroy_g_group(PyGOptionGroup *self) +{ + PyGILState_STATE state; + state = pyg_gil_state_ensure(); + + self->group = NULL; + Py_CLEAR(self->callback); + g_slist_foreach(self->strings, (GFunc) g_free, NULL); + g_slist_free(self->strings); + self->strings = NULL; + + if (self->is_in_context) + { + Py_DECREF(self); + } + + pyg_gil_state_release(state); +} + +static int +pyg_option_group_init(PyGOptionGroup *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "name", "description", "help_description", + "callback", NULL }; + char *name, *description, *help_description; + PyObject *callback; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zzzO:GOptionGroup.__init__", + kwlist, &name, &description, + &help_description, &callback)) + return -1; + + self->group = g_option_group_new(name, description, help_description, + self, (GDestroyNotify) destroy_g_group); + self->other_owner = FALSE; + self->is_in_context = FALSE; + + Py_INCREF(callback); + self->callback = callback; + + return 0; +} + +static void +pyg_option_group_dealloc(PyGOptionGroup *self) +{ + if (!self->other_owner && !self->is_in_context) + { + GOptionGroup *tmp = self->group; + g_assert(tmp != NULL); + self->group = NULL; + g_option_group_free(tmp); + } + + PyObject_Del(self); +} + +static gboolean +arg_func(const gchar *option_name, + const gchar *value, + PyGOptionGroup *self, + GError *error) +{ + PyObject *ret; + PyGILState_STATE state; + + state = pyg_gil_state_ensure(); + + if (value == NULL) + ret = PyObject_CallFunction(self->callback, "sOO", + option_name, Py_None, self); + else + ret = PyObject_CallFunction(self->callback, "ssO", + option_name, value, self); + + if (ret == NULL) + PyErr_Print(); + + pyg_gil_state_release(state); + + if (ret == NULL) + return FALSE; + + Py_DECREF(ret); + return TRUE; +} + +static PyObject * +pyg_option_group_add_entries(PyGOptionGroup *self, PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = { "entries", NULL }; + gssize entry_count, pos; + PyObject *entry_tuple, *list; + GOptionEntry *entries; + + if (check_if_owned(self)) return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:GOptionGroup.add_entries", + kwlist, &list)) + return NULL; + + if (!PyList_Check(list)) + { + PyErr_SetString(PyExc_TypeError, + "GOptionGroup.add_entries expected a list of entries"); + return NULL; + } + + entry_count = PyList_Size(list); + if (entry_count == -1) + { + PyErr_SetString(PyExc_TypeError, + "GOptionGroup.add_entries expected a list of entries"); + return NULL; + } + + entries = g_new0(GOptionEntry, entry_count + 1); + for (pos = 0; pos < entry_count; pos++) + { + gchar *long_name, *description, *arg_description; + entry_tuple = PyList_GetItem(list, pos); + if (!PyTuple_Check(entry_tuple)) + { + PyErr_SetString(PyExc_TypeError, "GOptionGroup.add_entries " + "expected a list of entries"); + g_free(entries); + return NULL; + } + if (!PyArg_ParseTuple(entry_tuple, "scisz", + &long_name, + &(entries[pos].short_name), + &(entries[pos].flags), + &description, + &arg_description)) + { + PyErr_SetString(PyExc_TypeError, "GOptionGroup.add_entries " + "expected a list of entries"); + g_free(entries); + return NULL; + } + long_name = g_strdup(long_name); + self->strings = g_slist_prepend(self->strings, long_name); + entries[pos].long_name = long_name; + + description = g_strdup(description); + self->strings = g_slist_prepend(self->strings, description); + entries[pos].description = description; + + arg_description = g_strdup(arg_description); + self->strings = g_slist_prepend(self->strings, arg_description); + entries[pos].arg_description = arg_description; + + entries[pos].arg = G_OPTION_ARG_CALLBACK; + entries[pos].arg_data = arg_func; + } + + g_option_group_add_entries(self->group, entries); + + g_free(entries); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +pyg_option_group_set_translation_domain(PyGOptionGroup *self, + PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = { "domain", NULL }; + char *domain; + if (check_if_owned(self)) return NULL; + + if (self->group == NULL) + { + PyErr_SetString(PyExc_RuntimeError, + "The corresponding GOptionGroup was already freed, " + "probably through the release of GOptionContext"); + return NULL; + } + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "z:GOptionGroup.set_translate_domain", + kwlist, &domain)) + return NULL; + g_option_group_set_translation_domain(self->group, domain); + Py_INCREF(Py_None); + return Py_None; +} + +static int +pyg_option_group_compare(PyGOptionGroup *self, PyGOptionGroup *group) +{ + if (self->group == group->group) return 0; + if (self->group > group->group) + return 1; + return -1; +} + +static PyMethodDef pyg_option_group_methods[] = { + { "add_entries", (PyCFunction)pyg_option_group_add_entries, METH_VARARGS | METH_KEYWORDS }, + { "set_translation_domain", (PyCFunction)pyg_option_group_set_translation_domain, METH_VARARGS | METH_KEYWORDS }, + { NULL, NULL, 0 }, +}; + +PyTypeObject PyGOptionGroup_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "gobject.OptionGroup", + sizeof(PyGOptionGroup), + 0, + /* methods */ + (destructor)pyg_option_group_dealloc, + (printfunc)0, + (getattrfunc)0, + (setattrfunc)0, + (cmpfunc)pyg_option_group_compare, + (reprfunc)0, + 0, + 0, + 0, + (hashfunc)0, + (ternaryfunc)0, + (reprfunc)0, + (getattrofunc)0, + (setattrofunc)0, + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + NULL, + (traverseproc)0, + (inquiry)0, + (richcmpfunc)0, + 0, + (getiterfunc)0, + (iternextfunc)0, + pyg_option_group_methods, + 0, + 0, + NULL, + NULL, + (descrgetfunc)0, + (descrsetfunc)0, + 0, + (initproc)pyg_option_group_init, +}; + +/** + * pyg_option_group_transfer_group: + * @group: a GOptionGroup wrapper + * + * This is used to transfer the GOptionGroup to a GOptionContext. After this + * is called, the calle must handle the release of the GOptionGroup. + * + * When #NULL is returned, the GOptionGroup was already transfered. + * + * Returns: Either #NULL or the wrapped GOptionGroup. + */ +GOptionGroup * +pyg_option_group_transfer_group(PyGOptionGroup *self) +{ + if (self->is_in_context) return NULL; + self->is_in_context = TRUE; + + /* Here we increase the reference count of the PyGOptionGroup, because now + * the GOptionContext holds an reference to us (it is the userdata passed + * to g_option_group_new(). + * + * The GOptionGroup is freed with the GOptionContext. + * + * We set it here because if we would do this in the init method we would + * hold two references and the PyGOptionGroup would never be freed. + */ + Py_INCREF(self); + + return self->group; +} + +/** + * pyg_option_group_new: + * @group: a GOptionGroup + * + * The returned GOptionGroup can't be used to set any hooks, translation domains + * or add entries. It's only intend is, to use for GOptionContext.add_group(). + * + * Returns: the GOptionGroup wrapper. + */ +PyObject * +pyg_option_group_new (GOptionGroup *group) +{ + PyGOptionGroup *self; + + self = (PyGOptionGroup *)PyObject_NEW(PyGOptionGroup, + &PyGOptionGroup_Type); + if (self == NULL) + return NULL; + + self->group = group; + self->other_owner = TRUE; + self->is_in_context = FALSE; + + return (PyObject *)self; +} |
