]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/drawingarea.c
db5a62d97f4f7e144150d593f0297a6db22160fb
[~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 cairo_surface_t *surface = NULL;
21
22 /* Create a new surface 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   GtkAllocation allocation;
29   cairo_t *cr;
30
31   if (surface)
32     cairo_surface_destroy (surface);
33
34   gtk_widget_get_allocation (widget, &allocation);
35   surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
36                                                CAIRO_CONTENT_COLOR,
37                                                allocation.width,
38                                                allocation.height);
39
40   /* Initialize the surface to white */
41   cr = cairo_create (surface);
42
43   cairo_set_source_rgb (cr, 1, 1, 1);
44   cairo_paint (cr);
45
46   cairo_destroy (cr);
47
48   /* We've handled the configure event, no need for further processing. */
49   return TRUE;
50 }
51
52 /* Redraw the screen from the surface */
53 static gboolean
54 scribble_draw (GtkWidget *widget,
55                cairo_t   *cr,
56                gpointer   data)
57 {
58   cairo_set_source_surface (cr, surface, 0, 0);
59   cairo_paint (cr);
60
61   return FALSE;
62 }
63
64 /* Draw a rectangle on the screen */
65 static void
66 draw_brush (GtkWidget *widget,
67             gdouble    x,
68             gdouble    y)
69 {
70   GdkRectangle update_rect;
71   cairo_t *cr;
72
73   update_rect.x = x - 3;
74   update_rect.y = y - 3;
75   update_rect.width = 6;
76   update_rect.height = 6;
77
78   /* Paint to the surface, where we store our state */
79   cr = cairo_create (surface);
80
81   gdk_cairo_rectangle (cr, &update_rect);
82   cairo_fill (cr);
83
84   cairo_destroy (cr);
85
86   /* Now invalidate the affected region of the drawing area. */
87   gdk_window_invalidate_rect (gtk_widget_get_window (widget),
88                               &update_rect,
89                               FALSE);
90 }
91
92 static gboolean
93 scribble_button_press_event (GtkWidget      *widget,
94                              GdkEventButton *event,
95                              gpointer        data)
96 {
97   if (surface == NULL)
98     return FALSE; /* paranoia check, in case we haven't gotten a configure event */
99
100   if (event->button == 1)
101     draw_brush (widget, event->x, event->y);
102
103   /* We've handled the event, stop processing */
104   return TRUE;
105 }
106
107 static gboolean
108 scribble_motion_notify_event (GtkWidget      *widget,
109                               GdkEventMotion *event,
110                               gpointer        data)
111 {
112   int x, y;
113   GdkModifierType state;
114
115   if (surface == NULL)
116     return FALSE; /* paranoia check, in case we haven't gotten a configure event */
117
118   /* This call is very important; it requests the next motion event.
119    * If you don't call gdk_window_get_pointer() you'll only get
120    * a single motion event. The reason is that we specified
121    * GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events().
122    * If we hadn't specified that, we could just use event->x, event->y
123    * as the pointer location. But we'd also get deluged in events.
124    * By requesting the next event as we handle the current one,
125    * we avoid getting a huge number of events faster than we
126    * can cope.
127    */
128
129   gdk_window_get_pointer (event->window, &x, &y, &state);
130
131   if (state & GDK_BUTTON1_MASK)
132     draw_brush (widget, x, y);
133
134   /* We've handled it, stop processing */
135   return TRUE;
136 }
137
138
139 static gboolean
140 checkerboard_draw (GtkWidget *da,
141                    cairo_t   *cr,
142                    gpointer   data)
143 {
144   gint i, j, xcount, ycount, width, height;
145
146 #define CHECK_SIZE 10
147 #define SPACING 2
148
149   /* At the start of a draw handler, a clip region has been set on
150    * the Cairo context, and the contents have been cleared to the
151    * widget's background color. The docs for
152    * gdk_window_begin_paint_region() give more details on how this
153    * works.
154    */
155
156   xcount = 0;
157   width = gtk_widget_get_allocated_width (da);
158   height = gtk_widget_get_allocated_height (da);
159   i = SPACING;
160   while (i < width)
161     {
162       j = SPACING;
163       ycount = xcount % 2; /* start with even/odd depending on row */
164       while (j < height)
165         {
166           if (ycount % 2)
167             cairo_set_source_rgb (cr, 0.45777, 0, 0.45777);
168           else
169             cairo_set_source_rgb (cr, 1, 1, 1);
170
171           /* If we're outside the clip, this will do nothing.
172            */
173           cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
174           cairo_fill (cr);
175
176           j += CHECK_SIZE + SPACING;
177           ++ycount;
178         }
179
180       i += CHECK_SIZE + SPACING;
181       ++xcount;
182     }
183
184   /* return TRUE because we've handled this event, so no
185    * further processing is required.
186    */
187   return TRUE;
188 }
189
190 static void
191 close_window (void)
192 {
193   window = NULL;
194
195   if (surface)
196     cairo_surface_destroy (surface);
197   surface = NULL;
198 }
199
200 GtkWidget *
201 do_drawingarea (GtkWidget *do_widget)
202 {
203   GtkWidget *frame;
204   GtkWidget *vbox;
205   GtkWidget *da;
206   GtkWidget *label;
207
208   if (!window)
209     {
210       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
211       gtk_window_set_screen (GTK_WINDOW (window),
212                              gtk_widget_get_screen (do_widget));
213       gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
214
215       g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
216
217       gtk_container_set_border_width (GTK_CONTAINER (window), 8);
218
219       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 8);
220       gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
221       gtk_container_add (GTK_CONTAINER (window), vbox);
222
223       /*
224        * Create the checkerboard area
225        */
226
227       label = gtk_label_new (NULL);
228       gtk_label_set_markup (GTK_LABEL (label),
229                             "<u>Checkerboard pattern</u>");
230       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
231
232       frame = gtk_frame_new (NULL);
233       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
234       gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
235
236       da = gtk_drawing_area_new ();
237       /* set a minimum size */
238       gtk_widget_set_size_request (da, 100, 100);
239
240       gtk_container_add (GTK_CONTAINER (frame), da);
241
242       g_signal_connect (da, "draw",
243                         G_CALLBACK (checkerboard_draw), NULL);
244
245       /*
246        * Create the scribble area
247        */
248
249       label = gtk_label_new (NULL);
250       gtk_label_set_markup (GTK_LABEL (label),
251                             "<u>Scribble area</u>");
252       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
253
254       frame = gtk_frame_new (NULL);
255       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
256       gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
257
258       da = gtk_drawing_area_new ();
259       /* set a minimum size */
260       gtk_widget_set_size_request (da, 100, 100);
261
262       gtk_container_add (GTK_CONTAINER (frame), da);
263
264       /* Signals used to handle backing surface */
265
266       g_signal_connect (da, "draw",
267                         G_CALLBACK (scribble_draw), NULL);
268       g_signal_connect (da,"configure-event",
269                         G_CALLBACK (scribble_configure_event), NULL);
270
271       /* Event signals */
272
273       g_signal_connect (da, "motion-notify-event",
274                         G_CALLBACK (scribble_motion_notify_event), NULL);
275       g_signal_connect (da, "button-press-event",
276                         G_CALLBACK (scribble_button_press_event), NULL);
277
278
279       /* Ask to receive events the drawing area doesn't normally
280        * subscribe to
281        */
282       gtk_widget_set_events (da, gtk_widget_get_events (da)
283                              | GDK_LEAVE_NOTIFY_MASK
284                              | GDK_BUTTON_PRESS_MASK
285                              | GDK_POINTER_MOTION_MASK
286                              | GDK_POINTER_MOTION_HINT_MASK);
287
288     }
289
290   if (!gtk_widget_get_visible (window))
291       gtk_widget_show_all (window);
292   else
293       gtk_widget_destroy (window);
294
295   return window;
296 }