diff options
Diffstat (limited to 'packages/gtk2/examples/gtk_demo/images.inc')
-rw-r--r-- | packages/gtk2/examples/gtk_demo/images.inc | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/packages/gtk2/examples/gtk_demo/images.inc b/packages/gtk2/examples/gtk_demo/images.inc new file mode 100644 index 0000000000..bc89affcbb --- /dev/null +++ b/packages/gtk2/examples/gtk_demo/images.inc @@ -0,0 +1,438 @@ +(* 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+. + * This way you won't need to depend on loading external files, your + * application binary can be self-contained. + *) + + +var + image_window : PGtkWidget; + image_pixbuf_loader : PGdkPixbufLoader; + image_load_timeout : guint; + image_stream : file; + +type + TBuffer256 = array [0..255] of byte; + +procedure progressive_prepared_callback (loader : PGdkPixbufLoader; + data : gpointer); cdecl; +var + pixbuf : PGdkPixbuf; + image : PGtkWidget; + +begin + image := PGtkWidget (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, $aaaaaaff); + + gtk_image_set_from_pixbuf (pGtkImage (image), pixbuf); +end; + +procedure progressive_updated_callback (loader : PGdkPixbufLoader; + x, y, + width, + height : gint; + data : gpointer); cdecl; +var + image : PGtkWidget; + +begin + image := PGtkWidget (data); + + (* We know the pixbuf inside the GtkImage 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); +end; + +function progressive_timeout (data : gpointer): gboolean; cdecl; +var + image : PGtkWidget; + buf : TBuffer256; + bytes_read : integer; + error : PGError; + dialog : PGtkWidget; + error_msg, + filename : pgchar; + +begin + image := PGtkWidget (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 file_is_valid (image_stream) then // is there a better way??? + begin + error := NULL; + + blockread (image_stream, buf, sizeof(buf), bytes_read); + + if not gdk_pixbuf_loader_write (image_pixbuf_loader, + @buf[0], bytes_read, @error) then + begin + dialog := gtk_message_dialog_new (GTK_WINDOW (image_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + 'Failed to load image: %s', + [error^.message]); + + g_error_free (error); + + g_signal_connect (dialog, 'response', + G_CALLBACK (@gtk_widget_destroy), NULL); + + close (image_stream); + + gtk_widget_show (dialog); + + image_load_timeout := 0; + + exit (FALSE); (* uninstall the timeout *) + end; {of not gdk_pixbuf_loader_write} + + if eof (image_stream) then + begin + close (image_stream); + + (* 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 not gdk_pixbuf_loader_close (image_pixbuf_loader, @error) then + begin + dialog := gtk_message_dialog_new (GTK_WINDOW (image_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + 'Failed to load image: %s', + [error^.message]); + + g_error_free (error); + + g_signal_connect (dialog, 'response', + G_CALLBACK (@gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + + g_object_unref (G_OBJECT (image_pixbuf_loader)); + image_pixbuf_loader := NULL; + + image_load_timeout := 0; + + exit(FALSE); (* uninstall the timeout *) + end; {of not gdk_pixbuf_loader_close} + + g_object_unref (G_OBJECT (image_pixbuf_loader)); + image_pixbuf_loader := NULL; + end; {of eof} + end {of image_stream} + else begin + error_msg := NULL; + + (* demo_find_file() looks in the the current directory first, + * so you can run gtk-demo without installing GTK, then looks + * in the location where the file is installed. + *) + + filename := demo_find_file ('alphatest.png', @error); + if error <> NULL then + begin + error_msg := g_strdup (error^.message); + g_error_free (error); + end else + begin + {$I-} + assign (image_stream, filename); + reset (image_stream, 1); + {$I+} + + if IOResult <> 0 then + error_msg := g_strdup_printf ('Error while opening file "%s"', + [filename]); + g_free (filename); + end; + + if not file_is_valid (image_stream) then + begin + dialog := gtk_message_dialog_new (GTK_WINDOW (image_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + '%s', [error_msg]); + g_free (error_msg); + + g_signal_connect (dialog, 'response', + G_CALLBACK (@gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + + image_load_timeout := 0; + + exit (FALSE); (* uninstall the timeout *) + end; + + if image_pixbuf_loader <> NULL then + begin + gdk_pixbuf_loader_close (image_pixbuf_loader, NULL); + g_object_unref (G_OBJECT (image_pixbuf_loader)); + image_pixbuf_loader := NULL; + end; + + image_pixbuf_loader := gdk_pixbuf_loader_new (); + + g_signal_connect (G_OBJECT (image_pixbuf_loader), 'area_prepared', + G_CALLBACK (@progressive_prepared_callback), image); + + g_signal_connect (G_OBJECT (image_pixbuf_loader), 'area_updated', + G_CALLBACK (@progressive_updated_callback), image); + end; {of else image_stream} + + (* leave timeout installed *) + exit (TRUE); +end; + +procedure start_progressive_loading (image : PGtkWidget); cdecl; +begin + (* 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. + *) + image_load_timeout := g_timeout_add (150, + @progressive_timeout, + image); +end; + +procedure images_cleanup_callback (theobject : PGtkObject; + data : gpointer); cdecl; +begin + if image_load_timeout <> 0 then + begin + g_source_remove (image_load_timeout); + image_load_timeout := 0; + end; + + if image_pixbuf_loader <> NULL then + begin + gdk_pixbuf_loader_close (image_pixbuf_loader, NULL); + g_object_unref (G_OBJECT (image_pixbuf_loader)); + image_pixbuf_loader := NULL; + end; + + if file_is_valid (image_stream) then + close (image_stream); +end; + +procedure toggle_sensitivity_callback (togglebutton : PGtkWidget; + user_data : gpointer);cdecl; +var + container : PGtkContainer; + list, + tmp : PGList; + +begin + container := PGtkContainer (user_data); + + list := gtk_container_get_children (container); + + tmp := list; + + while tmp <> NULL do + begin + (* don't disable our toggle *) + if pGtkWidget (tmp^.data) <> togglebutton then + gtk_widget_set_sensitive (pGtkWidget (tmp^.data), + not gtk_toggle_button_get_active (pGtkToggleButton (togglebutton))); + + tmp := tmp^.next; + end; + + g_list_free (list); +end; + + +function do_images : PGtkWidget; +var + frame, + vbox, + image, + thelabel, + align, + dialog, + button : PGtkWidget; + + pixbuf : PGdkPixbuf; + error : PGError; + filename : pgchar; + +begin + error := NULL; + + if image_window = NULL then + begin + image_window := gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (image_window), 'Images'); + + g_signal_connect (image_window, 'destroy', + G_CALLBACK (@gtk_widget_destroyed), @image_window); + g_signal_connect (image_window, 'destroy', + G_CALLBACK (@images_cleanup_callback), NULL); + + gtk_container_set_border_width (GTK_CONTAINER (image_window), 8); + + vbox := gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_container_add (GTK_CONTAINER (image_window), vbox); + + thelabel := gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (thelabel), + '<u>Image loaded from a file</u>'); + + gtk_box_pack_start (GTK_BOX (vbox), thelabel, 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); + + (* demo_find_file() looks in the the current directory first, + * so you can run gtk-demo without installing GTK, then looks + * in the location where the file is installed. + *) + + pixbuf := NULL; + filename := demo_find_file ('gtk-logo-rgb.gif', @error); + if filename <> NULL then + begin + pixbuf := gdk_pixbuf_new_from_file (filename, @error); + g_free (filename); + end; + + if error <> NULL then + begin + (* 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. + *) + + dialog := gtk_message_dialog_new (GTK_WINDOW (image_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); + + g_signal_connect (dialog, 'response', + G_CALLBACK (@gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + end; + + image := gtk_image_new_from_pixbuf (pixbuf); + + gtk_container_add (GTK_CONTAINER (frame), image); + + + (* Animation *) + + thelabel := gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (thelabel), + '<u>Animation loaded from a file</u>'); + gtk_box_pack_start (GTK_BOX (vbox), thelabel, 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); + + filename := demo_find_file ('floppybuddy.gif', NULL); + image := gtk_image_new_from_file (filename); + g_free (filename); + + gtk_container_add (GTK_CONTAINER (frame), image); + + + (* Progressive *) + + + thelabel := gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (thelabel), + '<u>Progressive image loading</u>'); + gtk_box_pack_start (GTK_BOX (vbox), thelabel, 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); + + (* Sensitivity control *) + button := gtk_toggle_button_new_with_mnemonic ('_Insensitive'); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + g_signal_connect (G_OBJECT (button), 'toggled', + G_CALLBACK (@toggle_sensitivity_callback), + vbox); + end; + + if not GTK_WIDGET_VISIBLE (image_window) then + gtk_widget_show_all (image_window) + else + begin + gtk_widget_destroy (image_window); + image_window := NULL; + end; + + do_images := image_window; +end; + |