gtkimage.c 34.1 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

Elliot Lee's avatar
Elliot Lee committed
27 28
#include "gtkcontainer.h"
#include "gtkimage.h"
29
#include "gtkiconfactory.h"
30
#include "gtkstock.h"
Havoc Pennington's avatar
Havoc Pennington committed
31
#include <string.h>
Elliot Lee's avatar
Elliot Lee committed
32

33 34 35 36
static void gtk_image_class_init   (GtkImageClass  *klass);
static void gtk_image_init         (GtkImage       *image);
static gint gtk_image_expose       (GtkWidget      *widget,
                                    GdkEventExpose *event);
Havoc Pennington's avatar
Havoc Pennington committed
37
static void gtk_image_unmap        (GtkWidget      *widget);
38 39
static void gtk_image_size_request (GtkWidget      *widget,
                                    GtkRequisition *requisition);
40
static void gtk_image_destroy      (GtkObject      *object);
41
static void gtk_image_clear        (GtkImage       *image);
42
static void gtk_image_reset        (GtkImage       *image);
43 44 45
static void gtk_image_update_size  (GtkImage       *image,
                                    gint            image_width,
                                    gint            image_height);
Elliot Lee's avatar
Elliot Lee committed
46

47
static gpointer parent_class;
Elliot Lee's avatar
Elliot Lee committed
48

