/* Python plug-in for dia * Copyright (C) 1999 James Henstridge * Copyright (C) 2000-2005 Hans Breuer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "pydia-object.h" #include "pydia-properties.h" #include "pydia-geometry.h" #include "pydia-font.h" #include "pydia-color.h" #include "pydia-text.h" /* include mess: propinternals.h needs tree.h, we don't */ #include "prop_inttypes.h" #include "prop_geomtypes.h" #include "prop_attr.h" #include "prop_text.h" #include "prop_sdarray.h" /* * New */ PyObject* PyDiaProperty_New (Property* property) { PyDiaProperty *self; self = PyObject_NEW(PyDiaProperty, &PyDiaProperty_Type); if (!self) return NULL; self->property = property->ops->copy (property); return (PyObject *)self; } /* * Dealloc */ static void PyDiaProperty_Dealloc(PyDiaProperty *self) { self->property->ops->free(self->property); PyObject_DEL(self); } /* * Compare */ static int PyDiaProperty_Compare(PyDiaProperty *self, PyDiaProperty *other) { /* ??? */ return memcmp(&(self->property), &(other->property), sizeof(Property)); } /* * Hash */ static long PyDiaProperty_Hash(PyObject *self) { return (long)self; } /* * Still not convinced that this is better than an integral * property->type and some casting ... * It is trading a straightforward 40 lines switch statement to * this nice 'type safe' function mapping (about 125 lines) * --hb */ typedef PyObject * (*PyDiaPropGetFunc) (Property*); typedef int (*PyDiaPropSetFunc) (Property*, PyObject *val); static PyObject * PyDia_get_Char (CharProperty *prop) { return PyInt_FromLong(prop->char_data); } static PyObject * PyDia_get_Bool (BoolProperty *prop) { return PyInt_FromLong(prop->bool_data); } static PyObject * PyDia_get_Int (IntProperty *prop) { return PyInt_FromLong(prop->int_data); } static PyObject * PyDia_get_IntArray (IntarrayProperty *prop) { PyObject *ret; int i, num; num = prop->intarray_data->len; ret = PyTuple_New (num); for (i = 0; i < num; i++) PyTuple_SetItem(ret, i, PyInt_FromLong(g_array_index(prop->intarray_data, gint, i))); return ret; } static PyObject * PyDia_get_Enum (EnumProperty *prop) { return PyInt_FromLong(prop->enum_data); } static PyObject * PyDia_get_LineStyle (LinestyleProperty *prop) { PyObject *ret; ret = PyTuple_New (2); PyTuple_SetItem(ret, 0, PyInt_FromLong(prop->style)); PyTuple_SetItem(ret, 1, PyFloat_FromDouble(prop->dash)); return ret; } static PyObject * PyDia_get_Real (RealProperty *prop) { return PyFloat_FromDouble(prop->real_data); } static PyObject * PyDia_get_Length (LengthProperty *prop) { return PyFloat_FromDouble(prop->length_data); } static PyObject * PyDia_get_Fontsize (FontsizeProperty *prop) { return PyFloat_FromDouble(prop->fontsize_data); } static PyObject * PyDia_get_String (StringProperty *prop) { if (NULL == prop->string_data) return PyString_FromString("(NULL)"); else if (1 == prop->num_lines) return PyString_FromString(prop->string_data); else /* FIXME: MULTISTRING ? */ return PyString_FromString(prop->string_data); } static PyObject * PyDia_get_StringList (StringListProperty *prop) { GList *tmp; PyObject *ret = PyList_New(0); for (tmp = prop->string_list; tmp; tmp = tmp->next) PyList_Append(ret, PyString_FromString(tmp->data)); return ret; } static PyObject * PyDia_get_Text (TextProperty *prop) { return PyDiaText_New (prop->text_data, &prop->attr); } static PyObject * PyDia_get_Point (PointProperty *prop) { return PyDiaPoint_New (&prop->point_data); } static PyObject * PyDia_get_PointArray (PointarrayProperty *prop) { PyObject *ret; int i, num; num = prop->pointarray_data->len; ret = PyTuple_New (num); for (i = 0; i < num; i++) PyTuple_SetItem(ret, i, PyDiaPoint_New(&g_array_index(prop->pointarray_data, Point, i))); return ret; } static PyObject * PyDia_get_BezPoint (BezPointProperty *prop) { return PyDiaBezPoint_New (&prop->bezpoint_data); } static PyObject * PyDia_get_BezPointArray (BezPointarrayProperty *prop) { PyObject *ret; int i, num; num = prop->bezpointarray_data->len; ret = PyTuple_New (num); for (i = 0; i < num; i++) PyTuple_SetItem(ret, i, PyDiaBezPoint_New(&g_array_index(prop->bezpointarray_data, BezPoint, i))); return ret; } static PyObject * PyDia_get_Rect (RectProperty *prop) { return PyDiaRectangle_New (&prop->rect_data, NULL); } static PyObject * PyDia_get_Arrow (ArrowProperty *prop) { return PyDiaArrow_New (&prop->arrow_data); } static PyObject * PyDia_get_Color (ColorProperty *prop) { return PyDiaColor_New (&prop->color_data); } static PyObject * PyDia_get_Font (FontProperty *prop) { return PyDiaFont_New (prop->font_data); } static PyObject * PyDia_get_Array (ArrayProperty *prop); static int PyDia_set_Bool (Property *prop, PyObject *val) { BoolProperty *p = (BoolProperty *)prop; if (PyInt_Check(val)) { p->bool_data = !!PyInt_AsLong(val); return 0; } return -1; } static int PyDia_set_Int (Property *prop, PyObject *val) { IntProperty *p = (IntProperty *)prop; if (PyInt_Check(val)) { p->int_data = PyInt_AsLong(val); return 0; } return -1; } static int PyDia_set_IntArray(Property *prop, PyObject *val) { IntarrayProperty *p = (IntarrayProperty *)prop; if (PyTuple_Check(val)) { int i, len = PyTuple_Size(val); g_array_set_size(p->intarray_data, len); for (i = 0; i < len; i++) { PyObject *o = PyTuple_GetItem(val, i); g_array_index(p->intarray_data, gint, i) = PyInt_Check(o) ? PyInt_AsLong(o) : 0; } return 0; } else if (PyList_Check(val)) { int i, len = PyList_Size(val); g_array_set_size(p->intarray_data, len); for (i = 0; i < len; i++) { PyObject *o = PyList_GetItem(val, i); g_array_index(p->intarray_data, gint, i) = PyInt_Check(o) ? PyInt_AsLong(o) : 0; } return 0; } return -1; } static int PyDia_set_Enum (Property *prop, PyObject *val) { EnumProperty *p = (EnumProperty *)prop; /* XXX a range check would not hurt */ if (PyInt_Check(val)) { p->enum_data = PyInt_AsLong(val); return 0; } return -1; } static int PyDia_set_Color (Property *prop, PyObject *val) { ColorProperty *p = (ColorProperty*)prop; if (PyString_Check(val)) { gchar *str = PyString_AsString(val); PangoColor color; if (pango_color_parse(&color, str)) { p->color_data.red = color.red / 65535.0; p->color_data.green = color.green / 65535.0; p->color_data.blue = color.blue / 65535.0; return 0; } else g_warning("Failed to parse color string '%s'", str); } else if (PyTuple_Check (val)) { int i, len = PyTuple_Size(val); real f[3]; if (len < 3) return -1; for (i = 0; i < 3; i++) { PyObject *o = PyTuple_GetItem(val, i); if (PyFloat_Check(o)) f[i] = PyFloat_AsDouble(o); else if (PyInt_Check(o)) f[i] = PyInt_AsLong(o) / 65535.; else f[i] = 0.0; } p->color_data.red = f[0]; p->color_data.green = f[1]; p->color_data.blue = f[2]; return 0; } /* also convert char/255 ? */ return -1; } static int PyDia_set_LineStyle(Property *prop, PyObject *val) { LinestyleProperty *p = (LinestyleProperty*)prop; if (PyTuple_Check(val) && PyTuple_Size(val) == 2) { p->style = PyInt_AsLong(PyTuple_GetItem(val, 0)); p->dash = PyFloat_Check(PyTuple_GetItem(val, 1)) ? PyFloat_AsDouble(PyTuple_GetItem(val, 1)) : PyInt_AsLong(PyTuple_GetItem(val, 1)); return 0; } return -1; } static int PyDia_set_Real(Property *prop, PyObject *val) { RealProperty *p = (RealProperty *)prop; if (PyFloat_Check(val)) { p->real_data = PyFloat_AsDouble(val); return 0; } else if (PyInt_Check(val)) { /* be tolerant for up-casting */ p->real_data = PyInt_AsLong(val); return 0; } return -1; } /* as of this writing the only difference between Real-, Length- and Fontsize-property * is the widget representing them. But that may change so here are the 'type-safe' * accessors. */ static int PyDia_set_Length(Property *prop, PyObject *val) { LengthProperty *p = (LengthProperty *)prop; if (PyFloat_Check(val)) { p->length_data = PyFloat_AsDouble(val); return 0; } else if (PyInt_Check(val)) { /* be tolerant for up-casting */ p->length_data = PyInt_AsLong(val); return 0; } return -1; } static int PyDia_set_Fontsize(Property *prop, PyObject *val) { FontsizeProperty *p = (FontsizeProperty *)prop; if (PyFloat_Check(val)) { p->fontsize_data = PyFloat_AsDouble(val); return 0; } else if (PyInt_Check(val)) { /* be tolerant for up-casting */ p->fontsize_data = PyInt_AsLong(val); return 0; } return -1; } static int PyDia_set_String(Property *prop, PyObject *val) { StringProperty *p = (StringProperty *)prop; if (Py_None == val) { /* XXX: maybe a little dangerous, string = NULL not handle in everystring prop */ g_free (p->string_data); p->string_data = NULL; p->num_lines = 0; return 0; } else if (PyString_Check (val)) { gchar *str = PyString_AsString (val); g_free (p->string_data); p->string_data = g_strdup (str); p->num_lines = 1; return 0; } else if (PyUnicode_Check (val)) { PyObject *uval = PyUnicode_AsUTF8String (val); gchar *str = PyString_AsString(uval); g_free (p->string_data); p->string_data = g_strdup (str); p->num_lines = 1; Py_DECREF (uval); return 0; } return -1; } static int PyDia_set_Text(Property *prop, PyObject *val) { TextProperty *p = (TextProperty *)prop; if (PyString_Check (val)) { gchar *str = PyString_AsString (val); g_free (p->text_data); p->text_data = g_strdup (str); /* XXX: update size calculation ? */ return 0; } else if (PyUnicode_Check (val)) { PyObject *uval = PyUnicode_AsUTF8String (val); gchar *str = PyString_AsString(uval); g_free (p->text_data); p->text_data = g_strdup (str); Py_DECREF (uval); return 0; } return -1; } static int PyDia_set_Point(Property *prop, PyObject *val) { PointProperty *p = (PointProperty*)prop; if (PyTuple_Check(val) && PyTuple_Size(val) == 2) { p->point_data.x = PyFloat_AsDouble(PyTuple_GetItem(val, 0)); p->point_data.y = PyFloat_AsDouble(PyTuple_GetItem(val, 1)); return 0; } return -1; } static int PyDia_set_PointArray(Property *prop, PyObject *val) { /* accept either tuple or list */ PointarrayProperty *ptp = (PointarrayProperty *)prop; if (PyTuple_Check(val) || PyList_Check(val)) { Point pt; gboolean is_list = !PyTuple_Check(val); int i, len = is_list ? PyList_Size(val) : PyTuple_Size(val); gboolean is_flat = TRUE; g_array_set_size(ptp->pointarray_data,len); for (i = 0; i < len; i++) { PyObject *o = is_list ? PyList_GetItem(val, i) : PyTuple_GetItem(val, i); if (PyTuple_Check(o)) { pt.x = PyFloat_AsDouble(PyTuple_GetItem(o, 0)); pt.y = PyFloat_AsDouble(PyTuple_GetItem(o, 1)); g_array_index(ptp->pointarray_data,Point,i) = pt; is_flat = FALSE; } else { if (i % 2) { pt.x = PyFloat_AsDouble(PyTuple_GetItem(val, i-1)); pt.y = PyFloat_AsDouble(PyTuple_GetItem(val, i)); g_array_index(ptp->pointarray_data,Point,i/2) = pt; } } } if (is_flat) g_array_set_size(ptp->pointarray_data,len/2); return 0; } return -1; } static int PyDia_set_BezPointArray(Property *prop, PyObject *val) { /* accept either tuple or list */ BezPointarrayProperty *ptp = (BezPointarrayProperty *)prop; if (PyTuple_Check(val) || PyList_Check(val)) { BezPoint bpt; gboolean is_list = !PyTuple_Check(val); int i, len = is_list ? PyList_Size(val) : PyTuple_Size(val); g_array_set_size(ptp->bezpointarray_data,len); for (i = 0; i < len; i++) { /* a tuple of at least (int,double,double) */ PyObject *o = is_list ? PyList_GetItem(val, i) : PyTuple_GetItem(val, i); int tp = PyInt_AsLong(PyTuple_GetItem(o, 0)); bpt.p1.x = PyFloat_AsDouble(PyTuple_GetItem(o, 1)); bpt.p1.y = PyFloat_AsDouble(PyTuple_GetItem(o, 2)); if (BEZ_CURVE_TO == tp) { bpt.type = BEZ_CURVE_TO; bpt.p2.x = PyFloat_AsDouble(PyTuple_GetItem(o, 3)); bpt.p2.y = PyFloat_AsDouble(PyTuple_GetItem(o, 4)); bpt.p3.x = PyFloat_AsDouble(PyTuple_GetItem(o, 5)); bpt.p3.y = PyFloat_AsDouble(PyTuple_GetItem(o, 6)); } else { if (0 == i && tp != BEZ_MOVE_TO) g_warning("First bezpoint must be BEZ_MOVE_TO"); if (0 < i && tp != BEZ_LINE_TO) g_warning("Further bezpoint must be BEZ_LINE_TO or BEZ_CURVE_TO"); bpt.type = (0 == i) ? BEZ_MOVE_TO : BEZ_LINE_TO; /* not strictly needed */ bpt.p2 = bpt.p3 = bpt.p1; } g_array_index(ptp->bezpointarray_data,BezPoint,i) = bpt; } return 0; } return -1; } static int PyDia_set_Rect(Property *prop, PyObject *val) { RectProperty *p = (RectProperty*)prop; if (PyTuple_Check(val) && PyTuple_Size(val) == 4) { p->rect_data.left = PyFloat_AsDouble(PyTuple_GetItem(val, 0)); p->rect_data.top = PyFloat_AsDouble(PyTuple_GetItem(val, 1)); p->rect_data.right = PyFloat_AsDouble(PyTuple_GetItem(val, 2)); p->rect_data.bottom = PyFloat_AsDouble(PyTuple_GetItem(val, 3)); return 0; } return -1; } static int PyDia_set_Array(Property*, PyObject*); struct { char *type; PyObject *(*propget)(); int (*propset)(Property*, PyObject*); GQuark quark; } prop_type_map [] = { { PROP_TYPE_CHAR, PyDia_get_Char }, { PROP_TYPE_BOOL, PyDia_get_Bool, PyDia_set_Bool }, { PROP_TYPE_INT, PyDia_get_Int, PyDia_set_Int }, { PROP_TYPE_INTARRAY, PyDia_get_IntArray, PyDia_set_IntArray }, { PROP_TYPE_ENUM, PyDia_get_Enum, PyDia_set_Enum }, { PROP_TYPE_ENUMARRAY, PyDia_get_IntArray, PyDia_set_IntArray }, /* Enum == Int */ { PROP_TYPE_LINESTYLE, PyDia_get_LineStyle, PyDia_set_LineStyle }, { PROP_TYPE_REAL, PyDia_get_Real, PyDia_set_Real }, { PROP_TYPE_LENGTH, PyDia_get_Length, PyDia_set_Length }, { PROP_TYPE_FONTSIZE, PyDia_get_Fontsize, PyDia_set_Fontsize }, { PROP_TYPE_STRING, PyDia_get_String, PyDia_set_String }, { PROP_TYPE_STRINGLIST, PyDia_get_StringList }, { PROP_TYPE_FILE, PyDia_get_String, PyDia_set_String }, { PROP_TYPE_MULTISTRING, PyDia_get_String }, { PROP_TYPE_TEXT, PyDia_get_Text, PyDia_set_Text }, { PROP_TYPE_POINT, PyDia_get_Point, PyDia_set_Point }, { PROP_TYPE_POINTARRAY, PyDia_get_PointArray, PyDia_set_PointArray }, { PROP_TYPE_BEZPOINT, PyDia_get_BezPoint }, { PROP_TYPE_BEZPOINTARRAY, PyDia_get_BezPointArray, PyDia_set_BezPointArray }, { PROP_TYPE_RECT, PyDia_get_Rect, PyDia_set_Rect }, { PROP_TYPE_ARROW, PyDia_get_Arrow }, { PROP_TYPE_COLOUR, PyDia_get_Color, PyDia_set_Color }, { PROP_TYPE_FONT, PyDia_get_Font }, { PROP_TYPE_SARRAY, PyDia_get_Array, PyDia_set_Array }, { PROP_TYPE_DARRAY, PyDia_get_Array, PyDia_set_Array } }; static void ensure_quarks(void) { static gboolean type_quarks_calculated = FALSE; int i; if (!type_quarks_calculated) { for (i = 0; i < G_N_ELEMENTS(prop_type_map); i++) { prop_type_map[i].quark = g_quark_from_string (prop_type_map[i].type); } type_quarks_calculated = TRUE; } } static PyObject * PyDia_get_Array (ArrayProperty *prop) { PyObject *ret; int num, num_props; num_props = prop->ex_props->len; num = prop->records->len; ret = PyTuple_New (num); /* fill it with tuples or single types */ if (num > 0) { PyDiaPropGetFunc *getters = g_new0(PyDiaPropGetFunc, num_props); int i; /* resolve the getter functions once */ for (i = 0; i < num_props; i++) { int j; for (j = 0; j < G_N_ELEMENTS(prop_type_map); j++) { Property *inner = g_ptr_array_index(prop->ex_props, i); if (prop_type_map[j].quark == inner->type_quark) getters[i] = (PyDiaPropGetFunc)prop_type_map[j].propget; } } for (i = 0; i < num; i++) { PyObject *o; GPtrArray *p = g_ptr_array_index(prop->records, i); int j = 0; if (1 == num_props) { Property *sub = g_ptr_array_index(p,j); o = getters[j](sub); } else { o = PyTuple_New (num_props); for (j = 0; j < num_props; j++) { Property *sub = g_ptr_array_index(p,j); PyTuple_SetItem(o, j, getters[j](sub)); } } PyTuple_SetItem(ret, i, o); } g_free(getters); } return ret; } static int PyDia_set_Array (Property *prop, PyObject *val) { ArrayProperty *p = (ArrayProperty *)prop; guint i, num_props = p->ex_props->len; PyDiaPropSetFunc *setters = g_new0(PyDiaPropSetFunc, num_props); int ret = 0; /* resolve the getter functions once */ for (i = 0; i < num_props; i++) { Property *ex = g_ptr_array_index(p->ex_props, i); int j; for (j = 0; j < G_N_ELEMENTS(prop_type_map); j++) { if (prop_type_map[j].quark == ex->type_quark) setters[i] = (PyDiaPropSetFunc)prop_type_map[j].propset; } if (!setters[i]) { g_warning("No setter for '%s'", ex->type); g_free(setters); return -1; } } /* tuple or list containing tuples */ if (PyTuple_Check(val) || PyList_Check(val)) { gboolean is_list = !PyTuple_Check(val); guint len = is_list ? PyList_Size(val) : PyTuple_Size(val); for (i = 0; i < p->records->len; i++) { GPtrArray *record = g_ptr_array_index(p->records, i); guint j; for (j = 0; j < num_props; j++) { Property *inner =g_ptr_array_index(record,j); inner->ops->free(inner); } g_ptr_array_free(record, TRUE); } g_ptr_array_set_size(p->records, 0); for (i = 0; i < len; i++) { PyObject *t = is_list ? PyList_GetItem(val, i) : PyTuple_GetItem(val, i); GPtrArray *record = g_ptr_array_new(); guint j; if (!PyTuple_Check(t) || PyTuple_Size(t) != num_props) { g_warning("PyDia_set_Array: %s.", !PyTuple_Check(t) ? "no tuple" : " wrong size"); ret = -1; break; } g_ptr_array_set_size(record, 0); for (j = 0; j < num_props; j++) { Property *ex = g_ptr_array_index(p->ex_props, j); Property *inner = ex->ops->copy(ex); PyObject *v = PyTuple_GetItem(t, j); if (0 != setters[j] (inner, v)) { if (Py_None == v) { /* use just the defaults, setters don't need to handle this */ } else { g_warning ("Failed to set '%s::%s' from '%s'", p->common.name, inner->name, v->ob_type->tp_name); inner->ops->free(inner); ret = -1; break; } } g_ptr_array_add(record, inner); } g_ptr_array_add(p->records, record); if (ret != 0) break; } } g_free(setters); return ret; } /* * GetAttr */ static PyObject * PyDiaProperty_GetAttr(PyDiaProperty *self, gchar *attr) { if (!strcmp(attr, "__members__")) return Py_BuildValue("[ssss]", "name", "type", "value", "visible"); else if (!strcmp(attr, "name")) return PyString_FromString(self->property->name); else if (!strcmp(attr, "type")) return PyString_FromString(self->property->type); else if (!strcmp(attr, "visible")) return PyInt_FromLong(0 != (self->property->descr->flags & PROP_FLAG_VISIBLE)); else if (!strcmp(attr, "value")) { int i; ensure_quarks(); for (i = 0; i < G_N_ELEMENTS(prop_type_map); i++) if (prop_type_map[i].quark == self->property->type_quark) return prop_type_map[i].propget(self->property); if (0 == (PROP_FLAG_WIDGET_ONLY & self->property->descr->flags)) g_warning ("No handler for type '%s'", self->property->type); Py_INCREF(Py_None); return Py_None; } PyErr_SetString(PyExc_AttributeError, attr); return NULL; } /* * Similar to SetAttr but the property is directly applied * to the DiaObject */ int PyDiaProperty_ApplyToObject (DiaObject *object, gchar *key, Property *prop, PyObject *val) { int ret = -1; if PyDiaProperty_Check(val) { /* must be a Property object ? Or PyDiaRect etc ? */ Property* inprop = ((PyDiaProperty*)val)->property; if (0 == strcmp (prop->type, inprop->type)) { GPtrArray *plist; /* apply it */ prop->ops->free (prop); /* release this one */ prop = inprop->ops->copy(inprop); /* apply property to object */ plist = prop_list_from_single (prop); object->ops->set_props(object, plist); prop_list_free (plist); return 0; } else { g_warning("PyDiaProperty_ApplyToObject : no property conversion %s -> %s", inprop->type, prop->type); } } else { int i; ensure_quarks(); for (i = 0; i < G_N_ELEMENTS(prop_type_map); i++) { if (prop_type_map[i].quark == prop->type_quark) { if (!prop_type_map[i].propset) g_warning("Setter for '%s' not implemented.", prop_type_map[i].type); else if (0 == prop_type_map[i].propset(prop, val)) ret = 0; break; } } if (ret != 0) g_warning("PyDiaProperty_ApplyToObject : no conversion %s -> %s", key, prop->type); } if (0 == ret) { /* apply property to object */ GPtrArray *plist = prop_list_from_single (prop); object->ops->set_props(object, plist); prop_list_free (plist); } return ret; } /* * Repr / _Str */ static PyObject * PyDiaProperty_Str(PyDiaProperty *self) { PyObject* py_s; gchar* s; s = g_strdup_printf("", self, self->property->name, self->property->type); py_s = PyString_FromString(s); g_free (s); return py_s; } /* * Python object */ PyTypeObject PyDiaProperty_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "dia.Property", sizeof(PyDiaProperty), 0, (destructor)PyDiaProperty_Dealloc, (printfunc)0, (getattrfunc)PyDiaProperty_GetAttr, (setattrfunc)0, (cmpfunc)PyDiaProperty_Compare, (reprfunc)0, 0, 0, 0, (hashfunc)PyDiaProperty_Hash, (ternaryfunc)0, (reprfunc)PyDiaProperty_Str, (getattrofunc)0, (setattrofunc)0, (PyBufferProcs *)0, 0L, /* Flags */ "Interface to so called StdProps, the mechanism to control " "most of Dia's canvas objects properties. " };