diff options
Diffstat (limited to 'demos/gtk-demo/drawingarea.c')
-rw-r--r-- | demos/gtk-demo/drawingarea.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/demos/gtk-demo/drawingarea.c b/demos/gtk-demo/drawingarea.c new file mode 100644 index 0000000000..101b159730 --- /dev/null +++ b/demos/gtk-demo/drawingarea.c @@ -0,0 +1,322 @@ +/* Drawing Area + * + * GtkDrawingArea is a blank area where you can draw custom displays + * of various kinds. + * + * This demo has two drawing areas. The checkerboard area shows + * how you can just draw something; all you have to do is write + * a signal handler for expose_event, as shown here. + * + * The "scribble" area is a bit more advanced, and shows how to handle + * events such as button presses and mouse motion. Click the mouse + * and drag in the scribble area to draw squiggles. Resize the window + * to clear the area. + */ + +#include <gtk/gtk.h> + +static GtkWidget *window = NULL; +/* Pixmap for scribble area, to store current scribbles */ +static GdkPixmap *pixmap = NULL; + +/* Create a new pixmap of the appropriate size to store our scribbles */ +static gboolean +scribble_configure_event (GtkWidget *widget, + GdkEventConfigure *event, + gpointer data) +{ + if (pixmap) + g_object_unref (G_OBJECT (pixmap)); + + pixmap = gdk_pixmap_new (widget->window, + widget->allocation.width, + widget->allocation.height, + -1); + + /* Initialize the pixmap to white */ + gdk_draw_rectangle (pixmap, + widget->style->white_gc, + TRUE, + 0, 0, + widget->allocation.width, + widget->allocation.height); + + /* We've handled the configure event, no need for further processing. */ + return TRUE; +} + +/* Redraw the screen from the pixmap */ +static gboolean +scribble_expose_event (GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ + /* We use the "foreground GC" for the widget since it already exists, + * but honestly any GC would work. The only thing to worry about + * is whether the GC has an inappropriate clip region set. + */ + + gdk_draw_drawable (widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + pixmap, + /* Only copy the area that was exposed. */ + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return FALSE; +} + +/* Draw a rectangle on the screen */ +static void +draw_brush (GtkWidget *widget, + gdouble x, + gdouble y) +{ + GdkRectangle update_rect; + + update_rect.x = x - 3; + update_rect.y = y - 3; + update_rect.width = 6; + update_rect.height = 6; + + /* Paint to the pixmap, where we store our state */ + gdk_draw_rectangle (pixmap, + widget->style->black_gc, + TRUE, + update_rect.x, update_rect.y, + update_rect.width, update_rect.height); + + /* Now invalidate the affected region of the drawing area. */ + gdk_window_invalidate_rect (widget->window, + &update_rect, + FALSE); +} + +static gboolean +scribble_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + if (pixmap == NULL) + return FALSE; /* paranoia check, in case we haven't gotten a configure event */ + + if (event->button == 1) + draw_brush (widget, event->x, event->y); + + /* We've handled the event, stop processing */ + return TRUE; +} + +static gboolean +scribble_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event, + gpointer data) +{ + int x, y; + GdkModifierType state; + + if (pixmap == NULL) + return FALSE; /* paranoia check, in case we haven't gotten a configure event */ + + /* This call is very important; it requests the next motion event. + * If you don't call gdk_window_get_pointer() you'll only get + * a single motion event. The reason is that we specified + * GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events(). + * If we hadn't specified that, we could just use event->x, event->y + * as the pointer location. But we'd also get deluged in events. + * By requesting the next event as we handle the current one, + * we avoid getting a huge number of events faster than we + * can cope. + */ + + gdk_window_get_pointer (event->window, &x, &y, &state); + + if (state & GDK_BUTTON1_MASK) + draw_brush (widget, x, y); + + /* We've handled it, stop processing */ + return TRUE; +} + + +static gboolean +checkerboard_expose (GtkWidget *da, + GdkEventExpose *event, + gpointer data) +{ + gint i, j, xcount, ycount; + GdkGC *gc1, *gc2; + GdkColor color; + +#define CHECK_SIZE 10 +#define SPACING 2 + + /* At the start of an expose handler, a clip region of event->area + * is set on the window, and event->area has been cleared to the + * widget's background color. The docs for + * gdk_window_begin_paint_region() give more details on how this + * works. + */ + + /* It would be a bit more efficient to keep these + * GC's around instead of recreating on each expose, but + * this is the lazy/slow way. + */ + gc1 = gdk_gc_new (da->window); + color.red = 30000; + color.green = 0; + color.blue = 30000; + gdk_gc_set_rgb_fg_color (gc1, &color); + + gc2 = gdk_gc_new (da->window); + color.red = 65535; + color.green = 65535; + color.blue = 65535; + gdk_gc_set_rgb_fg_color (gc2, &color); + + xcount = 0; + i = SPACING; + while (i < da->allocation.width) + { + j = SPACING; + ycount = xcount % 2; /* start with even/odd depending on row */ + while (j < da->allocation.height) + { + GdkGC *gc; + + if (ycount % 2) + gc = gc1; + else + gc = gc2; + + /* If we're outside event->area, this will do nothing. + * It might be mildly more efficient if we handled + * the clipping ourselves, but again we're feeling lazy. + */ + gdk_draw_rectangle (da->window, + gc, + TRUE, + i, j, + CHECK_SIZE, + CHECK_SIZE); + + j += CHECK_SIZE + SPACING; + ++ycount; + } + + i += CHECK_SIZE + SPACING; + ++xcount; + } + + g_object_unref (G_OBJECT (gc1)); + g_object_unref (G_OBJECT (gc2)); + + /* return TRUE because we've handled this event, so no + * further processing is required. + */ + return TRUE; +} + +GtkWidget * +do_drawingarea (void) +{ + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *da; + GtkWidget *label; + + if (!window) + { + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "Drawing Area"); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &window); + + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + + vbox = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_container_add (GTK_CONTAINER (window), vbox); + + /* + * Create the checkerboard area + */ + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + "<u>Checkerboard pattern</u>"); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + + da = gtk_drawing_area_new (); + /* set a minimum size */ + gtk_widget_set_usize (da, 100, 100); + + gtk_container_add (GTK_CONTAINER (frame), da); + + gtk_signal_connect (GTK_OBJECT (da), + "expose_event", + GTK_SIGNAL_FUNC (checkerboard_expose), + NULL); + + /* + * Create the scribble area + */ + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + "<u>Scribble area</u>"); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + + da = gtk_drawing_area_new (); + /* set a minimum size */ + gtk_widget_set_usize (da, 100, 100); + + gtk_container_add (GTK_CONTAINER (frame), da); + + /* Signals used to handle backing pixmap */ + + gtk_signal_connect (GTK_OBJECT (da), "expose_event", + GTK_SIGNAL_FUNC (scribble_expose_event), NULL); + gtk_signal_connect (GTK_OBJECT (da),"configure_event", + GTK_SIGNAL_FUNC (scribble_configure_event), NULL); + + /* Event signals */ + + gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event", + GTK_SIGNAL_FUNC (scribble_motion_notify_event), NULL); + gtk_signal_connect (GTK_OBJECT (da), "button_press_event", + GTK_SIGNAL_FUNC (scribble_button_press_event), NULL); + + + /* Ask to receive events the drawing area doesn't normally + * subscribe to + */ + gtk_widget_set_events (da, gtk_widget_get_events (da) + | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK); + + } + + if (!GTK_WIDGET_VISIBLE (window)) + { + gtk_widget_show_all (window); + } + else + { + gtk_widget_destroy (window); + window = NULL; + } + + return window; +} |