49
GtkType
50
gtk_image_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
51
{
52
  static GtkType image_type = 0;
Elliot Lee's avatar
Elliot Lee committed
53 54 55

  if (!image_type)
    {
56
      static const GtkTypeInfo image_info =
Elliot Lee's avatar
Elliot Lee committed
57 58 59 60 61 62
      {
	"GtkImage",
	sizeof (GtkImage),
	sizeof (GtkImageClass),
	(GtkClassInitFunc) gtk_image_class_init,
	(GtkObjectInitFunc) gtk_image_init,
63 64
	/* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
65
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
66 67
      };

68
      image_type = gtk_type_unique (GTK_TYPE_MISC, &image_info);
Elliot Lee's avatar
Elliot Lee committed
69 70 71 72 73 74 75 76
    }

  return image_type;
}

static void
gtk_image_class_init (GtkImageClass *class)
{
77
  GtkObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
78 79
  GtkWidgetClass *widget_class;

80
  parent_class = g_type_class_peek_parent (class);
81 82

  object_class = (GtkObjectClass *) class;
83
  
84 85
  object_class->destroy = gtk_image_destroy;

Elliot Lee's avatar
Elliot Lee committed
86 87 88
  widget_class = (GtkWidgetClass*) class;

  widget_class->expose_event = gtk_image_expose;
89
  widget_class->size_request = gtk_image_size_request;
Havoc Pennington's avatar
Havoc Pennington committed
90
  widget_class->unmap = gtk_image_unmap;
Elliot Lee's avatar
Elliot Lee committed
91 92 93 94 95 96 97
}

static void
gtk_image_init (GtkImage *image)
{
  GTK_WIDGET_SET_FLAGS (image, GTK_NO_WINDOW);

98
  image->storage_type = GTK_IMAGE_EMPTY;
Elliot Lee's avatar
Elliot Lee committed
99 100
}

101 102 103 104 105 106 107 108 109 110 111
static void
gtk_image_destroy (GtkObject *object)
{
  GtkImage *image = GTK_IMAGE (object);

  gtk_image_clear (image);
  
  GTK_OBJECT_CLASS (parent_class)->destroy (object);
}


112 113 114 115 116 117 118 119 120 121 122 123 124
/**
 * gtk_image_new_from_pixmap:
 * @pixmap: a #GdkPixmap, or %NULL
 * @mask: a #GdkBitmap, or %NULL
 * 
 * Creates a #GtkImage widget displaying @pixmap with a @mask.
 * A #GdkImage is a server-side image buffer in the pixel format of the
 * current display. The #GtkImage does not assume a reference to the
 * pixmap or mask; you still need to unref them if you own references.
 * #GtkImage will add its own reference rather than adopting yours.
 * 
 * Return value: a new #GtkImage
 **/
Elliot Lee's avatar
Elliot Lee committed
125
GtkWidget*
126 127
gtk_image_new_from_pixmap (GdkPixmap *pixmap,
                           GdkBitmap *mask)
Elliot Lee's avatar
Elliot Lee committed
128 129 130
{
  GtkImage *image;

131 132 133 134 135 136 137
  image = gtk_type_new (GTK_TYPE_IMAGE);

  gtk_image_set_from_pixmap (image, pixmap, mask);

  return GTK_WIDGET (image);
}

138 139 140 141 142 143 144 145 146 147 148 149 150 151
/**
 * gtk_image_new_from_image:
 * @image: a #GdkImage, or %NULL
 * @mask: a #GdkBitmap, or %NULL 
 * 
 * Creates a #GtkImage widget displaying a @image with a @mask.
 * A #GdkImage is a client-side image buffer in the pixel format of the
 * current display.
 * The #GtkImage does not assume a reference to the
 * image or mask; you still need to unref them if you own references.
 * #GtkImage will add its own reference rather than adopting yours.
 * 
 * Return value: a new #GtkImage
 **/
152 153 154 155 156
GtkWidget*
gtk_image_new_from_image  (GdkImage  *gdk_image,
                           GdkBitmap *mask)
{
  GtkImage *image;
Elliot Lee's avatar
Elliot Lee committed
157

158
  image = gtk_type_new (GTK_TYPE_IMAGE);
Elliot Lee's avatar
Elliot Lee committed
159

160 161 162 163 164
  gtk_image_set_from_image (image, gdk_image, mask);

  return GTK_WIDGET (image);
}

165 166 167 168
/**
 * gtk_image_new_from_file:
 * @filename: a filename
 * 
Havoc Pennington's avatar
Havoc Pennington committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
 * Creates a new #GtkImage displaying the file @filename. If the file
 * isn't found or can't be loaded, the resulting #GtkImage will
 * display a "broken image" icon. This function never returns %NULL,
 * it always returns a valid #GtkImage widget.
 *
 * If the file contains an animation, the image will contain an
 * animation.
 *
 * If you need to detect failures to load the file, use
 * gdk_pixbuf_new_from_file() to load the file yourself, then create
 * the #GtkImage from the pixbuf. (Or for animations, use
 * gdk_pixbuf_animation_new_from_file()).
 *
 * The storage type (gtk_image_get_storage_type()) of the returned
 * image is not defined, it will be whatever is appropriate for
 * displaying the file.
185 186 187
 * 
 * Return value: a new #GtkImage
 **/
188 189 190 191 192 193 194 195 196 197 198 199
GtkWidget*
gtk_image_new_from_file   (const gchar *filename)
{
  GtkImage *image;

  image = gtk_type_new (GTK_TYPE_IMAGE);

  gtk_image_set_from_file (image, filename);

  return GTK_WIDGET (image);
}

200 201 202 203 204 205 206 207 208
/**
 * gtk_image_new_from_pixbuf:
 * @pixbuf: a #GdkPixbuf, or %NULL
 * 
 * Creates a new #GtkImage displaying @pixbuf.
 * The #GtkImage does not assume a reference to the
 * pixbuf; you still need to unref it if you own references.
 * #GtkImage will add its own reference rather than adopting yours.
 * 
209 210
 * Note that this function just creates an #GtkImage from the pixbuf.  The
 * #GtkImage created will not react to state changes.  Should you want that, you
Havoc Pennington's avatar
Havoc Pennington committed
211
 * should use gtk_image_new_from_icon_set().
212
 * 
213 214
 * Return value: a new #GtkImage
 **/
215 216 217 218 219 220 221 222 223 224 225 226
GtkWidget*
gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf)
{
  GtkImage *image;

  image = gtk_type_new (GTK_TYPE_IMAGE);

  gtk_image_set_from_pixbuf (image, pixbuf);

  return GTK_WIDGET (image);  
}

227 228 229 230 231 232
/**
 * gtk_image_new_from_stock:
 * @stock_id: a stock icon name
 * @size: a stock icon size
 * 
 * Creates a #GtkImage displaying a stock icon. Sample stock icon
233
 * names are #GTK_STOCK_OPEN, #GTK_STOCK_EXIT. Sample stock sizes
234 235
 * are #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_SMALL_TOOLBAR. If the stock
 * icon name isn't known, a "broken image" icon will be displayed instead.
236 237
 * You can register your own stock icon names, see
 * gtk_icon_factory_add_default() and gtk_icon_factory_add().
238 239 240
 * 
 * Return value: a new #GtkImage displaying the stock icon
 **/
241 242
GtkWidget*
gtk_image_new_from_stock (const gchar    *stock_id,
243
                          GtkIconSize     size)
244 245 246 247 248 249 250 251 252 253
{
  GtkImage *image;

  image = gtk_type_new (GTK_TYPE_IMAGE);

  gtk_image_set_from_stock (image, stock_id, size);

  return GTK_WIDGET (image);
}

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
/**
 * gtk_image_new_from_icon_set:
 * @icon_set: a #GtkIconSet
 * @size: a stock icon size
 *
 * Creates a #GtkImage displaying an icon set. Sample stock sizes are
 * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_SMALL_TOOLBAR. Instead of using
 * this function, usually it's better to create a #GtkIconFactory, put
 * your icon sets in the icon factory, add the icon factory to the
 * list of default factories with gtk_icon_factory_add_default(), and
 * then use gtk_image_new_from_stock(). This will allow themes to
 * override the icon you ship with your application.
 *
 * The #GtkImage does not assume a reference to the
 * icon set; you still need to unref it if you own references.
 * #GtkImage will add its own reference rather than adopting yours.
 * 
 * 
 * Return value: a new #GtkImage
 **/
274 275
GtkWidget*
gtk_image_new_from_icon_set (GtkIconSet     *icon_set,
276
                             GtkIconSize     size)
277 278 279 280 281 282
{
  GtkImage *image;

  image = gtk_type_new (GTK_TYPE_IMAGE);

  gtk_image_set_from_icon_set (image, icon_set, size);
Elliot Lee's avatar
Elliot Lee committed
283 284 285 286

  return GTK_WIDGET (image);
}

Havoc Pennington's avatar
Havoc Pennington committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
/**
 * gtk_image_new_from_animation:
 * @animation: an animation
 * 
 * Creates a #GtkImage displaying the given animation.
 * The #GtkImage does not assume a reference to the
 * animation; you still need to unref it if you own references.
 * #GtkImage will add its own reference rather than adopting yours.
 * 
 * Return value: a new #GtkImage widget
 **/
GtkWidget*
gtk_image_new_from_animation (GdkPixbufAnimation *animation)
{
  GtkImage *image;

  g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL);
  
  image = gtk_type_new (GTK_TYPE_IMAGE);

  gtk_image_set_from_animation (image, animation);

  return GTK_WIDGET (image);
}

312 313 314 315 316 317 318 319 320
/**
 * gtk_image_set_from_pixmap:
 * @image: a #GtkImage
 * @pixmap: a #GdkPixmap or %NULL
 * @mask: a #GdkBitmap or %NULL
 *
 * See gtk_image_new_from_pixmap() for details.
 * 
 **/
Elliot Lee's avatar
Elliot Lee committed
321
void
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
gtk_image_set_from_pixmap (GtkImage  *image,
                           GdkPixmap *pixmap,
                           GdkBitmap *mask)
{
  g_return_if_fail (GTK_IS_IMAGE (image));
  g_return_if_fail (pixmap == NULL ||
                    GDK_IS_PIXMAP (pixmap));
  g_return_if_fail (mask == NULL ||
                    GDK_IS_PIXMAP (mask));
  
  if (pixmap)
    g_object_ref (G_OBJECT (pixmap));

  if (mask)
    g_object_ref (G_OBJECT (mask));

338
  gtk_image_reset (image);
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

  if (pixmap)
    {
      int width;
      int height;
      
      image->storage_type = GTK_IMAGE_PIXMAP;

      image->data.pixmap.pixmap = pixmap;
      image->data.pixmap.mask = mask;

      gdk_drawable_get_size (GDK_DRAWABLE (pixmap), &width, &height);

      gtk_image_update_size (image, width, height);
    }
  else
    {
      /* Clean up the mask if pixmap was NULL */
      if (mask)
        g_object_unref (G_OBJECT (mask));
    }
}

362 363 364 365 366 367 368 369 370
/**
 * gtk_image_set_from_image:
 * @image: a #GtkImage
 * @gdk_image: a #GdkImage or %NULL
 * @mask: a #GdkBitmap or %NULL
 *
 * See gtk_image_new_from_image() for details.
 * 
 **/
371 372 373 374
void
gtk_image_set_from_image  (GtkImage  *image,
                           GdkImage  *gdk_image,
                           GdkBitmap *mask)
Elliot Lee's avatar
Elliot Lee committed
375 376
{
  g_return_if_fail (GTK_IS_IMAGE (image));
377 378 379 380 381 382 383 384 385 386 387
  g_return_if_fail (gdk_image == NULL ||
                    GDK_IS_IMAGE (gdk_image));
  g_return_if_fail (mask == NULL ||
                    GDK_IS_PIXMAP (mask));

  
  if (gdk_image)
    g_object_ref (G_OBJECT (gdk_image));

  if (mask)
    g_object_ref (G_OBJECT (mask));
Elliot Lee's avatar
Elliot Lee committed
388

389
  gtk_image_reset (image);
Elliot Lee's avatar
Elliot Lee committed
390

391
  if (gdk_image)
Elliot Lee's avatar
Elliot Lee committed
392
    {
393 394 395 396 397 398
      image->storage_type = GTK_IMAGE_IMAGE;

      image->data.image.image = gdk_image;
      image->data.image.mask = mask;

      gtk_image_update_size (image, gdk_image->width, gdk_image->height);
Elliot Lee's avatar
Elliot Lee committed
399 400 401
    }
  else
    {
402 403 404
      /* Clean up the mask if gdk_image was NULL */
      if (mask)
        g_object_unref (G_OBJECT (mask));
Elliot Lee's avatar
Elliot Lee committed
405
    }
406
}
Elliot Lee's avatar
Elliot Lee committed
407

408 409 410 411 412 413 414 415
/**
 * gtk_image_set_from_file:
 * @image: a #GtkImage
 * @filename: a filename or %NULL
 *
 * See gtk_image_new_from_file() for details.
 * 
 **/
416 417 418 419
void
gtk_image_set_from_file   (GtkImage    *image,
                           const gchar *filename)
{
Havoc Pennington's avatar
Havoc Pennington committed
420
  GdkPixbufAnimation *anim;
421 422 423 424
  
  g_return_if_fail (GTK_IS_IMAGE (image));
  g_return_if_fail (filename != NULL);
  
425
  gtk_image_reset (image);
426 427 428 429

  if (filename == NULL)
    return;
  
Havoc Pennington's avatar
Havoc Pennington committed
430
  anim = gdk_pixbuf_animation_new_from_file (filename, NULL);
431

Havoc Pennington's avatar
Havoc Pennington committed
432
  if (anim == NULL)
433 434 435 436 437 438
    {
      gtk_image_set_from_stock (image,
                                GTK_STOCK_MISSING_IMAGE,
                                GTK_ICON_SIZE_BUTTON);
      return;
    }
439

Havoc Pennington's avatar
Havoc Pennington committed
440 441 442 443
  /* We could just unconditionally set_from_animation,
   * but it's nicer for memory if we toss the animation
   * if it's just a single pixbuf
   */
444

Havoc Pennington's avatar
Havoc Pennington committed
445 446 447 448 449 450 451 452 453 454 455
  if (gdk_pixbuf_animation_is_static_image (anim))
    {
      gtk_image_set_from_pixbuf (image,
                                 gdk_pixbuf_animation_get_static_image (anim));
    }
  else
    {
      gtk_image_set_from_animation (image, anim);
    }

  g_object_unref (G_OBJECT (anim));
456 457
}

458 459 460 461 462 463 464 465
/**
 * gtk_image_set_from_pixbuf:
 * @image: a #GtkImage
 * @pixbuf: a #GdkPixbuf or %NULL
 *
 * See gtk_image_new_from_pixbuf() for details. 
 * 
 **/
466 467 468 469 470 471 472 473 474 475 476
void
gtk_image_set_from_pixbuf (GtkImage  *image,
                           GdkPixbuf *pixbuf)
{
  g_return_if_fail (GTK_IS_IMAGE (image));
  g_return_if_fail (pixbuf == NULL ||
                    GDK_IS_PIXBUF (pixbuf));
  
  if (pixbuf)
    g_object_ref (G_OBJECT (pixbuf));

477
  gtk_image_reset (image);
478 479 480 481 482 483 484 485 486 487 488 489 490

  if (pixbuf != NULL)
    {
      image->storage_type = GTK_IMAGE_PIXBUF;

      image->data.pixbuf.pixbuf = pixbuf;

      gtk_image_update_size (image,
                             gdk_pixbuf_get_width (pixbuf),
                             gdk_pixbuf_get_height (pixbuf));
    }
}

491 492 493 494 495 496 497 498 499
/**
 * gtk_image_set_from_stock:
 * @image: a #GtkImage
 * @stock_id: a stock icon name
 * @size: a stock icon size
 *
 * See gtk_image_new_from_stock for details.
 * 
 **/
500 501 502
void
gtk_image_set_from_stock  (GtkImage       *image,
                           const gchar    *stock_id,
503
                           GtkIconSize     size)
504 505 506
{
  g_return_if_fail (GTK_IS_IMAGE (image));
  
507
  gtk_image_reset (image);
508 509 510 511 512 513

  if (stock_id)
    {      
      image->storage_type = GTK_IMAGE_STOCK;
      
      image->data.stock.stock_id = g_strdup (stock_id);
514
      image->data.stock.size = size;
515 516 517 518 519 520 521 522

      /* Size is demand-computed in size request method
       * if we're a stock image, since changing the
       * style impacts the size request
       */
    }
}

523 524 525 526 527 528 529 530 531
/**
 * gtk_image_set_from_icon_set:
 * @image: a #GtkImage
 * @icon_set: a #GtkIconSet
 * @size: a stock icon size
 *
 * See gtk_image_new_from_icon_set() for details.
 * 
 **/
532 533 534
void
gtk_image_set_from_icon_set  (GtkImage       *image,
                              GtkIconSet     *icon_set,
535
                              GtkIconSize     size)
536 537 538 539 540 541
{
  g_return_if_fail (GTK_IS_IMAGE (image));

  if (icon_set)
    gtk_icon_set_ref (icon_set);
  
542
  gtk_image_reset (image);
543 544 545 546 547 548

  if (icon_set)
    {      
      image->storage_type = GTK_IMAGE_ICON_SET;
      
      image->data.icon_set.icon_set = icon_set;
549
      image->data.icon_set.size = size;
550 551 552 553 554 555 556

      /* Size is demand-computed in size request method
       * if we're an icon set
       */
    }
}

Havoc Pennington's avatar
Havoc Pennington committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
/**
 * gtk_image_set_from_animation:
 * @image: a #GtkImage
 * @animation: the #GdkPixbufAnimation
 * 
 * Causes the #GtkImage to display the given animation (or display
 * nothing, if you set the animation to %NULL).
 **/
void
gtk_image_set_from_animation (GtkImage           *image,
                              GdkPixbufAnimation *animation)
{
  g_return_if_fail (GTK_IS_IMAGE (image));
  g_return_if_fail (animation == NULL ||
                    GDK_IS_PIXBUF_ANIMATION (animation));
  
  if (animation)
    g_object_ref (G_OBJECT (animation));

  gtk_image_reset (image);

  if (animation != NULL)
    {
      image->storage_type = GTK_IMAGE_ANIMATION;

      image->data.anim.anim = animation;
      image->data.anim.frame_timeout = 0;
      image->data.anim.iter = NULL;
      
      gtk_image_update_size (image,
                             gdk_pixbuf_animation_get_width (animation),
                             gdk_pixbuf_animation_get_height (animation));
    }
}

592 593 594 595 596 597 598 599 600 601
/**
 * gtk_image_get_storage_type:
 * @image: a #GtkImage
 * 
 * Gets the type of representation being used by the #GtkImage
 * to store image data. If the #GtkImage has no image data,
 * the return value will be %GTK_IMAGE_EMPTY.
 * 
 * Return value: image representation being used
 **/
602 603 604 605 606 607 608 609
GtkImageType
gtk_image_get_storage_type (GtkImage *image)
{
  g_return_val_if_fail (GTK_IS_IMAGE (image), GTK_IMAGE_EMPTY);

  return image->storage_type;
}

610 611 612 613 614 615 616 617 618 619 620 621 622
/**
 * gtk_image_get_pixmap:
 * @image: a #GtkImage
 * @pixmap: location to store the pixmap, or %NULL
 * @mask: location to store the mask, or %NULL
 *
 * Gets the pixmap and mask being displayed by the #GtkImage.
 * The storage type of the image must be %GTK_IMAGE_EMPTY or
 * %GTK_IMAGE_PIXMAP (see gtk_image_get_storage_type()).
 * The caller of this function does not own a reference to the
 * returned pixmap and mask.
 * 
 **/
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
void
gtk_image_get_pixmap (GtkImage   *image,
                      GdkPixmap **pixmap,
                      GdkBitmap **mask)
{
  g_return_if_fail (GTK_IS_IMAGE (image)); 
  g_return_if_fail (image->storage_type == GTK_IMAGE_PIXMAP ||
                    image->storage_type == GTK_IMAGE_EMPTY);
  
  if (pixmap)
    *pixmap = image->data.pixmap.pixmap;
  
  if (mask)
    *mask = image->data.pixmap.mask;
}

639 640 641 642 643 644 645 646 647 648 649 650
/**
 * gtk_image_get_image:
 * @image: a #GtkImage
 * @gdk_image: return location for a #GtkImage
 * @mask: return location for a #GdkBitmap
 * 
 * Gets the #GdkImage and mask being displayed by the #GtkImage.
 * The storage type of the image must be %GTK_IMAGE_EMPTY or
 * %GTK_IMAGE_IMAGE (see gtk_image_get_storage_type()).
 * The caller of this function does not own a reference to the
 * returned image and mask.
 **/
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
void
gtk_image_get_image  (GtkImage   *image,
                      GdkImage  **gdk_image,
                      GdkBitmap **mask)
{
  g_return_if_fail (GTK_IS_IMAGE (image));
  g_return_if_fail (image->storage_type == GTK_IMAGE_IMAGE ||
                    image->storage_type == GTK_IMAGE_EMPTY);

  if (gdk_image)
    *gdk_image = image->data.image.image;
  
  if (mask)
    *mask = image->data.image.mask;
}

667 668 669 670 671 672 673 674 675 676 677 678 679
/**
 * gtk_image_get_pixbuf:
 * @image: a #GtkImage
 *
 *
 * Gets the #GdkPixbuf being displayed by the #GtkImage.
 * The storage type of the image must be %GTK_IMAGE_EMPTY or
 * %GTK_IMAGE_PIXBUF (see gtk_image_get_storage_type()).
 * The caller of this function does not own a reference to the
 * returned pixbuf.
 * 
 * Return value: the displayed pixbuf, or %NULL if the image is empty
 **/
680 681 682 683 684 685 686 687 688 689 690 691 692
GdkPixbuf*
gtk_image_get_pixbuf (GtkImage *image)
{
  g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
  g_return_val_if_fail (image->storage_type == GTK_IMAGE_PIXBUF ||
                        image->storage_type == GTK_IMAGE_EMPTY, NULL);

  if (image->storage_type == GTK_IMAGE_EMPTY)
    image->data.pixbuf.pixbuf = NULL;
  
  return image->data.pixbuf.pixbuf;
}

693 694 695 696 697 698 699 700 701
/**
 * gtk_image_get_stock:
 * @image: a #GtkImage
 * @stock_id: place to store a stock icon name
 * @size: place to store a stock icon size
 *
 * Gets the stock icon name and size being displayed by the #GtkImage.
 * The storage type of the image must be %GTK_IMAGE_EMPTY or
 * %GTK_IMAGE_STOCK (see gtk_image_get_storage_type()).
702
 * The returned string is owned by the #GtkImage and should not
703 704 705
 * be freed.
 * 
 **/
706 707 708
void
gtk_image_get_stock  (GtkImage        *image,
                      gchar          **stock_id,
709
                      GtkIconSize     *size)
710 711 712 713 714 715 716 717 718
{
  g_return_if_fail (GTK_IS_IMAGE (image));
  g_return_if_fail (image->storage_type == GTK_IMAGE_STOCK ||
                    image->storage_type == GTK_IMAGE_EMPTY);

  if (image->storage_type == GTK_IMAGE_EMPTY)
    image->data.stock.stock_id = NULL;
  
  if (stock_id)
719
    *stock_id = image->data.stock.stock_id;
720 721 722 723 724

  if (size)
    *size = image->data.stock.size;
}

725 726 727 728 729 730 731 732 733 734 735
/**
 * gtk_image_get_icon_set:
 * @image: a #GtkImage
 * @icon_set: location to store a #GtkIconSet
 * @size: location to store a stock icon size
 *
 * Gets the icon set and size being displayed by the #GtkImage.
 * The storage type of the image must be %GTK_IMAGE_EMPTY or
 * %GTK_IMAGE_ICON_SET (see gtk_image_get_storage_type()).
 * 
 **/
736 737 738
void
gtk_image_get_icon_set  (GtkImage        *image,
                         GtkIconSet     **icon_set,
739
                         GtkIconSize     *size)
740 741 742 743 744 745 746 747 748
{
  g_return_if_fail (GTK_IS_IMAGE (image));
  g_return_if_fail (image->storage_type == GTK_IMAGE_ICON_SET ||
                    image->storage_type == GTK_IMAGE_EMPTY);
      
  if (icon_set)    
    *icon_set = image->data.icon_set.icon_set;

  if (size)
749
    *size = image->data.icon_set.size;
750 751
}

Havoc Pennington's avatar
Havoc Pennington committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
/**
 * gtk_image_get_animation:
 * @image: a #GtkImage
 *
 *
 * Gets the #GdkPixbufAnimation being displayed by the #GtkImage.
 * The storage type of the image must be %GTK_IMAGE_EMPTY or
 * %GTK_IMAGE_ANIMATION (see gtk_image_get_storage_type()).
 * The caller of this function does not own a reference to the
 * returned animation.
 * 
 * Return value: the displayed animation, or %NULL if the image is empty
 **/
GdkPixbufAnimation*
gtk_image_get_animation (GtkImage *image)
{
  g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
  g_return_val_if_fail (image->storage_type == GTK_IMAGE_ANIMATION ||
                        image->storage_type == GTK_IMAGE_EMPTY,
                        NULL);

  if (image->storage_type == GTK_IMAGE_EMPTY)
    image->data.anim.anim = NULL;
  
  return image->data.anim.anim;
}

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
GtkWidget*
gtk_image_new (GdkImage  *val,
	       GdkBitmap *mask)
{
  GtkImage *image;

  g_return_val_if_fail (val != NULL, NULL);

  image = gtk_type_new (GTK_TYPE_IMAGE);

  gtk_image_set (image, val, mask);

  return GTK_WIDGET (image);
}

void
gtk_image_set (GtkImage  *image,
	       GdkImage  *val,
	       GdkBitmap *mask)
{
  g_return_if_fail (GTK_IS_IMAGE (image));

  gtk_image_set_from_image (image, val, mask);
Elliot Lee's avatar
Elliot Lee committed
802 803 804 805 806 807 808 809 810
}

void
gtk_image_get (GtkImage   *image,
	       GdkImage  **val,
	       GdkBitmap **mask)
{
  g_return_if_fail (GTK_IS_IMAGE (image));

811
  gtk_image_get_image (image, val, mask);
Elliot Lee's avatar
Elliot Lee committed
812 813
}

Havoc Pennington's avatar
Havoc Pennington committed
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
static void
gtk_image_unmap (GtkWidget *widget)
{
  GtkImage *image;

  image = GTK_IMAGE (widget);

  if (image->storage_type == GTK_IMAGE_ANIMATION)
    {
      /* Reset the animation */
      
      if (image->data.anim.frame_timeout)
        {
          g_source_remove (image->data.anim.frame_timeout);
          image->data.anim.frame_timeout = 0;
        }

      if (image->data.anim.iter)
        {
          g_object_unref (G_OBJECT (image->data.anim.iter));
          image->data.anim.iter = NULL;
        }
    }

  if (GTK_WIDGET_CLASS (parent_class)->unmap)
    GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}

gint
animation_timeout (gpointer data)
{
  GtkImage *image;

847 848
  GDK_THREADS_ENTER ();

Havoc Pennington's avatar
Havoc Pennington committed
849 850 851 852 853 854 855 856 857 858 859 860 861 862
  image = GTK_IMAGE (data);
  
  image->data.anim.frame_timeout = 0;

  gdk_pixbuf_animation_iter_advance (image->data.anim.iter, NULL);

  if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0)
    image->data.anim.frame_timeout =
      g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter),
                     animation_timeout,
                     image);
  
  gtk_widget_queue_draw (GTK_WIDGET (image));

