]> Pileus Git - ~andy/gtk/blob - gtk/gtkpreview.c
cb0cf04190dbcc4e42b83be9966bf1052de59bd6
[~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.visual = NULL;
121   klass->info.cmap = NULL;
122
123   klass->info.lookup = NULL;
124
125   klass->info.gamma = 1.0;
126
127   gdk_rgb_init ();
128   klass->info.cmap = gdk_rgb_get_cmap ();
129   klass->info.visual = gdk_rgb_get_visual ();
130
131   g_object_class_install_property (gobject_class,
132                                    PROP_EXPAND,
133                                    g_param_spec_boolean ("expand",
134                                                          _("Expand"),
135                                                          _("Whether the preview widget should take up the entire space it is allocated"),
136                                                          FALSE,
137                                                          G_PARAM_READWRITE));
138 }
139
140 static void
141 gtk_preview_set_property (GObject      *object,
142                           guint         prop_id,
143                           const GValue *value,
144                           GParamSpec   *pspec)
145 {
146   GtkPreview *preview = GTK_PREVIEW (object);
147   
148   switch (prop_id)
149     {
150     case PROP_EXPAND:
151       gtk_preview_set_expand (preview, g_value_get_boolean (value));
152       break;
153     default:
154       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
155       break;
156     }
157 }
158
159 static void
160 gtk_preview_get_property (GObject      *object,
161                           guint         prop_id,
162                           GValue       *value,
163                           GParamSpec   *pspec)
164 {
165   GtkPreview *preview;
166   
167   preview = GTK_PREVIEW (object);
168   
169   switch (prop_id)
170     {
171     case PROP_EXPAND:
172       g_value_set_boolean (value, preview->expand);
173       break;
174     default:
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176       break;
177     }
178 }
179
180 void
181 gtk_preview_reset (void)
182 {
183   /* unimplemented */
184 }
185
186 static void
187 gtk_preview_init (GtkPreview *preview)
188 {
189   preview->buffer = NULL;
190   preview->buffer_width = 0;
191   preview->buffer_height = 0;
192   preview->expand = FALSE;
193 }
194
195 void
196 gtk_preview_uninit (void)
197 {
198   /* unimplemented */
199 }
200
201 GtkWidget*
202 gtk_preview_new (GtkPreviewType type)
203 {
204   GtkPreview *preview;
205
206   preview = gtk_type_new (gtk_preview_get_type ());
207   preview->type = type;
208
209   if (type == GTK_PREVIEW_COLOR)
210     preview->bpp = 3;
211   else
212     preview->bpp = 1;
213
214   preview->dither = GDK_RGB_DITHER_NORMAL;
215
216   return GTK_WIDGET (preview);
217 }
218
219 void
220 gtk_preview_size (GtkPreview *preview,
221                   gint        width,
222                   gint        height)
223 {
224   g_return_if_fail (preview != NULL);
225   g_return_if_fail (GTK_IS_PREVIEW (preview));
226
227   if ((width != GTK_WIDGET (preview)->requisition.width) ||
228       (height != GTK_WIDGET (preview)->requisition.height))
229     {
230       GTK_WIDGET (preview)->requisition.width = width;
231       GTK_WIDGET (preview)->requisition.height = height;
232
233       if (preview->buffer)
234         g_free (preview->buffer);
235       preview->buffer = NULL;
236     }
237 }
238
239 void
240 gtk_preview_put (GtkPreview   *preview,
241                  GdkWindow    *window,
242                  GdkGC        *gc,
243                  gint          srcx,
244                  gint          srcy,
245                  gint          destx,
246                  gint          desty,
247                  gint          width,
248                  gint          height)
249 {
250   GtkWidget *widget;
251   GdkRectangle r1, r2, r3;
252   guchar *src;
253   guint bpp;
254   guint rowstride;
255
256   g_return_if_fail (preview != NULL);
257   g_return_if_fail (GTK_IS_PREVIEW (preview));
258   g_return_if_fail (window != NULL);
259
260   if (!preview->buffer)
261     return;
262
263   widget = GTK_WIDGET (preview);
264
265   r1.x = 0;
266   r1.y = 0;
267   r1.width = preview->buffer_width;
268   r1.height = preview->buffer_height;
269
270   r2.x = srcx;
271   r2.y = srcy;
272   r2.width = width;
273   r2.height = height;
274
275   if (!gdk_rectangle_intersect (&r1, &r2, &r3))
276     return;
277
278   bpp = preview->bpp;
279   rowstride = preview->rowstride;
280
281   src = preview->buffer + r3.y * rowstride + r3.x * bpp;
282
283   if (preview->type == GTK_PREVIEW_COLOR)
284     gdk_draw_rgb_image (window,
285                         gc,
286                         destx + (r3.x - srcx),
287                         desty + (r3.y - srcy),
288                         r3.width,
289                         r3.height,
290                         preview->dither,
291                         src,
292                         rowstride);
293   else
294     gdk_draw_gray_image (window,
295                          gc,
296                          destx + (r3.x - srcx),
297                          desty + (r3.y - srcy),
298                          r3.width,
299                          r3.height,
300                          preview->dither,
301                          src,
302                          rowstride);
303                         
304 }
305
306 void
307 gtk_preview_draw_row (GtkPreview *preview,
308                       guchar     *data,
309                       gint        x,
310                       gint        y,
311                       gint        w)
312 {
313   guint bpp;
314   guint rowstride;
315
316   g_return_if_fail (preview != NULL);
317   g_return_if_fail (GTK_IS_PREVIEW (preview));
318   g_return_if_fail (data != NULL);
319   g_return_if_fail (preview_class->info.visual != NULL);
320   
321   bpp = (preview->type == GTK_PREVIEW_COLOR ? 3 : 1);
322   rowstride = (preview->buffer_width * bpp + 3) & -4;
323
324   if ((w <= 0) || (y < 0))
325     return;
326
327   g_return_if_fail (data != NULL);
328
329   gtk_preview_make_buffer (preview);
330
331   if (x + w > preview->buffer_width)
332     return;
333
334   if (y + 1 > preview->buffer_height)
335     return;
336
337   if (preview_class->info.gamma == 1.0)
338     memcpy (preview->buffer + y * rowstride + x * bpp, data, w * bpp);
339   else
340     {
341       guint i, size;
342       guchar *src, *dst;
343       guchar *lookup;
344
345       if (preview_class->info.lookup != NULL)
346         lookup = preview_class->info.lookup;
347       else
348         {
349           preview_class->info.lookup = g_new (guchar, 256);
350           gtk_fill_lookup_array (preview_class->info.lookup);
351           lookup = preview_class->info.lookup;
352         }
353       size = w * bpp;
354       src = data;
355       dst = preview->buffer + y * rowstride + x * bpp;
356       for (i = 0; i < size; i++)
357         *dst++ = lookup[*src++];
358     }
359 }
360
361 void
362 gtk_preview_set_expand (GtkPreview *preview,
363                         gboolean    expand)
364 {
365   g_return_if_fail (preview != NULL);
366   g_return_if_fail (GTK_IS_PREVIEW (preview));
367
368   expand = expand != FALSE;
369
370   if (preview->expand != expand)
371     {
372       preview->expand = expand;
373       gtk_widget_queue_resize (GTK_WIDGET (preview));
374  
375       g_object_notify (G_OBJECT (preview), "expand"); 
376     }
377 }
378
379 void
380 gtk_preview_set_gamma (double _gamma)
381 {
382   if (!preview_class)
383     preview_class = gtk_type_class (gtk_preview_get_type ());
384
385   if (preview_class->info.gamma != _gamma)
386     {
387       preview_class->info.gamma = _gamma;
388       if (preview_class->info.lookup != NULL)
389         {
390           g_free (preview_class->info.lookup);
391           preview_class->info.lookup = NULL;
392         }
393     }
394 }
395
396 void
397 gtk_preview_set_color_cube (guint nred_shades,
398                             guint ngreen_shades,
399                             guint nblue_shades,
400                             guint ngray_shades)
401 {
402   /* unimplemented */
403 }
404
405 void
406 gtk_preview_set_install_cmap (gint _install_cmap)
407 {
408   /* effectively unimplemented */
409   install_cmap = _install_cmap;
410 }
411
412 void
413 gtk_preview_set_reserved (gint nreserved)
414 {
415
416   /* unimplemented */
417 }
418
419 void
420 gtk_preview_set_dither (GtkPreview      *preview,
421                         GdkRgbDither     dither)
422 {
423   preview->dither = dither;
424 }
425
426 GdkVisual*
427 gtk_preview_get_visual (void)
428 {
429   if (!preview_class)
430     preview_class = gtk_type_class (gtk_preview_get_type ());
431
432   return preview_class->info.visual;
433 }
434
435 GdkColormap*
436 gtk_preview_get_cmap (void)
437 {
438   if (!preview_class)
439     preview_class = gtk_type_class (gtk_preview_get_type ());
440
441   return preview_class->info.cmap;
442 }
443
444 GtkPreviewInfo*
445 gtk_preview_get_info (void)
446 {
447   if (!preview_class)
448     preview_class = gtk_type_class (gtk_preview_get_type ());
449
450   return &preview_class->info;
451 }
452
453
454 static void
455 gtk_preview_finalize (GObject *object)
456 {
457   GtkPreview *preview;
458
459   g_return_if_fail (GTK_IS_PREVIEW (object));
460
461   preview = GTK_PREVIEW (object);
462   if (preview->buffer)
463     g_free (preview->buffer);
464   preview->type = (GtkPreviewType) -1;
465
466   G_OBJECT_CLASS (parent_class)->finalize (object);
467 }
468
469 static void
470 gtk_preview_realize (GtkWidget *widget)
471 {
472   GtkPreview *preview;
473   GdkWindowAttr attributes;
474   gint attributes_mask;
475
476   g_return_if_fail (widget != NULL);
477   g_return_if_fail (GTK_IS_PREVIEW (widget));
478
479   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
480   preview = GTK_PREVIEW (widget);
481
482   attributes.window_type = GDK_WINDOW_CHILD;
483
484   if (preview->expand)
485     {
486       attributes.width = widget->allocation.width;
487       attributes.height = widget->allocation.height;
488     }
489   else
490     {
491       attributes.width = MIN (widget->requisition.width, widget->allocation.width);
492       attributes.height = MIN (widget->requisition.height, widget->allocation.height);
493     }
494
495   attributes.x = widget->allocation.x + (widget->allocation.width - attributes.width) / 2;
496   attributes.y = widget->allocation.y + (widget->allocation.height - attributes.height) / 2;;
497
498   attributes.wclass = GDK_INPUT_OUTPUT;
499   attributes.visual = preview_class->info.visual;
500   attributes.colormap = preview_class->info.cmap;
501   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
502   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
503
504   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
505   gdk_window_set_user_data (widget->window, widget);
506
507   widget->style = gtk_style_attach (widget->style, widget->window);
508   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
509 }
510
511 static void   
512 gtk_preview_size_allocate (GtkWidget        *widget,
513                            GtkAllocation    *allocation)
514 {
515   GtkPreview *preview;
516   gint width, height;
517
518   g_return_if_fail (widget != NULL);
519   g_return_if_fail (GTK_IS_PREVIEW (widget));
520
521   preview = GTK_PREVIEW (widget);
522   widget->allocation = *allocation;
523
524   if (GTK_WIDGET_REALIZED (widget))
525     {
526       if (preview->expand)
527         {
528           width = widget->allocation.width;
529           height = widget->allocation.height;
530         }
531       else
532         {
533           width = MIN (widget->allocation.width, widget->requisition.width);
534           height = MIN (widget->allocation.height, widget->requisition.height);
535         }
536
537       gdk_window_move_resize (widget->window,
538                               widget->allocation.x + (widget->allocation.width - width) / 2,
539                               widget->allocation.y + (widget->allocation.height - height) / 2,
540                               width, height);
541     }
542 }
543
544 static gint
545 gtk_preview_expose (GtkWidget      *widget,
546                     GdkEventExpose *event)
547 {
548   GtkPreview *preview;
549   gint width, height;
550
551   g_return_val_if_fail (widget != NULL, FALSE);
552   g_return_val_if_fail (GTK_IS_PREVIEW (widget), FALSE);
553   g_return_val_if_fail (event != NULL, FALSE);
554
555   if (GTK_WIDGET_DRAWABLE (widget))
556     {
557       preview = GTK_PREVIEW (widget);
558       
559       gdk_window_get_size (widget->window, &width, &height);
560
561       gtk_preview_put (GTK_PREVIEW (widget),
562                        widget->window, widget->style->black_gc,
563                        event->area.x - (width - preview->buffer_width)/2,
564                        event->area.y - (height - preview->buffer_height)/2,
565                        event->area.x, event->area.y,
566                        event->area.width, event->area.height);
567     }
568   
569   return FALSE;
570 }
571
572 static void
573 gtk_preview_make_buffer (GtkPreview *preview)
574 {
575   GtkWidget *widget;
576   gint width;
577   gint height;
578
579   g_return_if_fail (preview != NULL);
580   g_return_if_fail (GTK_IS_PREVIEW (preview));
581
582   widget = GTK_WIDGET (preview);
583
584   if (preview->expand &&
585       (widget->allocation.width != 0) &&
586       (widget->allocation.height != 0))
587     {
588       width = widget->allocation.width;
589       height = widget->allocation.height;
590     }
591   else
592     {
593       width = widget->requisition.width;
594       height = widget->requisition.height;
595     }
596
597   if (!preview->buffer ||
598       (preview->buffer_width != width) ||
599       (preview->buffer_height != height))
600     {
601       if (preview->buffer)
602         g_free (preview->buffer);
603
604       preview->buffer_width = width;
605       preview->buffer_height = height;
606
607       preview->rowstride = (preview->buffer_width * preview->bpp + 3) & -4;
608       preview->buffer = g_new0 (guchar,
609                                 preview->buffer_height *
610                                 preview->rowstride);
611     }
612 }
613
614 /* This is used for implementing gamma. */
615 static void
616 gtk_fill_lookup_array (guchar *array)
617 {
618   double one_over_gamma;
619   double ind;
620   int val;
621   int i;
622
623   one_over_gamma = 1.0 / preview_class->info.gamma;
624
625   for (i = 0; i < 256; i++)
626     {
627       ind = (double) i / 255.0;
628       val = (int) (255 * pow (ind, one_over_gamma));
629       array[i] = val;
630     }
631 }