]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/drawingarea.c
Deprecate widget flag: GTK_WIDGET_VISIBLE
[~andy/gtk] / demos / gtk-demo / drawingarea.c
1 /* Drawing Area
2  *
3  * GtkDrawingArea is a blank area where you can draw custom displays
4  * of various kinds.
5  *
6  * This demo has two drawing areas. The checkerboard area shows
7  * how you can just draw something; all you have to do is write
8  * a signal handler for expose_event, as shown here.
9  *
10  * The "scribble" area is a bit more advanced, and shows how to handle
11  * events such as button presses and mouse motion. Click the mouse
12  * and drag in the scribble area to draw squiggles. Resize the window
13  * to clear the area.
14  */
15
16 #include <gtk/gtk.h>
17
18 static GtkWidget *window = NULL;
19 /* Pixmap for scribble area, to store current scribbles */
20 static GdkPixmap *pixmap = NULL;
21
22 /* Create a new pixmap of the appropriate size to store our scribbles */
23 static gboolean
24 scribble_configure_event (GtkWidget         *widget,
25                           GdkEventConfigure *event,
26                           gpointer           data)
27 {
28   if (pixmap)
29     g_object_unref (pixmap);
30
31   pixmap = gdk_pixmap_new (widget->window,
32                            widget->allocation.width,
33                            widget->allocation.height,
34                            -1);
35
36   /* Initialize the pixmap to white */
37   gdk_draw_rectangle (pixmap,
38                       widget->style->white_gc,
39                       TRUE,
40                       0, 0,
41                       widget->allocation.width,
42                       widget->allocation.height);
43
44   /* We've handled the configure event, no need for further processing. */
45   return TRUE;
46 }
47
48 /* Redraw the screen from the pixmap */
49 static gboolean
50 scribble_expose_event (GtkWidget      *widget,
51                        GdkEventExpose *event,
52                        gpointer        data)
53 {
54   /* We use the "foreground GC" for the widget since it already exists,
55    * but honestly any GC would work. The only thing to worry about
56    * is whether the GC has an inappropriate clip region set.
57    */
58
59   gdk_draw_drawable (widget->window,
60                      widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
61                      pixmap,
62                      /* Only copy the area that was exposed. */
63                      event->area.x, event->area.y,
64                      event->area.x, event->area.y,
65                      event->area.width, event->area.height);
66
67   return FALSE;
68 }
69
70 /* Draw a rectangle on the screen */
71 static void
72 draw_brush (GtkWidget *widget,
73             gdouble    x,
74             gdouble    y)
75 {
76   GdkRectangle update_rect;
77
78   update_rect.x = x - 3;
79   update_rect.y = y - 3;
80   update_rect.width = 6;
81   update_rect.height = 6;
82
83   /* Paint to the pixmap, where we store our state */
84   gdk_draw_rectangle (pixmap,
85                       widget->style->black_gc,
86                       TRUE,
87                       update_rect.x, update_rect.y,
88                       update_rect.width, update_rect.height);
89
90   /* Now invalidate the affected region of the drawing area. */
91   gdk_window_invalidate_rect (widget->window,
92                               &update_rect,
93                               FALSE);
94 }
95
96 static gboolean
97 scribble_button_press_event (GtkWidget      *widget,
98                              GdkEventButton *event,
99                              gpointer        data)
100 {
101   if (pixmap == NULL)
102     return FALSE; /* paranoia check, in case we haven't gotten a configure event */
103
104   if (event->button == 1)
105     draw_brush (widget, event->x, event->y);
106
107   /* We've handled the event, stop processing */
108   return TRUE;
109 }
110
111 static gboolean
112 scribble_motion_notify_event (GtkWidget      *widget,
113                               GdkEventMotion *event,
114                               gpointer        data)
115 {
116   int x, y;
117   GdkModifierType state;
118
119   if (pixmap == NULL)
120     return FALSE; /* paranoia check, in case we haven't gotten a configure event */
121
122   /* This call is very important; it requests the next motion event.
123    * If you don't call gdk_window_get_pointer() you'll only get
124    * a single motion event. The reason is that we specified
125    * GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events().
126    * If we hadn't specified that, we could just use event->x, event->y
127    * as the pointer location. But we'd also get deluged in events.
128    * By requesting the next event as we handle the current one,
129    * we avoid getting a huge number of events faster than we
130    * can cope.
131    */
132
133   gdk_window_get_pointer (event->window, &x, &y, &state);
134
135   if (state & GDK_BUTTON1_MASK)
136     draw_brush (widget, x, y);
137
138   /* We've handled it, stop processing */
139   return TRUE;
140 }
141
142
143 static gboolean
144 checkerboard_expose (GtkWidget      *da,
145                      GdkEventExpose *event,
146                      gpointer        data)
147 {
148   gint i, j, xcount, ycount;
149   GdkGC *gc1, *gc2;
150   GdkColor color;
151
152 #define CHECK_SIZE 10
153 #define SPACING 2
154
155   /* At the start of an expose handler, a clip region of event->area
156    * is set on the window, and event->area has been cleared to the
157    * widget's background color. The docs for
158    * gdk_window_begin_paint_region() give more details on how this
159    * works.
160    */
161
162   /* It would be a bit more efficient to keep these
163    * GC's around instead of recreating on each expose, but
164    * this is the lazy/slow way.
165    */
166   gc1 = gdk_gc_new (da->window);
167   color.red = 30000;
168   color.green = 0;
169   color.blue = 30000;
170   gdk_gc_set_rgb_fg_color (gc1, &color);
171
172   gc2 = gdk_gc_new (da->window);
173   color.red = 65535;
174   color.green = 65535;
175   color.blue = 65535;
176   gdk_gc_set_rgb_fg_color (gc2, &color);
177
178   xcount = 0;
179   i = SPACING;
180   while (i < da->allocation.width)
181     {
182       j = SPACING;
183       ycount = xcount % 2; /* start with even/odd depending on row */
184       while (j < da->allocation.height)
185         {
186           GdkGC *gc;
187
188           if (ycount % 2)
189             gc = gc1;
190           else
191             gc = gc2;
192
193           /* If we're outside event->area, this will do nothing.
194            * It might be mildly more efficient if we handled
195            * the clipping ourselves, but again we're feeling lazy.
196            */
197           gdk_draw_rectangle (da->window,
198                               gc,
199                               TRUE,
200                               i, j,
201                               CHECK_SIZE,
202                               CHECK_SIZE);
203
204           j += CHECK_SIZE + SPACING;
205           ++ycount;
206         }
207
208       i += CHECK_SIZE + SPACING;
209       ++xcount;
210     }
211
212   g_object_unref (gc1);
213   g_object_unref (gc2);
214
215   /* return TRUE because we've handled this event, so no
216    * further processing is required.
217    */
218   return TRUE;
219 }
220
221 static void
222 close_window (void)
223 {
224   window = NULL;
225
226   if (pixmap)
227     g_object_unref (pixmap);
228   pixmap = NULL;
229 }
230
231 GtkWidget *
232 do_drawingarea (GtkWidget *do_widget)
233 {
234   GtkWidget *frame;
235   GtkWidget *vbox;
236   GtkWidget *da;
237   GtkWidget *label;
238
239   if (!window)
240     {
241       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
242       gtk_window_set_screen (GTK_WINDOW (window),
243                              gtk_widget_get_screen (do_widget));
244       gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
245
246       g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
247
248       gtk_container_set_border_width (GTK_CONTAINER (window), 8);
249
250       vbox = gtk_vbox_new (FALSE, 8);
251       gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
252       gtk_container_add (GTK_CONTAINER (window), vbox);
253
254       /*
255        * Create the checkerboard area
256        */
257
258       label = gtk_label_new (NULL);
259       gtk_label_set_markup (GTK_LABEL (label),
260                             "<u>Checkerboard pattern</u>");
261       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
262
263       frame = gtk_frame_new (NULL);
264       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
265       gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
266
267       da = gtk_drawing_area_new ();
268       /* set a minimum size */
269       gtk_widget_set_size_request (da, 100, 100);
270
271       gtk_container_add (GTK_CONTAINER (frame), da);
272
273       g_signal_connect (da, "expose-event",
274                         G_CALLBACK (checkerboard_expose), NULL);
275
276       /*
277        * Create the scribble area
278        */
279
280       label = gtk_label_new (NULL);
281       gtk_label_set_markup (GTK_LABEL (label),
282                             "<u>Scribble area</u>");
283       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
284
285       frame = gtk_frame_new (NULL);
286       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
287       gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
288
289       da = gtk_drawing_area_new ();
290       /* set a minimum size */
291       gtk_widget_set_size_request (da, 100, 100);
292
293       gtk_container_add (GTK_CONTAINER (frame), da);
294
295       /* Signals used to handle backing pixmap */
296
297       g_signal_connect (da, "expose-event",
298                         G_CALLBACK (scribble_expose_event), NULL);
299       g_signal_connect (da,"configure-event",
300                         G_CALLBACK (scribble_configure_event), NULL);
301
302       /* Event signals */
303
304       g_signal_connect (da, "motion-notify-event",
305                         G_CALLBACK (scribble_motion_notify_event), NULL);
306       g_signal_connect (da, "button-press-event",
307                         G_CALLBACK (scribble_button_press_event), NULL);
308
309
310       /* Ask to receive events the drawing area doesn't normally
311        * subscribe to
312        */
313       gtk_widget_set_events (da, gtk_widget_get_events (da)
314                              | GDK_LEAVE_NOTIFY_MASK
315                              | GDK_BUTTON_PRESS_MASK
316                              | GDK_POINTER_MOTION_MASK
317                              | GDK_POINTER_MOTION_HINT_MASK);
318
319     }
320
321   if (!gtk_widget_get_visible (window))
322       gtk_widget_show_all (window);
323   else
324       gtk_widget_destroy (window);
325
326   return window;
327 }