863 864
  GDK_THREADS_LEAVE ();

Havoc Pennington's avatar
Havoc Pennington committed
865 866
  return FALSE;
}
Elliot Lee's avatar
Elliot Lee committed
867 868 869 870 871 872 873 874

static gint
gtk_image_expose (GtkWidget      *widget,
		  GdkEventExpose *event)
{
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_IMAGE (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
875 876 877
  
  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
      GTK_IMAGE (widget)->storage_type != GTK_IMAGE_EMPTY)
Elliot Lee's avatar
Elliot Lee committed
878
    {
879 880
      GtkImage *image;
      GtkMisc *misc;
881
      GdkRectangle area, image_bound;
882
      gint x, y;
883 884
      GdkBitmap *mask = NULL;
      GdkPixbuf *stock_pixbuf = NULL;
885
      
Elliot Lee's avatar
Elliot Lee committed
886 887 888 889 890 891 892 893 894 895 896 897
      image = GTK_IMAGE (widget);
      misc = GTK_MISC (widget);

      x = (widget->allocation.x * (1.0 - misc->xalign) +
	   (widget->allocation.x + widget->allocation.width
	    - (widget->requisition.width - misc->xpad * 2)) *
	   misc->xalign) + 0.5;
      y = (widget->allocation.y * (1.0 - misc->yalign) +
	   (widget->allocation.y + widget->allocation.height
	    - (widget->requisition.height - misc->ypad * 2)) *
	   misc->yalign) + 0.5;

898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
      image_bound.x = x;
      image_bound.y = y;      

      switch (image->storage_type)
        {
        case GTK_IMAGE_PIXMAP:
          mask = image->data.pixmap.mask;
          gdk_drawable_get_size (image->data.pixmap.pixmap,
                                 &image_bound.width,
                                 &image_bound.height);
          break;

        case GTK_IMAGE_IMAGE:
          mask = image->data.image.mask;
          image_bound.width = image->data.image.image->width;
          image_bound.height = image->data.image.image->height;
          break;

        case GTK_IMAGE_PIXBUF:
          image_bound.width = gdk_pixbuf_get_width (image->data.pixbuf.pixbuf);
          image_bound.height = gdk_pixbuf_get_height (image->data.pixbuf.pixbuf);
          break;

        case GTK_IMAGE_STOCK:
922 923 924 925
          stock_pixbuf = gtk_widget_render_icon (widget,
                                                 image->data.stock.stock_id,
                                                 image->data.stock.size,
                                                 NULL);
926
          if (stock_pixbuf)
927
            {              
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
              image_bound.width = gdk_pixbuf_get_width (stock_pixbuf);
              image_bound.height = gdk_pixbuf_get_height (stock_pixbuf);
            }
          break;

        case GTK_IMAGE_ICON_SET:
          stock_pixbuf =
            gtk_icon_set_render_icon (image->data.icon_set.icon_set,
                                      widget->style,
                                      gtk_widget_get_direction (widget),
                                      GTK_WIDGET_STATE (widget),
                                      image->data.icon_set.size,
                                      widget,
                                      NULL);

          if (stock_pixbuf)
            {
              image_bound.width = gdk_pixbuf_get_width (stock_pixbuf);
              image_bound.height = gdk_pixbuf_get_height (stock_pixbuf);
            }
          break;
Havoc Pennington's avatar
Havoc Pennington committed
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966

        case GTK_IMAGE_ANIMATION:
          {
            if (image->data.anim.iter == NULL)
              {
                image->data.anim.iter = gdk_pixbuf_animation_get_iter (image->data.anim.anim, NULL);
                
                if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0)
                  image->data.anim.frame_timeout =
                    g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter),
                                   animation_timeout,
                                   image);
              }

            image_bound.width = gdk_pixbuf_animation_get_width (image->data.anim.anim);
            image_bound.height = gdk_pixbuf_animation_get_height (image->data.anim.anim);
          }
          break;
