]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/drawingarea.c
It's the worlds ugliest highlighting code!!!! The result is okay so long
[~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 (G_OBJECT (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 (G_OBJECT (gc1));
213   g_object_unref (G_OBJECT (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 GtkWidget *
222 do_drawingarea (void)
223 {
224   GtkWidget *frame;
225   GtkWidget *vbox;
226   GtkWidget *da;
227   GtkWidget *label;
228   
229   if (!window)
230     {
231       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
232       gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
233
234       gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &window);
235
236       gtk_container_set_border_width (GTK_CONTAINER (window), 8);
237
238       vbox = gtk_vbox_new (FALSE, 8);
239       gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
240       gtk_container_add (GTK_CONTAINER (window), vbox);
241
242       /*
243        * Create the checkerboard area
244        */
245       
246       label = gtk_label_new (NULL);
247       gtk_label_set_markup (GTK_LABEL (label),
248                             "<u>Checkerboard pattern</u>");
249       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
250       
251       frame = gtk_frame_new (NULL);
252       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
253       gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
254       
255       da = gtk_drawing_area_new ();
256       /* set a minimum size */
257       gtk_widget_set_usize (da, 100, 100);
258
259       gtk_container_add (GTK_CONTAINER (frame), da);
260
261       gtk_signal_connect (GTK_OBJECT (da),
262                           "expose_event",
263                           GTK_SIGNAL_FUNC (checkerboard_expose),
264                           NULL);
265
266       /*
267        * Create the scribble area
268        */
269       
270       label = gtk_label_new (NULL);
271       gtk_label_set_markup (GTK_LABEL (label),
272                             "<u>Scribble area</u>");
273       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
274       
275       frame = gtk_frame_new (NULL);
276       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
277       gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
278       
279       da = gtk_drawing_area_new ();
280       /* set a minimum size */
281       gtk_widget_set_usize (da, 100, 100);
282
283       gtk_container_add (GTK_CONTAINER (frame), da);
284
285       /* Signals used to handle backing pixmap */
286       
287       gtk_signal_connect (GTK_OBJECT (da), "expose_event",
288                           GTK_SIGNAL_FUNC (scribble_expose_event), NULL);
289       gtk_signal_connect (GTK_OBJECT (da),"configure_event",
290                           GTK_SIGNAL_FUNC (scribble_configure_event), NULL);
291       
292       /* Event signals */
293       
294       gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
295                           GTK_SIGNAL_FUNC (scribble_motion_notify_event), NULL);
296       gtk_signal_connect (GTK_OBJECT (da), "button_press_event",
297                           GTK_SIGNAL_FUNC (scribble_button_press_event), NULL);
298
299
300       /* Ask to receive events the drawing area doesn't normally
301        * subscribe to
302        */
303       gtk_widget_set_events (da, gtk_widget_get_events (da)
304                              | GDK_LEAVE_NOTIFY_MASK
305                              | GDK_BUTTON_PRESS_MASK
306                              | GDK_POINTER_MOTION_MASK
307                              | GDK_POINTER_MOTION_HINT_MASK);
308
309     }
310
311   if (!GTK_WIDGET_VISIBLE (window))
312     {
313       gtk_widget_show_all (window);
314     }
315   else
316     {
317       gtk_widget_destroy (window);
318       window = NULL;
319     }
320
321   return window;
322 }