]> Pileus Git - ~andy/gtk/blob - gtk/gtkpreview.c
do not call gtk_settings_get_default() here as that requires an X
[~andy/gtk] / gtk / gtkpreview.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include <math.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #include "gdk/gdkrgb.h"
36 #include "gtkpreview.h"
37 #include "gtksignal.h"
38 #include "gtkintl.h"
39
40
41 #define PREVIEW_CLASS(w)      GTK_PREVIEW_CLASS (GTK_OBJECT (w)->klass)
42
43 enum {
44   PROP_0,
45   PROP_EXPAND
46 };
47
48
49 static void   gtk_preview_class_init    (GtkPreviewClass  *klass);
50 static void   gtk_preview_init          (GtkPreview       *preview);
51 static void   gtk_preview_set_property  (GObject          *object,
52                                          guint             prop_id,
53                                          const GValue     *value,
54                                          GParamSpec       *pspec);
55 static void   gtk_preview_get_property  (GObject          *object,
56                                          guint             prop_id,
57                                          GValue           *value,
58                                          GParamSpec       *pspec);
59 static void   gtk_preview_finalize      (GObject          *object);
60 static void   gtk_preview_realize       (GtkWidget        *widget);
61 static void   gtk_preview_size_allocate (GtkWidget        *widget,
62                                          GtkAllocation    *allocation);
63 static gint   gtk_preview_expose        (GtkWidget        *widget,
64                                          GdkEventExpose   *event);
65 static void   gtk_preview_make_buffer   (GtkPreview       *preview);
66 static void   gtk_fill_lookup_array     (guchar           *array);
67
68 static GtkWidgetClass *parent_class = NULL;
69 static GtkPreviewClass *preview_class = NULL;
70 static gint install_cmap = FALSE;
71
72
73 GtkType
74 gtk_preview_get_type (void)
75 {
76   static GtkType preview_type = 0;
77
78   if (!preview_type)
79     {
80       static const GtkTypeInfo preview_info =
81       {
82         "GtkPreview",
83         sizeof (GtkPreview),
84         sizeof (GtkPreviewClass),
85         (GtkClassInitFunc) gtk_preview_class_init,
86         (GtkObjectInitFunc) gtk_preview_init,
87         /* reserved_1 */ NULL,
88         /* reserved_2 */ NULL,
89         (GtkClassInitFunc) NULL,
90       };
91
92       preview_type = gtk_type_unique (GTK_TYPE_WIDGET, &preview_info);
93     }
94
95   return preview_type;
96 }
97
98 static void
99 gtk_preview_class_init (GtkPreviewClass *klass)
100 {
101   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
102   GtkObjectClass *object_class;
103   GtkWidgetClass *widget_class;
104
105   object_class = (GtkObjectClass*) klass;
106   widget_class = (GtkWidgetClass*) klass;
107
108   parent_class = gtk_type_class (GTK_TYPE_WIDGET);
109   preview_class = klass;
110
111   gobject_class->finalize = gtk_preview_finalize;
112
113   gobject_class->set_property = gtk_preview_set_property;
114   gobject_class->get_property = gtk_preview_get_property;
115
116   widget_class->realize = gtk_preview_realize;
117   widget_class->size_allocate = gtk_preview_size_allocate;
118   widget_class->expose_event = gtk_preview_expose;
119
120   klass->info.lookup = NULL;
121
122   klass->info.gamma = 1.0;
123
124   gdk_rgb_init ();
125
126   g_object_class_install_property (gobject_class,
127                                    PROP_EXPAND,
128                                    g_param_spec_boolean ("expand",
129                                                          _("Expand"),
130                                                          _("Whether the preview widget should take up the entire space it is allocated"),
131                                                          FALSE,
132                                                          G_PARAM_READWRITE));
133 }
134
135 static void
136 gtk_preview_set_property (GObject      *object,
137                           guint         prop_id,
138                           const GValue *value,
139                           GParamSpec   *pspec)
140 {
141   GtkPreview *preview = GTK_PREVIEW (object);
142   
143   switch (prop_id)
144     {
145     case PROP_EXPAND:
146       gtk_preview_set_expand (preview, g_value_get_boolean (value));
147       break;
148     default:
149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150       break;
151     }
152 }
153
154 static void
155 gtk_preview_get_property (GObject      *object,
156                           guint         prop_id,
157                           GValue       *value,
158                           GParamSpec   *pspec)
159 {
160   GtkPreview *preview;
161   
162   preview = GTK_PREVIEW (object);
163   
164   switch (prop_id)
165     {
166     case PROP_EXPAND:
167       g_value_set_boolean (value, preview->expand);
168       break;
169     default:
170       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
171       break;
172     }
173 }
174
175 void
176 gtk_preview_reset (void)
177 {
178   /* unimplemented */
179 }
180
181 static void
182 gtk_preview_init (GtkPreview *preview)
183 {
184   preview->buffer = NULL;
185   preview->buffer_width = 0;
186   preview->buffer_height = 0;
187   preview->expand = FALSE;
188 }
189
190 void
191 gtk_preview_uninit (void)
192 {
193   /* unimplemented */
194 }
195
196 GtkWidget*
197 gtk_preview_new (GtkPreviewType type)
198 {
199   GtkPreview *preview;
200
201   preview = gtk_type_new (gtk_preview_get_type ());
202   preview->type = type;
203
204   if (type == GTK_PREVIEW_COLOR)
205     preview->bpp = 3;
206   else
207     preview->bpp = 1;
208
209   preview->dither = GDK_RGB_DITHER_NORMAL;
210
211   return GTK_WIDGET (preview);
212 }
213
214 void
215 gtk_preview_size (GtkPreview *preview,
216                   gint        width,
217                   gint        height)
218 {
219   g_return_if_fail (GTK_IS_PREVIEW (preview));
220
221   if ((width != GTK_WIDGET (preview)->requisition.width) ||
222       (height != GTK_WIDGET (preview)->requisition.height))
223     {
224       GTK_WIDGET (preview)->requisition.width = width;
225       GTK_WIDGET (preview)->requisition.height = height;
226
227       if (preview->buffer)
228         g_free (preview->buffer);
229       preview->buffer = NULL;
230     }
231 }
232
233 void
234 gtk_preview_put (GtkPreview   *preview,
235                  GdkWindow    *window,
236                  GdkGC        *gc,
237                  gint          srcx,
238                  gint          srcy,
239                  gint          destx,
240                  gint          desty,
241                  gint          width,
242                  gint          height)
243 {
244   GtkWidget *widget;
245   GdkRectangle r1, r2, r3;
246   guchar *src;
247   guint bpp;
248   guint rowstride;
249
250   g_return_if_fail (GTK_IS_PREVIEW (preview));
251   g_return_if_fail (window != NULL);
252
253   if (!preview->buffer)
254     return;
255
256   widget = GTK_WIDGET (preview);
257
258   r1.x = 0;
259   r1.y = 0;
260   r1.width = preview->buffer_width;
261   r1.height = preview->buffer_height;
262
263   r2.x = srcx;
264   r2.y = srcy;
265   r2.width = width;
266   r2.height = height;
267
268   if (!gdk_rectangle_intersect (&r1, &r2, &r3))
269     return;
270
271   bpp = preview->bpp;
272   rowstride = preview->rowstride;
273
274   src = preview->buffer + r3.y * rowstride + r3.x * bpp;
275
276   if (preview->type == GTK_PREVIEW_COLOR)
277     gdk_draw_rgb_image (window,
278                         gc,
279                         destx + (r3.x - srcx),
280                         desty + (r3.y - srcy),
281                         r3.width,
282                         r3.height,
283                         preview->dither,
284                         src,
285                         rowstride);
286   else
287     gdk_draw_gray_image (window,
288                          gc,
289                          destx + (r3.x - srcx),
290                          desty + (r3.y - srcy),
291                          r3.width,
292                          r3.height,
293                          preview->dither,
294                          src,
295                          rowstride);
296                         
297 }
298
299 void
300 gtk_preview_draw_row (GtkPreview *preview,
301                       guchar     *data,
302                       gint        x,
303                       gint        y,
304                       gint        w)
305 {
306   guint bpp;
307   guint rowstride;
308
309   g_return_if_fail (GTK_IS_PREVIEW (preview));
310   g_return_if_fail (data != NULL);
311   
312   bpp = (preview->type == GTK_PREVIEW_COLOR ? 3 : 1);
313   rowstride = (preview->buffer_width * bpp + 3) & -4;
314
315   if ((w <= 0) || (y < 0))
316     return;
317
318   g_return_if_fail (data != NULL);
319
320   gtk_preview_make_buffer (preview);
321
322   if (x + w > preview->buffer_width)
323     return;
324
325   if (y + 1 > preview->buffer_height)
326     return;
327
328   if (preview_class->info.gamma == 1.0)
329     memcpy (preview->buffer + y * rowstride + x * bpp, data, w * bpp);
330   else
331     {
332       guint i, size;
333       guchar *src, *dst;
334       guchar *lookup;
335
336       if (preview_class->info.lookup != NULL)
337         lookup = preview_class->info.lookup;
338       else
339         {
340           preview_class->info.lookup = g_new (guchar, 256);
341           gtk_fill_lookup_array (preview_class->info.lookup);
342           lookup = preview_class->info.lookup;
343         }
344       size = w * bpp;
345       src = data;
346       dst = preview->buffer + y * rowstride + x * bpp;
347       for (i = 0; i < size; i++)
348         *dst++ = lookup[*src++];
349     }
350 }
351
352 void
353 gtk_preview_set_expand (GtkPreview *preview,
354                         gboolean    expand)
355 {
356   g_return_if_fail (GTK_IS_PREVIEW (preview));
357
358   expand = expand != FALSE;
359
360   if (preview->expand != expand)
361     {
362       preview->expand = expand;
363       gtk_widget_queue_resize (GTK_WIDGET (preview));
364  
365       g_object_notify (G_OBJECT (preview), "expand"); 
366     }
367 }
368
369 void
370 gtk_preview_set_gamma (double _gamma)
371 {
372   if (!preview_class)
373     preview_class = gtk_type_class (gtk_preview_get_type ());
374
375   if (preview_class->info.gamma != _gamma)
376     {
377       preview_class->info.gamma = _gamma;
378       if (preview_class->info.lookup != NULL)
379         {
380           g_free (preview_class->info.lookup);
381           preview_class->info.lookup = NULL;
382         }
383     }
384 }
385
386 void
387 gtk_preview_set_color_cube (guint nred_shades,
388                             guint ngreen_shades,
389                             guint nblue_shades,
390                             guint ngray_shades)
391 {
392   /* unimplemented */
393 }
394
395 void
396 gtk_preview_set_install_cmap (gint _install_cmap)
397 {
398   /* effectively unimplemented */
399   install_cmap = _install_cmap;
400 }
401
402 void
403 gtk_preview_set_reserved (gint nreserved)
404 {
405
406   /* unimplemented */
407 }
408
409 void
410 gtk_preview_set_dither (GtkPreview      *preview,
411                         GdkRgbDither     dither)
412 {
413   preview->dither = dither;
414 }
415
416 GdkVisual*
417 gtk_preview_get_visual (void)
418 {
419   return gdk_rgb_get_visual ();
420 }
421
422 GdkColormap*
423 gtk_preview_get_cmap (void)
424 {
425   return gdk_rgb_get_colormap ();
426 }
427
428 GtkPreviewInfo*
429 gtk_preview_get_info (void)
430 {
431   if (!preview_class)
432     preview_class = gtk_type_class (gtk_preview_get_type ());
433
434   return &preview_class->info;
435 }
436
437
438 static void
439 gtk_preview_finalize (GObject *object)
440 {
441   GtkPreview *preview;
442
443   g_return_if_fail (GTK_IS_PREVIEW (object));
444
445   preview = GTK_PREVIEW (object);
446   if (preview->buffer)
447     g_free (preview->buffer);
448   preview->type = (GtkPreviewType) -1;
449
450   G_OBJECT_CLASS (parent_class)->finalize (object);
451 }
452
453 static void
454 gtk_preview_realize (GtkWidget *widget)
455 {
456   GtkPreview *preview;
457   GdkWindowAttr attributes;
458   gint attributes_mask;
459
460   g_return_if_fail (GTK_IS_PREVIEW (widget));
461
462   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
463   preview = GTK_PREVIEW (widget);
464
465   attributes.window_type = GDK_WINDOW_CHILD;
466
467   if (preview->expand)
468     {
469       attributes.width = widget->allocation.width;
470       attributes.height = widget->allocation.height;
471     }
472   else
473     {
474       attributes.width = MIN (widget->requisition.width, widget->allocation.width);
475       attributes.height = MIN (widget->requisition.height, widget->allocation.height);
476     }
477
478   attributes.x = widget->allocation.x + (widget->allocation.width - attributes.width) / 2;
479   attributes.y = widget->allocation.y + (widget->allocation.height - attributes.height) / 2;;
480
481   attributes.wclass = GDK_INPUT_OUTPUT;
482   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
483   attributes_mask = GDK_WA_X | GDK_WA_Y;
484
485   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
486   gdk_window_set_user_data (widget->window, widget);
487
488   widget->style = gtk_style_attach (widget->style, widget->window);
489   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
490 }
491
492 static void   
493 gtk_preview_size_allocate (GtkWidget        *widget,
494                            GtkAllocation    *allocation)
495 {
496   GtkPreview *preview;
497   gint width, height;
498
499   g_return_if_fail (GTK_IS_PREVIEW (widget));
500
501   preview = GTK_PREVIEW (widget);
502   widget->allocation = *allocation;
503
504   if (GTK_WIDGET_REALIZED (widget))
505     {
506       if (preview->expand)
507         {
508           width = widget->allocation.width;
509           height = widget->allocation.height;
510         }
511       else
512         {
513           width = MIN (widget->allocation.width, widget->requisition.width);
514           height = MIN (widget->allocation.height, widget->requisition.height);
515         }
516
517       gdk_window_move_resize (widget->window,
518                               widget->allocation.x + (widget->allocation.width - width) / 2,
519                               widget->allocation.y + (widget->allocation.height - height) / 2,
520                               width, height);
521     }
522 }
523
524 static gint
525 gtk_preview_expose (GtkWidget      *widget,
526                     GdkEventExpose *event)
527 {
528   GtkPreview *preview;
529   gint width, height;
530
531   g_return_val_if_fail (GTK_IS_PREVIEW (widget), FALSE);
532   g_return_val_if_fail (event != NULL, FALSE);
533
534   if (GTK_WIDGET_DRAWABLE (widget))
535     {
536       preview = GTK_PREVIEW (widget);
537       
538       gdk_window_get_size (widget->window, &width, &height);
539
540       gtk_preview_put (GTK_PREVIEW (widget),
541                        widget->window, widget->style->black_gc,
542                        event->area.x - (width - preview->buffer_width)/2,
543                        event->area.y - (height - preview->buffer_height)/2,
544                        event->area.x, event->area.y,
545                        event->area.width, event->area.height);
546     }
547   
548   return FALSE;
549 }
550
551 static void
552 gtk_preview_make_buffer (GtkPreview *preview)
553 {
554   GtkWidget *widget;
555   gint width;
556   gint height;
557
558   g_return_if_fail (GTK_IS_PREVIEW (preview));
559
560   widget = GTK_WIDGET (preview);
561
562   if (preview->expand &&
563       (widget->allocation.width != 0) &&
564       (widget->allocation.height != 0))
565     {
566       width = widget->allocation.width;
567       height = widget->allocation.height;
568     }
569   else
570     {
571       width = widget->requisition.width;
572       height = widget->requisition.height;
573     }
574
575   if (!preview->buffer ||
576       (preview->buffer_width != width) ||
577       (preview->buffer_height != height))
578     {
579       if (preview->buffer)
580         g_free (preview->buffer);
581
582       preview->buffer_width = width;
583       preview->buffer_height = height;
584
585       preview->rowstride = (preview->buffer_width * preview->bpp + 3) & -4;
586       preview->buffer = g_new0 (guchar,
587                                 preview->buffer_height *
588                                 preview->rowstride);
589     }
590 }
591
592 /* This is used for implementing gamma. */
593 static void
594 gtk_fill_lookup_array (guchar *array)
595 {
596   double one_over_gamma;
597   double ind;
598   int val;
599   int i;
600
601   one_over_gamma = 1.0 / preview_class->info.gamma;
602
603   for (i = 0; i < 256; i++)
604     {
605       ind = (double) i / 255.0;
606       val = (int) (255 * pow (ind, one_over_gamma));
607       array[i] = val;
608     }
609 }