967 968 969 970 971 972
          
        default:
          break;
        }

      if (mask)
Elliot Lee's avatar
Elliot Lee committed
973
	{
974
	  gdk_gc_set_clip_mask (widget->style->black_gc, mask);
Elliot Lee's avatar
Elliot Lee committed
975 976 977
	  gdk_gc_set_clip_origin (widget->style->black_gc, x, y);
	}

978 979
      area = event->area;
      
980 981
      if (gdk_rectangle_intersect (&area, &widget->allocation, &area) &&
          gdk_rectangle_intersect (&image_bound, &area, &image_bound))
982
        {
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
          switch (image->storage_type)
            {
            case GTK_IMAGE_PIXMAP:
              gdk_draw_drawable (widget->window,
                                 widget->style->black_gc,
                                 image->data.pixmap.pixmap,
                                 image_bound.x - x, image_bound.y - y,
                                 image_bound.x, image_bound.y,
                                 image_bound.width, image_bound.height);
              break;
              
            case GTK_IMAGE_IMAGE:
              gdk_draw_image (widget->window,
                              widget->style->black_gc,
                              image->data.image.image,
                              image_bound.x - x, image_bound.y - y,
                              image_bound.x, image_bound.y,
                              image_bound.width, image_bound.height);
              break;

            case GTK_IMAGE_PIXBUF:
              gdk_pixbuf_render_to_drawable_alpha (image->data.pixbuf.pixbuf,
                                                   widget->window,
1006 1007 1008 1009
                                                   image_bound.x - x,
                                                   image_bound.y - y,
                                                   image_bound.x,
                                                   image_bound.y,
1010 1011
                                                   image_bound.width,
                                                   image_bound.height,
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
                                                   GDK_PIXBUF_ALPHA_FULL,
                                                   128,
                                                   GDK_RGB_DITHER_NORMAL,
                                                   0, 0);
              break;

            case GTK_IMAGE_STOCK: /* fall thru */
            case GTK_IMAGE_ICON_SET:
              if (stock_pixbuf)
                {
                  gdk_pixbuf_render_to_drawable_alpha (stock_pixbuf,
                                                       widget->window,
1024 1025
                                                       image_bound.x - x,
                                                       image_bound.y - y,
1026 1027 1028 1029
                                                       image_bound.x,
                                                       image_bound.y,
                                                       image_bound.width,
                                                       image_bound.height,
1030 1031 1032 1033 1034 1035 1036 1037 1038
                                                       GDK_PIXBUF_ALPHA_FULL,
                                                       128,
                                                       GDK_RGB_DITHER_NORMAL,
                                                       0, 0);
                  
                  g_object_unref (G_OBJECT (stock_pixbuf));
                }
              break;

Havoc Pennington's avatar
Havoc Pennington committed
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
            case GTK_IMAGE_ANIMATION:
              /* don't advance the anim iter here, or we could get frame changes between two
               * exposes of different areas.
               */
              
              gdk_pixbuf_render_to_drawable_alpha (gdk_pixbuf_animation_iter_get_pixbuf (image->data.anim.iter),
                                                   widget->window,
                                                   image_bound.x - x,
                                                   image_bound.y - y,
                                                   image_bound.x,
                                                   image_bound.y,
                                                   image_bound.width,
                                                   image_bound.height,
                                                   GDK_PIXBUF_ALPHA_FULL,
                                                   128,
                                                   GDK_RGB_DITHER_NORMAL,
                                                   0, 0);
              break;
              
1058 1059 1060 1061 1062 1063 1064 1065
            default:
              break;
            }
        } /* if rectangle intersects */      
      if (mask)
        {
          gdk_gc_set_clip_mask (widget->style->black_gc, NULL);
          gdk_gc_set_clip_origin (widget->style->black_gc, 0, 0);
1066
        }
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
    } /* if widget is drawable */

  return FALSE;
}

