diff options
author | Havoc Pennington <hp@redhat.com> | 2001-04-18 18:09:18 +0000 |
---|---|---|
committer | Havoc Pennington <hp@src.gnome.org> | 2001-04-18 18:09:18 +0000 |
commit | 60b6a010e931aaaf97d723c893068381a421f0a0 (patch) | |
tree | 14982e448c4281c77bdfb5cd17add1a61fe72fce /demos/gtk-demo | |
parent | ebd3958c0641d82b54f26118155e4669725900e2 (diff) | |
download | gtk+-60b6a010e931aaaf97d723c893068381a421f0a0.tar.gz |
fix to properly queue resizes when the image is set
2001-04-18 Havoc Pennington <hp@redhat.com>
* gtk/gtkimage.c: fix to properly queue resizes when the image is
set
* gtk/gtktextview.c (gtk_text_view_do_popup): desensitize Paste
if the insertion point isn't editable
* demos/gtk-demo/images.c: Added a GtkImage demo
* demos/gtk-demo/drawingarea.c: drawing area demo
* demos/gtk-demo/menus.c (create_menu): cleanups
2001-04-18 Havoc Pennington <hp@redhat.com>
* gdk-pixbuf.c (gdk_pixbuf_fill): Function to fill pixbuf with a
given color.
Diffstat (limited to 'demos/gtk-demo')
-rw-r--r-- | demos/gtk-demo/Makefile.am | 22 | ||||
-rw-r--r-- | demos/gtk-demo/apple-red.png | bin | 0 -> 3545 bytes | |||
-rw-r--r-- | demos/gtk-demo/background.jpg | bin | 0 -> 22219 bytes | |||
-rw-r--r-- | demos/gtk-demo/button_box.c | 20 | ||||
-rw-r--r-- | demos/gtk-demo/drawingarea.c | 322 | ||||
-rw-r--r-- | demos/gtk-demo/gnome-applets.png | bin | 0 -> 3090 bytes | |||
-rw-r--r-- | demos/gtk-demo/gnome-calendar.png | bin | 0 -> 2755 bytes | |||
-rw-r--r-- | demos/gtk-demo/gnome-foot.png | bin | 0 -> 2916 bytes | |||
-rw-r--r-- | demos/gtk-demo/gnome-gimp.png | bin | 0 -> 3410 bytes | |||
-rw-r--r-- | demos/gtk-demo/gnome-gmush.png | bin | 0 -> 3244 bytes | |||
-rw-r--r-- | demos/gtk-demo/gnome-gsame.png | bin | 0 -> 4263 bytes | |||
-rw-r--r-- | demos/gtk-demo/gnu-keys.png | bin | 0 -> 3852 bytes | |||
-rw-r--r-- | demos/gtk-demo/gtk-logo-rgb.gif | bin | 0 -> 6459 bytes | |||
-rw-r--r-- | demos/gtk-demo/images.c | 396 | ||||
-rw-r--r-- | demos/gtk-demo/main.c | 31 | ||||
-rw-r--r-- | demos/gtk-demo/menus.c | 34 | ||||
-rw-r--r-- | demos/gtk-demo/pixbufs.c | 277 |
17 files changed, 1073 insertions, 29 deletions
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 6fa87fac72..b581f51e14 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -2,12 +2,17 @@ democodedir=$(datadir)/gtk-2.0/demo +## These should be in the order you want them to appear in the +## demo app, which means alphabetized by demo title, not filename demos = @STRIP_BEGIN@ \ button_box.c \ + dialog.c \ + drawingarea.c \ + images.c \ item_factory.c \ menus.c \ panes.c \ - dialog.c \ + pixbufs.c \ textview.c \ @STRIP_END@ @@ -63,4 +68,17 @@ gtk_demo_SOURCES = \ gtk_demo_DEPENDENCIES = $(DEPS) gtk_demo_LDADD = $(LDADDS) -democode_DATA = $(demos) +IMAGEFILES= apple-red.png \ + background.jpg \ + gnome-applets.png \ + gnome-calendar.png \ + gnome-foot.png \ + gnome-gimp.png \ + gnome-gmush.png \ + gnome-gsame.png \ + gnu-keys.png \ + gtk-logo-rgb.gif + +democode_DATA = $(demos) $(IMAGEFILES) + +EXTRA_DIST = $(IMAGEFILES) diff --git a/demos/gtk-demo/apple-red.png b/demos/gtk-demo/apple-red.png Binary files differnew file mode 100644 index 0000000000..b0a24e9418 --- /dev/null +++ b/demos/gtk-demo/apple-red.png diff --git a/demos/gtk-demo/background.jpg b/demos/gtk-demo/background.jpg Binary files differnew file mode 100644 index 0000000000..86c006aa46 --- /dev/null +++ b/demos/gtk-demo/background.jpg diff --git a/demos/gtk-demo/button_box.c b/demos/gtk-demo/button_box.c index a0fae11226..f8e7bd3838 100644 --- a/demos/gtk-demo/button_box.c +++ b/demos/gtk-demo/button_box.c @@ -72,19 +72,19 @@ do_button_box (void) gtk_container_add (GTK_CONTAINER (frame_horz), vbox); gtk_box_pack_start (GTK_BOX (vbox), - create_bbox (TRUE, "Spread", 40, GTK_BUTTONBOX_SPREAD), + create_bbox (TRUE, "Spread", 40, GTK_BUTTONBOX_SPREAD), TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), - create_bbox (TRUE, "Edge", 40, GTK_BUTTONBOX_EDGE), + create_bbox (TRUE, "Edge", 40, GTK_BUTTONBOX_EDGE), TRUE, TRUE, 5); - + gtk_box_pack_start (GTK_BOX (vbox), - create_bbox (TRUE, "Start", 40, GTK_BUTTONBOX_START), + create_bbox (TRUE, "Start", 40, GTK_BUTTONBOX_START), TRUE, TRUE, 5); - + gtk_box_pack_start (GTK_BOX (vbox), - create_bbox (TRUE, "End", 40, GTK_BUTTONBOX_END), + create_bbox (TRUE, "End", 40, GTK_BUTTONBOX_END), TRUE, TRUE, 5); frame_vert = gtk_frame_new ("Vertical Button Boxes"); @@ -95,19 +95,19 @@ do_button_box (void) gtk_container_add (GTK_CONTAINER (frame_vert), hbox); gtk_box_pack_start (GTK_BOX (hbox), - create_bbox (FALSE, "Spread", 30, GTK_BUTTONBOX_SPREAD), + create_bbox (FALSE, "Spread", 30, GTK_BUTTONBOX_SPREAD), TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), - create_bbox (FALSE, "Edge", 30, GTK_BUTTONBOX_EDGE), + create_bbox (FALSE, "Edge", 30, GTK_BUTTONBOX_EDGE), TRUE, TRUE, 5); gtk_box_pack_start (GTK_BOX (hbox), - create_bbox (FALSE, "Start", 30, GTK_BUTTONBOX_START), + create_bbox (FALSE, "Start", 30, GTK_BUTTONBOX_START), TRUE, TRUE, 5); gtk_box_pack_start (GTK_BOX (hbox), - create_bbox (FALSE, "End", 30, GTK_BUTTONBOX_END), + create_bbox (FALSE, "End", 30, GTK_BUTTONBOX_END), TRUE, TRUE, 5); } 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; +} diff --git a/demos/gtk-demo/gnome-applets.png b/demos/gtk-demo/gnome-applets.png Binary files differnew file mode 100644 index 0000000000..8d3549e97c --- /dev/null +++ b/demos/gtk-demo/gnome-applets.png diff --git a/demos/gtk-demo/gnome-calendar.png b/demos/gtk-demo/gnome-calendar.png Binary files differnew file mode 100644 index 0000000000..889f329ae5 --- /dev/null +++ b/demos/gtk-demo/gnome-calendar.png diff --git a/demos/gtk-demo/gnome-foot.png b/demos/gtk-demo/gnome-foot.png Binary files differnew file mode 100644 index 0000000000..0476658517 --- /dev/null +++ b/demos/gtk-demo/gnome-foot.png diff --git a/demos/gtk-demo/gnome-gimp.png b/demos/gtk-demo/gnome-gimp.png Binary files differnew file mode 100644 index 0000000000..f6bbc6d36c --- /dev/null +++ b/demos/gtk-demo/gnome-gimp.png diff --git a/demos/gtk-demo/gnome-gmush.png b/demos/gtk-demo/gnome-gmush.png Binary files differnew file mode 100644 index 0000000000..0a4b0d04e6 --- /dev/null +++ b/demos/gtk-demo/gnome-gmush.png diff --git a/demos/gtk-demo/gnome-gsame.png b/demos/gtk-demo/gnome-gsame.png Binary files differnew file mode 100644 index 0000000000..01c061151f --- /dev/null +++ b/demos/gtk-demo/gnome-gsame.png diff --git a/demos/gtk-demo/gnu-keys.png b/demos/gtk-demo/gnu-keys.png Binary files differnew file mode 100644 index 0000000000..58a33770e6 --- /dev/null +++ b/demos/gtk-demo/gnu-keys.png diff --git a/demos/gtk-demo/gtk-logo-rgb.gif b/demos/gtk-demo/gtk-logo-rgb.gif Binary files differnew file mode 100644 index 0000000000..f6e934d5e6 --- /dev/null +++ b/demos/gtk-demo/gtk-logo-rgb.gif diff --git a/demos/gtk-demo/images.c b/demos/gtk-demo/images.c new file mode 100644 index 0000000000..756046ecfd --- /dev/null +++ b/demos/gtk-demo/images.c @@ -0,0 +1,396 @@ +/* Images + * + * GtkImage is used to display an image; the image can be in a number of formats. + * Typically, you load an image into a GdkPixbuf, then display the pixbuf. + * + * This demo code shows some of the more obscure cases, in the simple + * case a call to gtk_image_new_from_file() is all you need. + * + * If you want to put image data in your program as a C variable, + * use the make-inline-pixbuf program that comes with GTK+. + */ + +#include <gtk/gtk.h> +#include <stdio.h> +#include <errno.h> + +static GtkWidget *window = NULL; +static GdkPixbufLoader *pixbuf_loader = NULL; +static guint load_timeout = 0; +static FILE* image_stream = NULL; + +static void +progressive_prepared_callback (GdkPixbufLoader* loader, gpointer data) +{ + GdkPixbuf* pixbuf; + GtkWidget* image; + + image = GTK_WIDGET (data); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + /* Avoid displaying random memory contents, since the pixbuf + * isn't filled in yet. + */ + gdk_pixbuf_fill (pixbuf, 0xaaaaaaff); + + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); +} + +static void +progressive_updated_callback (GdkPixbufLoader* loader, + guint x, guint y, guint width, guint height, + gpointer data) +{ + GtkWidget* image; + + image = GTK_WIDGET (data); + + /* We know the pixbuf inside the image has changed, but the image + * itself doesn't know this; so queue a redraw. If we wanted to be + * really efficient, we could use a drawing area or something + * instead of a GtkImage, so we could control the exact position of + * the pixbuf on the display, then we could queue a draw for only + * the updated area of the image. + */ + + gtk_widget_queue_draw (image); +} + +static gint +progressive_timeout (gpointer data) +{ + GtkWidget *image; + + image = GTK_WIDGET (data); + + /* This shows off fully-paranoid error handling, so looks scary. + * You could factor out the error handling code into a nice separate + * function to make things nicer. + */ + + if (image_stream) + { + size_t bytes_read; + guchar buf[256]; + GError *error = NULL; + + bytes_read = fread (buf, 1, 256, image_stream); + + if (ferror (image_stream)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failure reading image file 'gtk-logo-rgb.gif': %s", + g_strerror (errno)); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + fclose (image_stream); + image_stream = NULL; + + gtk_widget_show (dialog); + + load_timeout = 0; + + return FALSE; /* uninstall the timeout */ + } + + if (!gdk_pixbuf_loader_write (pixbuf_loader, + buf, bytes_read, + &error)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failed to load image: %s", + error->message); + + g_error_free (error); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + fclose (image_stream); + image_stream = NULL; + + gtk_widget_show (dialog); + + load_timeout = 0; + + return FALSE; /* uninstall the timeout */ + } + + if (feof (image_stream)) + { + fclose (image_stream); + image_stream = NULL; + + /* Errors can happen on close, e.g. if the image + * file was truncated we'll know on close that + * it was incomplete. + */ + error = NULL; + if (!gdk_pixbuf_loader_close (pixbuf_loader, + &error)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failed to load image: %s", + error->message); + + g_error_free (error); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_widget_show (dialog); + + g_object_unref (G_OBJECT (pixbuf_loader)); + pixbuf_loader = NULL; + + load_timeout = 0; + + return FALSE; /* uninstall the timeout */ + } + + g_object_unref (G_OBJECT (pixbuf_loader)); + pixbuf_loader = NULL; + } + } + else + { + const gchar *filename; + + if (g_file_test ("./gtk-logo-rgb.gif", G_FILE_TEST_EXISTS)) + filename = "./gtk-logo-rgb.gif"; + else + filename = DEMOCODEDIR"/gtk-log-rgb.gif"; + + image_stream = fopen (filename, "r"); + + if (image_stream == NULL) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Unable to open image file 'gtk-logo-rgb.gif': %s", + g_strerror (errno)); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_widget_show (dialog); + + load_timeout = 0; + + return FALSE; /* uninstall the timeout */ + } + + if (pixbuf_loader) + { + gdk_pixbuf_loader_close (pixbuf_loader, NULL); + g_object_unref (G_OBJECT (pixbuf_loader)); + pixbuf_loader = NULL; + } + + pixbuf_loader = gdk_pixbuf_loader_new (); + + g_signal_connect_data (G_OBJECT (pixbuf_loader), + "area_prepared", + G_CALLBACK (progressive_prepared_callback), + image, + NULL, FALSE, FALSE); + + g_signal_connect_data (G_OBJECT (pixbuf_loader), + "area_updated", + G_CALLBACK (progressive_updated_callback), + image, + NULL, FALSE, FALSE); + } + + /* leave timeout installed */ + return TRUE; +} + +static void +start_progressive_loading (GtkWidget *image) +{ + /* This is obviously totally contrived (we slow down loading + * on purpose to show how incremental loading works). + * The real purpose of incremental loading is the case where + * you are reading data from a slow source such as the network. + * The timeout simply simulates a slow data source by inserting + * pauses in the reading process. + */ + load_timeout = g_timeout_add (300, + progressive_timeout, + image); +} + +static void +cleanup_callback (GtkObject *object, + gpointer data) +{ + if (load_timeout) + { + g_source_remove (load_timeout); + load_timeout = 0; + } + + if (pixbuf_loader) + { + gdk_pixbuf_loader_close (pixbuf_loader, NULL); + g_object_unref (G_OBJECT (pixbuf_loader)); + pixbuf_loader = NULL; + } + + if (image_stream) + fclose (image_stream); + image_stream = NULL; +} + +GtkWidget * +do_images (void) +{ + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *image; + GtkWidget *label; + GtkWidget *align; + + if (!window) + { + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "Images"); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &window); + gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (cleanup_callback), NULL); + + 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); + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + "<u>Image loaded from a file</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); + /* The alignment keeps the frame from growing when users resize + * the window + */ + align = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (align), frame); + gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0); + + /* We look for the image in the current directory first, + * so you can run gtk-demo without installing GTK + */ + if (g_file_test ("./gtk-logo-rgb.gif", G_FILE_TEST_EXISTS)) + { + /* This code shows off error handling. You can just use + * gtk_image_new_from_file() instead if you don't want to report + * errors to the user. If the file doesn't load when using + * gtk_image_new_from_file(), a "missing image" icon will + * be displayed instead. + */ + GdkPixbuf *pixbuf; + GError *error = NULL; + + pixbuf = gdk_pixbuf_new_from_file ("./gtk-logo-rgb.gif", + &error); + if (error) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Unable to open image file 'gtk-logo-rgb.gif': %s", + error->message); + g_error_free (error); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_widget_show (dialog); + } + + image = gtk_image_new_from_pixbuf (pixbuf); + } + else + { + /* This is the simpler code, with no error handling. + * Here we're loading the installed gtk-logo-rgb.gif instead + * of the one in the current directory. + */ + image = gtk_image_new_from_file (DEMOCODEDIR"/gtk-logo-rgb.gif"); + } + + gtk_container_add (GTK_CONTAINER (frame), image); + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + "<u>Progressive image loading</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); + /* The alignment keeps the frame from growing when users resize + * the window + */ + align = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (align), frame); + gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0); + + /* Create an empty image for now; the progressive loader + * will create the pixbuf and fill it in. + */ + image = gtk_image_new_from_pixbuf (NULL); + gtk_container_add (GTK_CONTAINER (frame), image); + + start_progressive_loading (image); + } + + if (!GTK_WIDGET_VISIBLE (window)) + { + gtk_widget_show_all (window); + } + else + { + gtk_widget_destroy (window); + window = NULL; + } + + return window; +} diff --git a/demos/gtk-demo/main.c b/demos/gtk-demo/main.c index 0dfb4b1e26..fabf898d44 100644 --- a/demos/gtk-demo/main.c +++ b/demos/gtk-demo/main.c @@ -299,16 +299,18 @@ button_press_event_cb (GtkTreeView *tree_view, return FALSE; } -gboolean +void row_activated_cb (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkTreeModel *model) + GtkTreePath *path, + GtkTreeViewColumn *column) { GtkTreeIter iter; gboolean italic; GDoDemoFunc func; GtkWidget *window; + GtkTreeModel *model; + + model = gtk_tree_view_get_model (tree_view); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (GTK_TREE_MODEL (model), @@ -321,6 +323,7 @@ row_activated_cb (GtkTreeView *tree_view, ITALIC_COLUMN, !italic, -1); window = (func) (); + if (window != NULL) { CallbackData *cbdata; @@ -334,10 +337,6 @@ row_activated_cb (GtkTreeView *tree_view, window_closed_cb, cbdata); } - else - { - gtk_tree_path_free (path); - } } static void @@ -385,9 +384,20 @@ create_text (GtkTextBuffer **buffer, font_desc = pango_font_description_from_string ("Courier 10"); gtk_widget_modify_font (text_view, font_desc); pango_font_description_free (font_desc); + + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), + GTK_WRAP_NONE); + } + else + { + /* Make it a bit nicer for text. */ + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), + GTK_WRAP_WORD); + gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (text_view), + 2); + gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (text_view), + 2); } - - gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), !is_source); return scrolled_window; } @@ -458,6 +468,7 @@ main (int argc, char **argv) gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "GTK+ Code Demos"); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); diff --git a/demos/gtk-demo/menus.c b/demos/gtk-demo/menus.c index ac8a7e2c53..4788edd861 100644 --- a/demos/gtk-demo/menus.c +++ b/demos/gtk-demo/menus.c @@ -1,10 +1,31 @@ /* Menus * - * GTK+ includes a number of widgets for menus of actions. - * GtkMenu is a drop-down menu, GtkMenuBar a horizontal menu bar. - * Each of these widgets can hold various types of menuitem. - * As well as the base type, GtkMenuItem, there are GtkCheckMenuItem, - * GtkRadioMenuItem and GtkTearoffMenuItem. + * There are several widgets involved in displaying menus. The + * GtkMenuBar widget is a horizontal menu bar, which normally appears + * at the top of an application. The GtkMenu widget is the actual menu + * that pops up. Both GtkMenuBar and GtkMenu are subclasses of + * GtkMenuShell; a GtkMenuShell contains menu items + * (GtkMenuItem). Each menu item contains text and/or images and can + * be selected by the user. + * + * There are several kinds of menu item, including plain GtkMenuItem, + * GtkCheckMenuItem which can be checked/unchecked, GtkRadioMenuItem + * which is a check menu item that's in a mutually exclusive group, + * GtkSeparatorMenuItem which is a separator bar, GtkTearoffMenuItem + * which allows a GtkMenu to be torn off, and GtkImageMenuItem which + * can place a GtkImage or other widget next to the menu text. + * + * A GtkMenuItem can have a submenu, which is simply a GtkMenu to pop + * up when the menu item is selected. Typically, all menu items in a menu bar + * have submenus. + * + * The GtkOptionMenu widget is a button that pops up a GtkMenu when clicked. + * It's used inside dialogs and such. + * + * GtkItemFactory provides a higher-level interface for creating menu bars + * and menus; while you can construct menus manually, most people don't + * do that. There's a separate demo for GtkItemFactory. + * */ #include <stdio.h> @@ -39,8 +60,7 @@ create_menu (gint depth, gboolean tearoff) sprintf (buf, "item %2d - %d", depth, j); menuitem = gtk_radio_menu_item_new_with_label (group, buf); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem)); - if (depth % 2) - gtk_check_menu_item_set_show_toggle (GTK_CHECK_MENU_ITEM (menuitem), TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_widget_show (menuitem); if (i == 3) diff --git a/demos/gtk-demo/pixbufs.c b/demos/gtk-demo/pixbufs.c new file mode 100644 index 0000000000..6c067098ac --- /dev/null +++ b/demos/gtk-demo/pixbufs.c @@ -0,0 +1,277 @@ +/* Pixbufs + * + * A GdkPixbuf represents an image, normally in RGB or RGBA format. + * Pixbufs are normally used to load files from disk and perform + * image scaling. + * + * This demo is not all that educational, but looks cool. It was written + * by Extreme Pixbuf Hacker Federico Mena Quintero. It also shows + * off how to use GtkDrawingArea to do a simple animation. + * + * Look at the Image demo for additional pixbuf usage examples. + * + */ + +#include <config.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#include <math.h> + + + +#define FRAME_DELAY 50 + +#define RELATIVE_BACKGROUND_NAME "background.jpg" +#define INSTALLED_BACKGROUND_NAME DEMOCODEDIR"/background.jpg" + +static const char *relative_image_names[] = { + "apple-red.png", + "gnome-applets.png", + "gnome-calendar.png", + "gnome-foot.png", + "gnome-gmush.png", + "gnome-gimp.png", + "gnome-gsame.png", + "gnu-keys.png" +}; + +static const char *installed_image_names[] = { + DEMOCODEDIR"/apple-red.png", + DEMOCODEDIR"/gnome-applets.png", + DEMOCODEDIR"/gnome-calendar.png", + DEMOCODEDIR"/gnome-foot.png", + DEMOCODEDIR"/gnome-gmush.png", + DEMOCODEDIR"/gnome-gimp.png", + DEMOCODEDIR"/gnome-gsame.png", + DEMOCODEDIR"/gnu-keys.png" +}; + +#define N_IMAGES G_N_ELEMENTS (relative_image_names) + +/* demo window */ +static GtkWindow *window = NULL; + +/* Current frame */ +static GdkPixbuf *frame; + +/* Background image */ +static GdkPixbuf *background; +static int back_width, back_height; + +/* Images */ +static GdkPixbuf *images[N_IMAGES]; + +/* Widgets */ +static GtkWidget *da; + + + +/* Loads the images for the demo and returns whether the operation succeeded */ +static gboolean +load_pixbufs (GError **error) +{ + int i; + const char **image_names; + + if (background) + return TRUE; /* already loaded earlier */ + + background = gdk_pixbuf_new_from_file (RELATIVE_BACKGROUND_NAME, NULL); + + if (!background) + background = gdk_pixbuf_new_from_file (INSTALLED_BACKGROUND_NAME, error); + + if (!background) + return FALSE; /* note that "error" was filled in and returned */ + + back_width = gdk_pixbuf_get_width (background); + back_height = gdk_pixbuf_get_height (background); + + if (g_file_test (relative_image_names[0], G_FILE_TEST_EXISTS)) + image_names = relative_image_names; + else + image_names = installed_image_names; + + for (i = 0; i < N_IMAGES; i++) + { + images[i] = gdk_pixbuf_new_from_file (image_names[i], error); + if (!images[i]) + return FALSE; /* Note that "error" was filled with a GError */ + } + + return TRUE; +} + +/* Expose callback for the drawing area */ +static gint +expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + guchar *pixels; + int rowstride; + + rowstride = gdk_pixbuf_get_rowstride (frame); + + pixels = gdk_pixbuf_get_pixels (frame) + rowstride * event->area.y + event->area.x * 3; + + gdk_draw_rgb_image_dithalign (widget->window, + widget->style->black_gc, + event->area.x, event->area.y, + event->area.width, event->area.height, + GDK_RGB_DITHER_NORMAL, + pixels, rowstride, + event->area.x, event->area.y); + + return TRUE; +} + +#define CYCLE_LEN 60 + +static int frame_num; + +/* Timeout handler to regenerate the frame */ +static gint +timeout (gpointer data) +{ + double f; + int i; + double xmid, ymid; + double radius; + + gdk_pixbuf_copy_area (background, 0, 0, back_width, back_height, + frame, 0, 0); + + f = (double) (frame_num % CYCLE_LEN) / CYCLE_LEN; + + xmid = back_width / 2.0; + ymid = back_height / 2.0; + + radius = MIN (xmid, ymid) / 2.0; + + for (i = 0; i < N_IMAGES; i++) + { + double ang; + int xpos, ypos; + int iw, ih; + double r; + GdkRectangle r1, r2, dest; + double k; + + ang = 2.0 * M_PI * (double) i / N_IMAGES - f * 2.0 * M_PI; + + iw = gdk_pixbuf_get_width (images[i]); + ih = gdk_pixbuf_get_height (images[i]); + + r = radius + (radius / 3.0) * sin (f * 2.0 * M_PI); + + xpos = floor (xmid + r * cos (ang) - iw / 2.0 + 0.5); + ypos = floor (ymid + r * sin (ang) - ih / 2.0 + 0.5); + + k = (i & 1) ? sin (f * 2.0 * M_PI) : cos (f * 2.0 * M_PI); + k = 2.0 * k * k; + k = MAX (0.25, k); + + r1.x = xpos; + r1.y = ypos; + r1.width = iw * k; + r1.height = ih * k; + + r2.x = 0; + r2.y = 0; + r2.width = back_width; + r2.height = back_height; + + if (gdk_rectangle_intersect (&r1, &r2, &dest)) + gdk_pixbuf_composite (images[i], + frame, + dest.x, dest.y, + dest.width, dest.height, + xpos, ypos, + k, k, + GDK_INTERP_NEAREST, + ((i & 1) + ? MAX (127, fabs (255 * sin (f * 2.0 * M_PI))) + : MAX (127, fabs (255 * cos (f * 2.0 * M_PI))))); + } + + gtk_widget_queue_draw (da); + + frame_num++; + return TRUE; +} + +static guint timeout_id; + +static void +cleanup_callback (GtkObject *object, + gpointer data) +{ + g_source_remove (timeout_id); + timeout_id = 0; +} + +GtkWidget * +do_pixbufs (void) +{ + if (!window) + { + GError *error; + + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "Pixbufs"); + gtk_window_set_resizeable (GTK_WINDOW (window), FALSE); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &window); + gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (cleanup_callback), NULL); + + + error = NULL; + if (!load_pixbufs (&error)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failed to load an image: %s", + error->message); + + g_error_free (error); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_widget_show (dialog); + } + else + { + gtk_widget_set_usize (window, back_width, back_height); + + frame = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, back_width, back_height); + + da = gtk_drawing_area_new (); + + gtk_signal_connect (GTK_OBJECT (da), "expose_event", + GTK_SIGNAL_FUNC (expose_cb), NULL); + + gtk_container_add (GTK_CONTAINER (window), da); + + timeout_id = gtk_timeout_add (FRAME_DELAY, timeout, NULL); + } + } + + if (!GTK_WIDGET_VISIBLE (window)) + { + gtk_widget_show_all (window); + } + else + { + gtk_widget_destroy (window); + window = NULL; + } + + return window; +} |