123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- (* 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;
|