static void
gtk_image_clear (GtkImage *image)
{
  switch (image->storage_type)
    {
    case GTK_IMAGE_PIXMAP:

      if (image->data.pixmap.pixmap)
        g_object_unref (G_OBJECT (image->data.pixmap.pixmap));

      if (image->data.pixmap.mask)
        g_object_unref (G_OBJECT (image->data.pixmap.mask));

      break;

    case GTK_IMAGE_IMAGE:

      if (image->data.image.image)
        g_object_unref (G_OBJECT (image->data.image.image));

      if (image->data.image.mask)
        g_object_unref (G_OBJECT (image->data.image.mask));

      break;

    case GTK_IMAGE_PIXBUF:

      if (image->data.pixbuf.pixbuf)
        g_object_unref (G_OBJECT (image->data.pixbuf.pixbuf));

      break;

    case GTK_IMAGE_STOCK:

      g_free (image->data.stock.stock_id);
      
      break;

    case GTK_IMAGE_ICON_SET:
      if (image->data.icon_set.icon_set)
        gtk_icon_set_unref (image->data.icon_set.icon_set);
      
      break;
Havoc Pennington's avatar
Havoc Pennington committed
1115 1116 1117 1118 1119 1120 1121 1122

    case GTK_IMAGE_ANIMATION:
      if (image->data.anim.frame_timeout)
        g_source_remove (image->data.anim.frame_timeout);

      if (image->data.anim.anim)
        g_object_unref (G_OBJECT (image->data.anim.anim));      
      break;
1123 1124 1125 1126
      
    case GTK_IMAGE_EMPTY:
    default:
      break;
1127
      
Elliot Lee's avatar
Elliot Lee committed
1128 1129
    }

1130
  image->storage_type = GTK_IMAGE_EMPTY;
Havoc Pennington's avatar
Havoc Pennington committed
1131 1132

  memset (&image->data, '\0', sizeof (image->data));
1133
}
1134

