1/* -*- Mode: C; c-basic-offset: 4 -*-
2 * pygtk- Python bindings for the GTK toolkit.
3 * Copyright (C) 1998-2003  James Henstridge
4 *
5 *   gdkcolor.override: gtk.gdk.Color and gtk.gdk.ColorMap overrides
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22%%
23ignore
24  gdk_color_copy gdk_color_free
25  gdk_color_hash gdk_color_equal
26%%
27override gdk_color_new kwargs
28static int
29_wrap_gdk_color_new(PyGBoxed *self,
30                    PyObject *args,
31                    PyObject *kwargs)
32{
33    static char *kwlist1[] = {"red", "green", "blue", "pixel", NULL };
34    static char *kwlist2[] = { "spec", NULL };
35    PyObject *red = Py_None, *green = Py_None, *blue = Py_None;
36    const char *spec = NULL;
37    GdkColor colour;
38
39    /* Note: this constructor has become quite complicated, because it
40     * is heavily overloaded.  Additionally, we try to optimize a
41     * little. */
42
43    if (PyArg_ParseTupleAndKeywords(args, kwargs, "|s:gdk.Color", kwlist2,
44                                    &spec)) {
45        if (!spec)
46            memset(&colour, 0, sizeof(colour));
47        else if (!gdk_color_parse(spec, &colour)) {
48            PyErr_SetString(PyExc_ValueError,
49                            "unable to parse colour specification");
50            return -1;
51        }
52
53        goto success;
54    }
55
56    PyErr_Clear();
57
58    if (PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOk:gdk.Color", kwlist1,
59                                    &red, &green, &blue, &colour.pixel)) {
60        /* We don't allow mixing floats and non-floats as that is too
61         * error-prone.  All non-floats are deemed integers in case
62         * they have __int__() method. */
63        int have_floats = 0;
64        int have_nonfloats = 0;
65
66        if (red == Py_None)
67            colour.red = 0;
68        else {
69            if (PyFloat_Check(red)) {
70                have_floats = 1;
71                colour.red = MIN(MAX(0.0, PyFloat_AsDouble(red)), 1.0) * 65535.0;
72            }
73            else {
74                have_nonfloats = 1;
75                colour.red = PyInt_AsLong(red);
76            }
77        }
78
79        if (PyErr_Occurred())
80            return -1;
81
82        if (green == Py_None)
83            colour.green = 0;
84        else {
85            if (PyFloat_Check(green)) {
86                if (have_nonfloats)
87                    goto mixed_types_error;
88                have_floats = 1;
89                colour.green = MIN(MAX(0.0, PyFloat_AsDouble(green)), 1.0) * 65535.0;
90            }
91            else {
92                if (have_floats)
93                    goto mixed_types_error;
94                have_nonfloats = 1;
95                colour.green = PyInt_AsLong(green);
96            }
97        }
98
99        if (PyErr_Occurred())
100            return -1;
101
102        if (blue == Py_None)
103            colour.blue = 0;
104        else {
105            if (PyFloat_Check(blue)) {
106                if (have_nonfloats)
107                    goto mixed_types_error;
108                colour.blue = MIN(MAX(0.0, PyFloat_AsDouble(blue)), 1.0) * 65535.0;
109            }
110            else {
111                if (have_floats)
112                    goto mixed_types_error;
113                colour.blue = PyInt_AsLong(blue);
114            }
115        }
116
117        if (PyErr_Occurred())
118            return -1;
119
120        goto success;
121
122    mixed_types_error:
123        PyErr_SetString(PyExc_TypeError, "arguments must either be all integers or all floats");
124        return -1;
125    }
126
127    PyErr_Clear();
128    PyErr_SetString(PyExc_TypeError, "Usage:\n"
129                    "  gtk.gdk.Color(red, green, blue, pixel)  [all are optional]\n"
130                    "  gtk.gdk.Color(spec)                     [see gtk.gdk.color_parse()]");
131    return -1;
132
133 success:
134    self->boxed =  g_boxed_copy(GDK_TYPE_COLOR, &colour);
135    self->free_on_dealloc = TRUE;
136    self->gtype = GDK_TYPE_COLOR;
137
138    return 0;
139}
140
141%%
142define color_from_hsv kwargs
143static PyObject *
144_wrap_color_from_hsv (PyObject *ignored, PyObject *args, PyObject*kwargs)
145{
146    static char *kwlist[] = { "hue", "saturation", "value", NULL };
147    gdouble hue, saturation, value;
148    gdouble red, green, blue;
149    GdkColor color;
150
151    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ddd:gtk.gdk.color_from_hsv", kwlist,
152                                     &hue, &saturation, &value))
153        return NULL;
154
155    /* See documentation of the class for rationale. */
156
157    hue -= floor(hue);
158
159    if (saturation > 1.0)
160        saturation = 1.0;
161    else if (saturation < 0.0)
162        saturation = 0.0;
163
164    if (value > 1.0)
165        value = 1.0;
166    else if (value < 0.0)
167        value = 0.0;
168
169    gtk_hsv_to_rgb(hue, saturation, value,
170                   &red, &green, &blue);
171
172    color.red = red * 65535.0;
173    color.green = green * 65535.0;
174    color.blue = blue * 65535.0;
175
176    return pyg_boxed_new(GDK_TYPE_COLOR, &color, TRUE, TRUE);
177}
178
179%%
180override-attr GdkColor.red
181static int
182_wrap_gdk_color__set_red(PyObject *self, PyObject *value, void *closure)
183{
184    long red = PyInt_AsLong(value);
185    if (red == -1 && PyErr_Occurred())
186        return -1;
187    else {
188        pyg_boxed_get(self, GdkColor)->red = red;
189        return 0;
190    }
191}
192%%
193override-attr GdkColor.blue
194static int
195_wrap_gdk_color__set_blue(PyObject *self, PyObject *value, void *closure)
196{
197    long blue = PyInt_AsLong(value);
198    if (blue == -1 && PyErr_Occurred())
199        return -1;
200    else {
201        pyg_boxed_get(self, GdkColor)->blue = blue;
202        return 0;
203    }
204}
205%%
206override-attr GdkColor.green
207static int
208_wrap_gdk_color__set_green(PyObject *self, PyObject *value, void *closure)
209{
210    long green = PyInt_AsLong(value);
211    if (green == -1 && PyErr_Occurred())
212        return -1;
213    else {
214        pyg_boxed_get(self, GdkColor)->green = green;
215        return 0;
216    }
217}
218%%
219override-attr GdkColor.pixel
220static int
221_wrap_gdk_color__set_pixel(PyObject *self, PyObject *value, void *closure)
222{
223    long pixel = PyInt_AsLong(value);
224    if (pixel == -1 && PyErr_Occurred())
225        return -1;
226    else {
227        pyg_boxed_get(self, GdkColor)->pixel = pixel;
228        return 0;
229    }
230}
231%%
232override-attr GdkColor.red_float
233
234static PyObject *
235_wrap_gdk_color__get_red_float(PyObject *self, void *closure)
236{
237    return PyFloat_FromDouble(pyg_boxed_get(self, GdkColor)->red / 65535.0);
238}
239
240static int
241_wrap_gdk_color__set_red_float(PyObject *self, PyObject *value, void *closure)
242{
243    double red = PyFloat_AsDouble(value);
244    if (red == -1 && PyErr_Occurred())
245        return -1;
246    else {
247        pyg_boxed_get(self, GdkColor)->red = MIN(MAX(0.0, red), 1.0) * 65535.0;
248        return 0;
249    }
250}
251%%
252override-attr GdkColor.green_float
253
254static PyObject *
255_wrap_gdk_color__get_green_float(PyObject *self, void *closure)
256{
257    return PyFloat_FromDouble(pyg_boxed_get(self, GdkColor)->green / 65535.0);
258}
259
260static int
261_wrap_gdk_color__set_green_float(PyObject *self, PyObject *value, void *closure)
262{
263    double green = PyFloat_AsDouble(value);
264    if (green == -1 && PyErr_Occurred())
265        return -1;
266    else {
267        pyg_boxed_get(self, GdkColor)->green = MIN(MAX(0.0, green), 1.0) * 65535.0;
268        return 0;
269    }
270}
271%%
272override-attr GdkColor.blue_float
273
274static PyObject *
275_wrap_gdk_color__get_blue_float(PyObject *self, void *closure)
276{
277    return PyFloat_FromDouble(pyg_boxed_get(self, GdkColor)->blue / 65535.0);
278}
279
280static int
281_wrap_gdk_color__set_blue_float(PyObject *self, PyObject *value, void *closure)
282{
283    double blue = PyFloat_AsDouble(value);
284    if (blue == -1 && PyErr_Occurred())
285        return -1;
286    else {
287        pyg_boxed_get(self, GdkColor)->blue = MIN(MAX(0.0, blue), 1.0) * 65535.0;
288        return 0;
289    }
290}
291%%
292override-attr GdkColor.hue
293
294static PyObject *
295_wrap_gdk_color__get_hue(PyObject *self, void *closure)
296{
297    GdkColor *color = pyg_boxed_get(self, GdkColor);
298    gdouble red = color->red / 65535.0;
299    gdouble green = color->green / 65535.0;
300    gdouble blue = color->blue / 65535.0;
301    gdouble hue;
302
303    gtk_rgb_to_hsv(red, green, blue, &hue, NULL, NULL);
304    return PyFloat_FromDouble(hue);
305}
306%%
307override-attr GdkColor.saturation
308
309static PyObject *
310_wrap_gdk_color__get_saturation(PyObject *self, void *closure)
311{
312    GdkColor *color = pyg_boxed_get(self, GdkColor);
313    gdouble red = color->red / 65535.0;
314    gdouble green = color->green / 65535.0;
315    gdouble blue = color->blue / 65535.0;
316    gdouble saturation;
317
318    gtk_rgb_to_hsv(red, green, blue, NULL, &saturation, NULL);
319    return PyFloat_FromDouble(saturation);
320}
321%%
322override-attr GdkColor.value
323
324static PyObject *
325_wrap_gdk_color__get_value(PyObject *self, void *closure)
326{
327    GdkColor *color = pyg_boxed_get(self, GdkColor);
328    gdouble red = color->red / 65535.0;
329    gdouble green = color->green / 65535.0;
330    gdouble blue = color->blue / 65535.0;
331    gdouble value;
332
333    gtk_rgb_to_hsv(red, green, blue, NULL, NULL, &value);
334    return PyFloat_FromDouble(value);
335}
336%%
337override gdk_color_parse kwargs
338static PyObject *
339_wrap_gdk_color_parse(PyObject *self, PyObject *args, PyObject *kwargs)
340{
341    static char *kwlist[] = { "spec", NULL };
342    const char *spec;
343    GdkColor colour = { 0, };
344
345    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:color_parse", kwlist,
346                                     &spec))
347        return NULL;
348
349    if (!gdk_color_parse(spec, &colour)) {
350        PyErr_SetString(PyExc_ValueError,
351                        "unable to parse colour specification");
352        return NULL;
353    }
354
355    /* pyg_boxed_new handles NULL checking */
356    return pyg_boxed_new (GDK_TYPE_COLOR, &colour, TRUE, TRUE);
357}
358%%
359override gdk_colormap_alloc_color kwargs
360static PyObject *
361_wrap_gdk_colormap_alloc_color(PyGObject *self, PyObject *args,
362                              PyObject *kwargs)
363{
364    static char *kwlist1[] = { "red", "green", "blue", "writeable", "best_match", NULL };
365    static char *kwlist2[] = { "spec", "writeable", "best_match", NULL };
366    static char *kwlist3[] = { "color", "writeable", "best_match", NULL };
367    GdkColor colour = { 0, 0, 0, 0 };
368    gboolean writeable = FALSE;
369    gboolean best_match = TRUE;
370
371    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
372                                     "HHH|ii:GdkColormap.alloc_color", kwlist1,
373                                     &colour.red, &colour.green, &colour.blue,
374                                     &writeable, &best_match)) {
375
376 	PyObject *pycolour;
377
378        PyErr_Clear();
379        if (!PyArg_ParseTupleAndKeywords(args, kwargs,
380 					 "O|ii:GdkColormap.alloc_color",
381 					 kwlist3, &pycolour,
382 					 &writeable, &best_match)) {
383            return NULL;
384	}
385
386 	if (!pyg_boxed_check(pycolour, GDK_TYPE_COLOR)) {
387 	    gchar *color_name;
388
389 	    PyErr_Clear();
390 	    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
391 					     "s|ii:GdkColormap.alloc_color",
392 					     kwlist2, &color_name, &writeable,
393 					     &best_match)) {
394 		return NULL;
395	    }
396
397 	    if (!gdk_color_parse(color_name, &colour)) {
398 		PyErr_SetString(PyExc_ValueError,
399 				"unable to parse colour specification");
400 		return NULL;
401	    }
402 	} else {
403 	    colour = *pyg_boxed_get(pycolour, GdkColor);
404        }
405    }
406
407    if (!gdk_colormap_alloc_color(GDK_COLORMAP(self->obj),
408                                  &colour, writeable, best_match)) {
409        PyErr_SetString(PyExc_RuntimeError, "couldn't allocate colour");
410        return NULL;
411    }
412    return pyg_boxed_new(GDK_TYPE_COLOR, &colour, TRUE, TRUE);
413}
414%%
415override gdk_color_alloc kwargs
416static PyObject *
417_wrap_gdk_color_alloc(PyGObject *self, PyObject *args, PyObject *kwargs)
418{
419    if (!PyErr_Warn(PyExc_DeprecationWarning, "use GdkColormap.alloc_color")<0)
420        return NULL;
421    return _wrap_gdk_colormap_alloc_color(self, args, kwargs);
422}
423%%
424override-slot GdkColor.tp_richcompare
425static PyObject *
426_wrap_gdk_color_tp_richcompare(PyObject *self, PyObject *other, int op)
427{
428    PyObject *result;
429
430    if (PyObject_TypeCheck(self, &PyGdkColor_Type)
431        && PyObject_TypeCheck(other, &PyGdkColor_Type)) {
432        GdkColor *color1 = pyg_boxed_get(self, GdkColor);
433        GdkColor *color2 = pyg_boxed_get(other, GdkColor);
434
435        switch (op) {
436        case Py_EQ:
437            result = (gdk_color_equal(color1, color2)
438                      ? Py_True : Py_False);
439            break;
440        case Py_NE:
441            result = (!gdk_color_equal(color1, color2)
442                      ? Py_True : Py_False);
443            break;
444        default:
445            result = Py_NotImplemented;
446        }
447    }
448    else
449        result = Py_NotImplemented;
450
451    Py_INCREF(result);
452    return result;
453}
454%%
455override-slot GdkColor.tp_repr
456static int
457pygdk_color_to_string_smart(char *buffer, int length, GdkColor *color)
458{
459    /* We use g_snprintf() because PyString_FromFormat() doesn't support %0Nx-like formats
460     * (i.e. with leading zeros).
461     *
462     * Note that numbers here are used so that there is no off-by-one errors in
463     * 'eval(repr(color))', i.e. so that 'eval(repr(color)) == color'.  See
464     * pango_color_parse() for justification of these numbers.  Three-nibble color
465     * components are deemed unimportant.
466     */
467    if (color->red % 0x1111 == 0 && color->green % 0x1111 == 0 && color->blue % 0x1111 == 0) {
468        return g_snprintf(buffer, length, "#%01x%01x%01x",
469                          color->red / 0x1111, color->green / 0x1111, color->blue / 0x1111);
470    }
471    else if (color->red % 0x0101 == 0 && color->green % 0x0101 == 0 && color->blue % 0x0101 == 0) {
472        return g_snprintf(buffer, length, "#%02x%02x%02x",
473                          color->red / 0x0101, color->green / 0x0101, color->blue / 0x0101);
474    }
475    else {
476        return g_snprintf(buffer, length, "#%04x%04x%04x",
477                          color->red, color->green, color->blue);
478    }
479}
480static PyObject *
481_wrap_gdk_color_tp_repr(PyGBoxed *self)
482{
483    static char buffer[0x40];
484    int length = 0;
485
486    length += g_snprintf(buffer + length, sizeof buffer - length, "%s('", self->ob_type->tp_name);
487    length += pygdk_color_to_string_smart(buffer + length, sizeof buffer - length,
488                                          pyg_boxed_get(self, GdkColor));
489    length += g_snprintf(buffer + length, sizeof buffer - length, "')");
490
491    return PyString_FromStringAndSize(buffer, length);
492}
493%%
494override-slot GdkColor.tp_str
495static PyObject *
496_wrap_gdk_color_tp_str(PyGBoxed *self)
497{
498    /* gtk.gdk.Color has a meaningful informal representation, so we define both __repr__
499     * and __str__, unlike for most other types.
500     */
501    static char buffer[1 + 4*3 + 1];  /* # + at most 4 digits per component + \0 */
502    int length = pygdk_color_to_string_smart(buffer, sizeof buffer, pyg_boxed_get(self, GdkColor));
503
504    return PyString_FromStringAndSize(buffer, length);
505}
506%%
507override gdk_colormap_query_color kwargs
508static PyObject *
509_wrap_gdk_colormap_query_color(PyGObject *self, PyObject *args,
510                               PyObject *kwargs)
511{
512    static char *kwlist[] = { "pixel", NULL };
513    GdkColor colour = { 0, 0, 0, 0 };
514    PyObject *py_pixel;
515    gulong pixel;
516
517    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
518                                     "O:GdkColormap.query_color", kwlist,
519                                     &py_pixel))
520        return NULL;
521
522    if (PyLong_Check(py_pixel)) {
523        pixel = PyLong_AsUnsignedLong(py_pixel);
524        if (PyErr_Occurred())
525            return NULL;
526    } else if (PyInt_Check(py_pixel))
527        pixel = PyInt_AS_LONG(py_pixel);
528    else {
529        PyErr_SetString(PyExc_TypeError, "GdkColormap.query_color: pixel must be"
530                        " either int or long");
531        return NULL;
532    }
533
534    gdk_colormap_query_color(GDK_COLORMAP(self->obj), pixel, &colour);
535
536    return pyg_boxed_new(GDK_TYPE_COLOR, &colour, TRUE, TRUE);
537}
538