You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

748 lines
21 KiB

  1. /*
  2. * Copyright (C) 2008-2010 Nick Schermer <nick@xfce.org>
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. #include <config.h>
  20. #endif
  21. #ifdef HAVE_MATH_H
  22. #include <math.h>
  23. #endif
  24. #ifdef HAVE_STRING_H
  25. #include <string.h>
  26. #endif
  27. #include <gtk/gtk.h>
  28. #include <libxfce4util/libxfce4util.h>
  29. #include <common/panel-private.h>
  30. #include <libxfce4panel/xfce-panel-macros.h>
  31. #include <libxfce4panel/xfce-panel-image.h>
  32. #include <libxfce4panel/xfce-panel-convenience.h>
  33. #include <libxfce4panel/libxfce4panel-alias.h>
  34. /**
  35. * SECTION: xfce-panel-image
  36. * @title: XfcePanelImage
  37. * @short_description: Scalable image suitable for panel plugins
  38. * @include: libxfce4panel/libxfce4panel.h
  39. *
  40. * The #XfcePanelImage is a widgets suitable for for example panel
  41. * buttons where the developer does not exacly know the size of the
  42. * image (due to theming and user setting).
  43. *
  44. * The #XfcePanelImage widget automatically scales to the allocated
  45. * size of the widget. Because of that nature it never requests a size,
  46. * so this will only work if you pack the image in another widget
  47. * that will expand it.
  48. * If you want to force an image size you can use xfce_panel_image_set_size()
  49. * to set a pixel size, in that case the widget will request an fixed size
  50. * which makes it usefull for usage in dialogs.
  51. **/
  52. /* design limit for the panel, to reduce the uncached pixbuf size */
  53. #define MAX_PIXBUF_SIZE (128)
  54. #define xfce_panel_image_unref_null(obj) G_STMT_START { if ((obj) != NULL) \
  55. { \
  56. g_object_unref (G_OBJECT (obj)); \
  57. (obj) = NULL; \
  58. } } G_STMT_END
  59. struct _XfcePanelImagePrivate
  60. {
  61. /* pixbuf set by the user */
  62. GdkPixbuf *pixbuf;
  63. /* internal cached pixbuf (resized) */
  64. GdkPixbuf *cache;
  65. /* source name */
  66. gchar *source;
  67. /* fixed size */
  68. gint size;
  69. /* whether we round to fixed icon sizes */
  70. guint force_icon_sizes : 1;
  71. /* cached width and height */
  72. gint width;
  73. gint height;
  74. /* idle load timeout */
  75. guint idle_load_id;
  76. };
  77. enum
  78. {
  79. PROP_0,
  80. PROP_SOURCE,
  81. PROP_PIXBUF,
  82. PROP_SIZE
  83. };
  84. static void xfce_panel_image_get_property (GObject *object,
  85. guint prop_id,
  86. GValue *value,
  87. GParamSpec *pspec);
  88. static void xfce_panel_image_set_property (GObject *object,
  89. guint prop_id,
  90. const GValue *value,
  91. GParamSpec *pspec);
  92. static void xfce_panel_image_finalize (GObject *object);
  93. static void xfce_panel_image_size_request (GtkWidget *widget,
  94. GtkRequisition *requisition);
  95. static void xfce_panel_image_size_allocate (GtkWidget *widget,
  96. GtkAllocation *allocation);
  97. static gboolean xfce_panel_image_expose_event (GtkWidget *widget,
  98. GdkEventExpose *event);
  99. static void xfce_panel_image_style_set (GtkWidget *widget,
  100. GtkStyle *previous_style);
  101. static gboolean xfce_panel_image_load (gpointer data);
  102. static void xfce_panel_image_load_destroy (gpointer data);
  103. static GdkPixbuf *xfce_panel_image_scale_pixbuf (GdkPixbuf *source,
  104. gint dest_width,
  105. gint dest_height);
  106. G_DEFINE_TYPE (XfcePanelImage, xfce_panel_image, GTK_TYPE_WIDGET)
  107. static void
  108. xfce_panel_image_class_init (XfcePanelImageClass *klass)
  109. {
  110. GObjectClass *gobject_class;
  111. GtkWidgetClass *gtkwidget_class;
  112. g_type_class_add_private (klass, sizeof (XfcePanelImagePrivate));
  113. gobject_class = G_OBJECT_CLASS (klass);
  114. gobject_class->get_property = xfce_panel_image_get_property;
  115. gobject_class->set_property = xfce_panel_image_set_property;
  116. gobject_class->finalize = xfce_panel_image_finalize;
  117. gtkwidget_class = GTK_WIDGET_CLASS (klass);
  118. gtkwidget_class->size_request = xfce_panel_image_size_request;
  119. gtkwidget_class->size_allocate = xfce_panel_image_size_allocate;
  120. gtkwidget_class->expose_event = xfce_panel_image_expose_event;
  121. gtkwidget_class->style_set = xfce_panel_image_style_set;
  122. g_object_class_install_property (gobject_class,
  123. PROP_SOURCE,
  124. g_param_spec_string ("source",
  125. "Source",
  126. "Icon or filename",
  127. NULL,
  128. G_PARAM_READWRITE
  129. | G_PARAM_STATIC_STRINGS));
  130. g_object_class_install_property (gobject_class,
  131. PROP_PIXBUF,
  132. g_param_spec_object ("pixbuf",
  133. "Pixbuf",
  134. "Pixbuf image",
  135. GDK_TYPE_PIXBUF,
  136. G_PARAM_READWRITE
  137. | G_PARAM_STATIC_STRINGS));
  138. g_object_class_install_property (gobject_class,
  139. PROP_SIZE,
  140. g_param_spec_int ("size",
  141. "Size",
  142. "Pixel size of the image",
  143. -1, MAX_PIXBUF_SIZE, -1,
  144. G_PARAM_READWRITE
  145. | G_PARAM_STATIC_STRINGS));
  146. gtk_widget_class_install_style_property (gtkwidget_class,
  147. g_param_spec_boolean ("force-gtk-icon-sizes",
  148. NULL,
  149. "Force the image to fix to GtkIconSizes",
  150. FALSE,
  151. G_PARAM_READWRITE
  152. | G_PARAM_STATIC_STRINGS));
  153. }
  154. static void
  155. xfce_panel_image_init (XfcePanelImage *image)
  156. {
  157. GTK_WIDGET_SET_FLAGS (image, GTK_NO_WINDOW);
  158. image->priv = G_TYPE_INSTANCE_GET_PRIVATE (image, XFCE_TYPE_PANEL_IMAGE, XfcePanelImagePrivate);
  159. image->priv->pixbuf = NULL;
  160. image->priv->cache = NULL;
  161. image->priv->source = NULL;
  162. image->priv->size = -1;
  163. image->priv->width = -1;
  164. image->priv->height = -1;
  165. image->priv->force_icon_sizes = FALSE;
  166. }
  167. static void
  168. xfce_panel_image_get_property (GObject *object,
  169. guint prop_id,
  170. GValue *value,
  171. GParamSpec *pspec)
  172. {
  173. XfcePanelImagePrivate *priv = XFCE_PANEL_IMAGE (object)->priv;
  174. switch (prop_id)
  175. {
  176. case PROP_SOURCE:
  177. g_value_set_string (value, priv->source);
  178. break;
  179. case PROP_PIXBUF:
  180. g_value_set_object (value, priv->pixbuf);
  181. break;
  182. case PROP_SIZE:
  183. g_value_set_int (value, priv->size);
  184. break;
  185. default:
  186. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  187. break;
  188. }
  189. }
  190. static void
  191. xfce_panel_image_set_property (GObject *object,
  192. guint prop_id,
  193. const GValue *value,
  194. GParamSpec *pspec)
  195. {
  196. switch (prop_id)
  197. {
  198. case PROP_SOURCE:
  199. xfce_panel_image_set_from_source (XFCE_PANEL_IMAGE (object),
  200. g_value_get_string (value));
  201. break;
  202. case PROP_PIXBUF:
  203. xfce_panel_image_set_from_pixbuf (XFCE_PANEL_IMAGE (object),
  204. g_value_get_object (value));
  205. break;
  206. case PROP_SIZE:
  207. xfce_panel_image_set_size (XFCE_PANEL_IMAGE (object),
  208. g_value_get_int (value));
  209. break;
  210. default:
  211. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  212. break;
  213. }
  214. }
  215. static void
  216. xfce_panel_image_finalize (GObject *object)
  217. {
  218. xfce_panel_image_clear (XFCE_PANEL_IMAGE (object));
  219. (*G_OBJECT_CLASS (xfce_panel_image_parent_class)->finalize) (object);
  220. }
  221. static void
  222. xfce_panel_image_size_request (GtkWidget *widget,
  223. GtkRequisition *requisition)
  224. {
  225. XfcePanelImagePrivate *priv = XFCE_PANEL_IMAGE (widget)->priv;
  226. if (priv->size > 0)
  227. {
  228. requisition->width = priv->size;
  229. requisition->height = priv->size;
  230. }
  231. else if (priv->pixbuf != NULL)
  232. {
  233. requisition->width = gdk_pixbuf_get_width (priv->pixbuf);
  234. requisition->height = gdk_pixbuf_get_height (priv->pixbuf);
  235. }
  236. else
  237. {
  238. requisition->width = widget->allocation.width;
  239. requisition->height = widget->allocation.height;
  240. }
  241. }
  242. static void
  243. xfce_panel_image_size_allocate (GtkWidget *widget,
  244. GtkAllocation *allocation)
  245. {
  246. XfcePanelImagePrivate *priv = XFCE_PANEL_IMAGE (widget)->priv;
  247. widget->allocation = *allocation;
  248. /* check if the available size changed */
  249. if ((priv->pixbuf != NULL || priv->source != NULL)
  250. && allocation->width > 0
  251. && allocation->height > 0
  252. && (allocation->width != priv->width
  253. || allocation->height != priv->height))
  254. {
  255. /* store the new size */
  256. priv->width = allocation->width;
  257. priv->height = allocation->height;
  258. /* free cache */
  259. xfce_panel_image_unref_null (priv->cache);
  260. if (priv->pixbuf == NULL)
  261. {
  262. /* delay icon loading */
  263. priv->idle_load_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, xfce_panel_image_load,
  264. widget, xfce_panel_image_load_destroy);
  265. }
  266. else
  267. {
  268. /* directly render pixbufs */
  269. xfce_panel_image_load (widget);
  270. }
  271. }
  272. }
  273. static gboolean
  274. xfce_panel_image_expose_event (GtkWidget *widget,
  275. GdkEventExpose *event)
  276. {
  277. XfcePanelImagePrivate *priv = XFCE_PANEL_IMAGE (widget)->priv;
  278. gint source_width, source_height;
  279. gint dest_x, dest_y;
  280. GtkIconSource *source;
  281. GdkPixbuf *rendered = NULL;
  282. GdkPixbuf *pixbuf = priv->cache;
  283. cairo_t *cr;
  284. if (G_LIKELY (pixbuf != NULL))
  285. {
  286. /* get the size of the cache pixbuf */
  287. source_width = gdk_pixbuf_get_width (pixbuf);
  288. source_height = gdk_pixbuf_get_height (pixbuf);
  289. /* position */
  290. dest_x = widget->allocation.x + (priv->width - source_width) / 2;
  291. dest_y = widget->allocation.y + (priv->height - source_height) / 2;
  292. if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE)
  293. {
  294. source = gtk_icon_source_new ();
  295. gtk_icon_source_set_pixbuf (source, pixbuf);
  296. rendered = gtk_style_render_icon (widget->style,
  297. source,
  298. gtk_widget_get_direction (widget),
  299. GTK_WIDGET_STATE (widget),
  300. -1, widget, "xfce-panel-image");
  301. gtk_icon_source_free (source);
  302. if (G_LIKELY (rendered != NULL))
  303. pixbuf = rendered;
  304. }
  305. /* draw the pixbuf */
  306. cr = gdk_cairo_create (gtk_widget_get_window (widget));
  307. if (G_LIKELY (cr != NULL))
  308. {
  309. gdk_cairo_set_source_pixbuf (cr, pixbuf, dest_x, dest_y);
  310. cairo_paint (cr);
  311. cairo_destroy (cr);
  312. }
  313. if (rendered != NULL)
  314. g_object_unref (G_OBJECT (rendered));
  315. }
  316. return FALSE;
  317. }
  318. static void
  319. xfce_panel_image_style_set (GtkWidget *widget,
  320. GtkStyle *previous_style)
  321. {
  322. XfcePanelImagePrivate *priv = XFCE_PANEL_IMAGE (widget)->priv;
  323. gboolean force;
  324. /* let gtk update the widget style */
  325. (*GTK_WIDGET_CLASS (xfce_panel_image_parent_class)->style_set) (widget, previous_style);
  326. /* get style property */
  327. gtk_widget_style_get (widget, "force-gtk-icon-sizes", &force, NULL);
  328. /* update if needed */
  329. if (priv->force_icon_sizes != force)
  330. {
  331. priv->force_icon_sizes = force;
  332. if (priv->size > 0)
  333. gtk_widget_queue_resize (widget);
  334. }
  335. /* update the icon if we have an icon-name source */
  336. if (previous_style != NULL && priv->source != NULL
  337. && !g_path_is_absolute (priv->source))
  338. {
  339. /* unset the size to force an update */
  340. priv->width = priv->height = -1;
  341. gtk_widget_queue_resize (widget);
  342. }
  343. }
  344. static gboolean
  345. xfce_panel_image_load (gpointer data)
  346. {
  347. XfcePanelImagePrivate *priv = XFCE_PANEL_IMAGE (data)->priv;
  348. GdkPixbuf *pixbuf;
  349. GdkScreen *screen;
  350. GtkIconTheme *icon_theme = NULL;
  351. gint dest_w, dest_h;
  352. GDK_THREADS_ENTER ();
  353. dest_w = priv->width;
  354. dest_h = priv->height;
  355. if (G_UNLIKELY (priv->force_icon_sizes
  356. && dest_w < 32
  357. && dest_w == dest_h))
  358. {
  359. /* we use some hardcoded values here for convienence,
  360. * above 32 pixels svg icons will kick in */
  361. if (dest_w > 16 && dest_w < 22)
  362. dest_w = 16;
  363. else if (dest_w > 22 && dest_w < 24)
  364. dest_w = 22;
  365. else if (dest_w > 24 && dest_w < 32)
  366. dest_w = 24;
  367. dest_h = dest_w;
  368. }
  369. if (priv->pixbuf != NULL)
  370. {
  371. /* use the pixbuf set by the user */
  372. pixbuf = g_object_ref (G_OBJECT (priv->pixbuf));
  373. if (G_LIKELY (pixbuf != NULL))
  374. {
  375. /* scale the icon to the correct size */
  376. priv->cache = xfce_panel_image_scale_pixbuf (pixbuf, dest_w, dest_h);
  377. g_object_unref (G_OBJECT (pixbuf));
  378. }
  379. }
  380. else
  381. {
  382. screen = gtk_widget_get_screen (GTK_WIDGET (data));
  383. if (G_LIKELY (screen != NULL))
  384. icon_theme = gtk_icon_theme_get_for_screen (screen);
  385. priv->cache = xfce_panel_pixbuf_from_source_at_size (priv->source, icon_theme, dest_w, dest_h);
  386. }
  387. if (G_LIKELY (priv->cache != NULL))
  388. gtk_widget_queue_draw (GTK_WIDGET (data));
  389. GDK_THREADS_LEAVE ();
  390. return FALSE;
  391. }
  392. static void
  393. xfce_panel_image_load_destroy (gpointer data)
  394. {
  395. XFCE_PANEL_IMAGE (data)->priv->idle_load_id = 0;
  396. }
  397. static GdkPixbuf *
  398. xfce_panel_image_scale_pixbuf (GdkPixbuf *source,
  399. gint dest_width,
  400. gint dest_height)
  401. {
  402. gdouble wratio;
  403. gdouble hratio;
  404. gint source_width;
  405. gint source_height;
  406. panel_return_val_if_fail (GDK_IS_PIXBUF (source), NULL);
  407. /* we fail on invalid sizes */
  408. if (G_UNLIKELY (dest_width <= 0 || dest_height <= 0))
  409. return NULL;
  410. source_width = gdk_pixbuf_get_width (source);
  411. source_height = gdk_pixbuf_get_height (source);
  412. /* check if we need to scale */
  413. if (source_width <= dest_width && source_height <= dest_height)
  414. return g_object_ref (G_OBJECT (source));
  415. /* calculate the new dimensions */
  416. wratio = (gdouble) source_width / (gdouble) dest_width;
  417. hratio = (gdouble) source_height / (gdouble) dest_height;
  418. if (hratio > wratio)
  419. dest_width = rint (source_width / hratio);
  420. else
  421. dest_height = rint (source_height / wratio);
  422. return gdk_pixbuf_scale_simple (source, MAX (dest_width, 1),
  423. MAX (dest_height, 1),
  424. GDK_INTERP_BILINEAR);
  425. }
  426. /**
  427. * xfce_panel_image_new:
  428. *
  429. * Creates a new empty #XfcePanelImage widget.
  430. *
  431. * returns: a newly created XfcePanelImage widget.
  432. *
  433. * Since: 4.8
  434. **/
  435. GtkWidget *
  436. xfce_panel_image_new (void)
  437. {
  438. return g_object_new (XFCE_TYPE_PANEL_IMAGE, NULL);
  439. }
  440. /**
  441. * xfce_panel_image_new_from_pixbuf:
  442. * @pixbuf : a #GdkPixbuf, or %NULL.
  443. *
  444. * Creates a new #XfcePanelImage displaying @pixbuf. #XfcePanelImage
  445. * will add its own reference rather than adopting yours. You don't
  446. * need to scale the pixbuf to the correct size, the #XfcePanelImage
  447. * will take care of that based on the allocation of the widget or
  448. * the size set with xfce_panel_image_set_size().
  449. *
  450. * returns: a newly created XfcePanelImage widget.
  451. *
  452. * Since: 4.8
  453. **/
  454. GtkWidget *
  455. xfce_panel_image_new_from_pixbuf (GdkPixbuf *pixbuf)
  456. {
  457. g_return_val_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf), NULL);
  458. return g_object_new (XFCE_TYPE_PANEL_IMAGE,
  459. "pixbuf", pixbuf, NULL);
  460. }
  461. /**
  462. * xfce_panel_image_new_from_source:
  463. * @source : source of the image. This can be an absolute path or
  464. * an icon-name or %NULL.
  465. *
  466. * Creates a new #XfcePanelImage displaying @source. #XfcePanelImage
  467. * will detect if @source points to an absolute file or it and icon-name.
  468. * For icon-names it will also look for files in the pixbuf folder or
  469. * strip the extensions, which makes it suitable for usage with icon
  470. * keys in .desktop files.
  471. *
  472. * returns: a newly created XfcePanelImage widget.
  473. *
  474. * Since: 4.8
  475. **/
  476. GtkWidget *
  477. xfce_panel_image_new_from_source (const gchar *source)
  478. {
  479. g_return_val_if_fail (source == NULL || *source != '\0', NULL);
  480. return g_object_new (XFCE_TYPE_PANEL_IMAGE,
  481. "source", source, NULL);
  482. }
  483. /**
  484. * xfce_panel_image_set_from_pixbuf:
  485. * @image : an #XfcePanelImage.
  486. * @pixbuf : a #GdkPixbuf, or %NULL.
  487. *
  488. * See xfce_panel_image_new_from_pixbuf() for details.
  489. *
  490. * Since: 4.8
  491. **/
  492. void
  493. xfce_panel_image_set_from_pixbuf (XfcePanelImage *image,
  494. GdkPixbuf *pixbuf)
  495. {
  496. g_return_if_fail (XFCE_IS_PANEL_IMAGE (image));
  497. g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
  498. xfce_panel_image_clear (image);
  499. /* set the new pixbuf, scale it to the maximum size if needed */
  500. image->priv->pixbuf = xfce_panel_image_scale_pixbuf (pixbuf,
  501. MAX_PIXBUF_SIZE, MAX_PIXBUF_SIZE);
  502. gtk_widget_queue_resize (GTK_WIDGET (image));
  503. }
  504. /**
  505. * xfce_panel_image_set_from_source:
  506. * @image : an #XfcePanelImage.
  507. * @source : source of the image. This can be an absolute path or
  508. * an icon-name or %NULL.
  509. *
  510. * See xfce_panel_image_new_from_source() for details.
  511. *
  512. * Since: 4.8
  513. **/
  514. void
  515. xfce_panel_image_set_from_source (XfcePanelImage *image,
  516. const gchar *source)
  517. {
  518. g_return_if_fail (XFCE_IS_PANEL_IMAGE (image));
  519. g_return_if_fail (source == NULL || *source != '\0');
  520. xfce_panel_image_clear (image);
  521. image->priv->source = g_strdup (source);
  522. gtk_widget_queue_resize (GTK_WIDGET (image));
  523. }
  524. /**
  525. * xfce_panel_image_set_size:
  526. * @image : an #XfcePanelImage.
  527. * @size : a new size in pixels.
  528. *
  529. * This will force an image size, instead of looking at the allocation
  530. * size, see introduction for more details. You can set a @size of
  531. * -1 to turn this off.
  532. *
  533. * Since: 4.8
  534. **/
  535. void
  536. xfce_panel_image_set_size (XfcePanelImage *image,
  537. gint size)
  538. {
  539. g_return_if_fail (XFCE_IS_PANEL_IMAGE (image));
  540. if (G_LIKELY (image->priv->size != size))
  541. {
  542. image->priv->size = size;
  543. gtk_widget_queue_resize (GTK_WIDGET (image));
  544. }
  545. }
  546. /**
  547. * xfce_panel_image_get_size:
  548. * @image : an #XfcePanelImage.
  549. *
  550. * The size of the image, set by xfce_panel_image_set_size() or -1
  551. * if no size is forced and the image is scaled to the allocation size.
  552. *
  553. * Returns: icon size in pixels of the image or -1.
  554. *
  555. * Since: 4.8
  556. **/
  557. gint
  558. xfce_panel_image_get_size (XfcePanelImage *image)
  559. {
  560. g_return_val_if_fail (XFCE_IS_PANEL_IMAGE (image), -1);
  561. return image->priv->size;
  562. }
  563. /**
  564. * xfce_panel_image_clear:
  565. * @image : an #XfcePanelImage.
  566. *
  567. * Resets the image to be empty.
  568. *
  569. * Since: 4.8
  570. **/
  571. void
  572. xfce_panel_image_clear (XfcePanelImage *image)
  573. {
  574. XfcePanelImagePrivate *priv = XFCE_PANEL_IMAGE (image)->priv;
  575. g_return_if_fail (XFCE_IS_PANEL_IMAGE (image));
  576. if (priv->idle_load_id != 0)
  577. g_source_remove (priv->idle_load_id);
  578. if (priv->source != NULL)
  579. {
  580. g_free (priv->source);
  581. priv->source = NULL;
  582. }
  583. xfce_panel_image_unref_null (priv->pixbuf);
  584. xfce_panel_image_unref_null (priv->cache);
  585. /* reset values */
  586. priv->width = -1;
  587. priv->height = -1;
  588. }
  589. #define __XFCE_PANEL_IMAGE_C__
  590. #include <libxfce4panel/libxfce4panel-aliasdef.c>