1135 1136 1137 1138
static void
gtk_image_reset (GtkImage *image)
{
  gtk_image_clear (image);
1139 1140

  gtk_image_update_size (image, 0, 0);
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
}

static void
gtk_image_size_request (GtkWidget      *widget,
                        GtkRequisition *requisition)
{
  GtkImage *image;
  GdkPixbuf *pixbuf = NULL;
  
  image = GTK_IMAGE (widget);

1152 1153 1154 1155 1156 1157
  /* We update stock/icon set on every size request, because
   * the theme could have affected the size; for other kinds of
   * image, we just update the requisition when the image data
   * is set.
   */
  
1158 1159 1160
  switch (image->storage_type)
    {
    case GTK_IMAGE_STOCK:
1161 1162 1163 1164
      pixbuf = gtk_widget_render_icon (GTK_WIDGET (image),
                                       image->data.stock.stock_id,
                                       image->data.stock.size,
                                       NULL);
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
      break;

    case GTK_IMAGE_ICON_SET:
      pixbuf = gtk_icon_set_render_icon (image->data.icon_set.icon_set,
                                         widget->style,
                                         gtk_widget_get_direction (widget),
                                         GTK_WIDGET_STATE (widget),
                                         image->data.icon_set.size,
                                         widget,
                                         NULL);
      break;
      
    default:
      break;
    }

  if (pixbuf)
    {
1183 1184 1185
      GTK_WIDGET (image)->requisition.width = gdk_pixbuf_get_width (pixbuf) + GTK_MISC (image)->xpad * 2;
      GTK_WIDGET (image)->requisition.height = gdk_pixbuf_get_height (pixbuf) + GTK_MISC (image)->ypad * 2;

1186 1187 1188 1189 1190
      g_object_unref (G_OBJECT (pixbuf));
    }

  /* Chain up to default that simply reads current requisition */
  GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
Elliot Lee's avatar
Elliot Lee committed
1191
}
1192 1193 1194 1195 1196 1197 1198 1199

static void
gtk_image_update_size (GtkImage *image,
                       gint      image_width,
                       gint      image_height)
{
  GTK_WIDGET (image)->requisition.width = image_width + GTK_MISC (image)->xpad * 2;
  GTK_WIDGET (image)->requisition.height = image_height + GTK_MISC (image)->ypad * 2;
1200 1201 1202

  if (GTK_WIDGET_VISIBLE (image))
    gtk_widget_queue_resize (GTK_WIDGET (image));
1203 1204 1205
}