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.
 
 
 
 
 

553 lines
16 KiB

  1. /*
  2. * Copyright (C) 2006-2007 Jasper Huijsmans <jasper@xfce.org>
  3. * Copyright (C) 2008-2010 Nick Schermer <nick@xfce.org>
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #ifdef HAVE_STDIO_H
  23. #include <stdio.h>
  24. #endif
  25. #ifdef HAVE_STRING_H
  26. #include <string.h>
  27. #endif
  28. #include <gtk/gtk.h>
  29. #include <common/panel-private.h>
  30. #include <libxfce4panel/xfce-panel-macros.h>
  31. #include <libxfce4panel/xfce-arrow-button.h>
  32. #include <libxfce4panel/libxfce4panel-alias.h>
  33. /**
  34. * SECTION: xfce-arrow-button
  35. * @title: XfceArrowButton
  36. * @short_description: Toggle button with arrow
  37. * @include: libxfce4panel/libxfce4panel.h
  38. *
  39. * Toggle button with (optional) arrow. The arrow direction will be
  40. * inverted when the button is toggled.
  41. * Since 4.8 it is also possible to make the button blink and pack additional
  42. * widgets in the button, using gtk_container_add().
  43. **/
  44. #define ARROW_WIDTH (8)
  45. #define MAX_BLINKING_COUNT (G_MAXUINT)
  46. enum
  47. {
  48. ARROW_TYPE_CHANGED,
  49. LAST_SIGNAL
  50. };
  51. enum
  52. {
  53. PROP_0,
  54. PROP_ARROW_TYPE
  55. };
  56. static void xfce_arrow_button_set_property (GObject *object,
  57. guint prop_id,
  58. const GValue *value,
  59. GParamSpec *pspec);
  60. static void xfce_arrow_button_get_property (GObject *object,
  61. guint prop_id,
  62. GValue *value,
  63. GParamSpec *pspec);
  64. static void xfce_arrow_button_finalize (GObject *object);
  65. static gboolean xfce_arrow_button_expose_event (GtkWidget *widget,
  66. GdkEventExpose *event);
  67. static void xfce_arrow_button_size_request (GtkWidget *widget,
  68. GtkRequisition *requisition);
  69. static void xfce_arrow_button_size_allocate (GtkWidget *widget,
  70. GtkAllocation *allocation);
  71. struct _XfceArrowButtonPrivate
  72. {
  73. /* arrow type of the button */
  74. GtkArrowType arrow_type;
  75. /* blinking timeout id */
  76. guint blinking_timeout_id;
  77. /* counter to make the blinking stop when
  78. * MAX_BLINKING_COUNT is reached */
  79. guint blinking_counter;
  80. /* button relief when the blinking starts */
  81. GtkReliefStyle last_relief;
  82. };
  83. static guint arrow_button_signals[LAST_SIGNAL];
  84. G_DEFINE_TYPE (XfceArrowButton, xfce_arrow_button, GTK_TYPE_TOGGLE_BUTTON)
  85. static void
  86. xfce_arrow_button_class_init (XfceArrowButtonClass * klass)
  87. {
  88. GObjectClass *gobject_class;
  89. GtkWidgetClass *gtkwidget_class;
  90. g_type_class_add_private (klass, sizeof (XfceArrowButtonPrivate));
  91. gobject_class = G_OBJECT_CLASS (klass);
  92. gobject_class->get_property = xfce_arrow_button_get_property;
  93. gobject_class->set_property = xfce_arrow_button_set_property;
  94. gobject_class->finalize = xfce_arrow_button_finalize;
  95. gtkwidget_class = GTK_WIDGET_CLASS (klass);
  96. gtkwidget_class->expose_event = xfce_arrow_button_expose_event;
  97. gtkwidget_class->size_request = xfce_arrow_button_size_request;
  98. gtkwidget_class->size_allocate = xfce_arrow_button_size_allocate;
  99. /**
  100. * XfceArrowButton::arrow-type-changed
  101. * @button: the object which emitted the signal
  102. * @type: the new #GtkArrowType of the button
  103. *
  104. * Emitted when the arrow direction of the menu button changes.
  105. * This value also determines the direction of the popup menu.
  106. **/
  107. arrow_button_signals[ARROW_TYPE_CHANGED] =
  108. g_signal_new (g_intern_static_string ("arrow-type-changed"),
  109. G_OBJECT_CLASS_TYPE (klass),
  110. G_SIGNAL_RUN_LAST,
  111. G_STRUCT_OFFSET (XfceArrowButtonClass, arrow_type_changed),
  112. NULL, NULL,
  113. g_cclosure_marshal_VOID__ENUM,
  114. G_TYPE_NONE, 1, GTK_TYPE_ARROW_TYPE);
  115. /**
  116. * XfceArrowButton:arrow-type
  117. *
  118. * The arrow type of the button. This value also determines the direction
  119. * of the popup menu.
  120. **/
  121. g_object_class_install_property (gobject_class,
  122. PROP_ARROW_TYPE,
  123. g_param_spec_enum ("arrow-type",
  124. "Arrow type",
  125. "The arrow type of the menu button",
  126. GTK_TYPE_ARROW_TYPE,
  127. GTK_ARROW_UP,
  128. G_PARAM_READWRITE
  129. | G_PARAM_STATIC_STRINGS));
  130. }
  131. static void
  132. xfce_arrow_button_init (XfceArrowButton *button)
  133. {
  134. button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button, XFCE_TYPE_ARROW_BUTTON, XfceArrowButtonPrivate);
  135. /* initialize button values */
  136. button->priv->arrow_type = GTK_ARROW_UP;
  137. button->priv->blinking_timeout_id = 0;
  138. button->priv->blinking_counter = 0;
  139. button->priv->last_relief = GTK_RELIEF_NORMAL;
  140. /* set some widget properties */
  141. GTK_WIDGET_SET_FLAGS (GTK_WIDGET (button), GTK_NO_WINDOW);
  142. GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_DEFAULT | GTK_CAN_FOCUS);
  143. gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
  144. }
  145. static void
  146. xfce_arrow_button_set_property (GObject *object,
  147. guint prop_id,
  148. const GValue *value,
  149. GParamSpec *pspec)
  150. {
  151. XfceArrowButton *button = XFCE_ARROW_BUTTON (object);
  152. switch (prop_id)
  153. {
  154. case PROP_ARROW_TYPE:
  155. xfce_arrow_button_set_arrow_type (button, g_value_get_enum (value));
  156. break;
  157. default:
  158. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  159. break;
  160. }
  161. }
  162. static void
  163. xfce_arrow_button_get_property (GObject *object,
  164. guint prop_id,
  165. GValue *value,
  166. GParamSpec *pspec)
  167. {
  168. XfceArrowButton *button = XFCE_ARROW_BUTTON (object);
  169. switch (prop_id)
  170. {
  171. case PROP_ARROW_TYPE:
  172. g_value_set_enum (value, xfce_arrow_button_get_arrow_type (button));
  173. break;
  174. default:
  175. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  176. break;
  177. }
  178. }
  179. static void
  180. xfce_arrow_button_finalize (GObject *object)
  181. {
  182. XfceArrowButton *button = XFCE_ARROW_BUTTON (object);
  183. if (button->priv->blinking_timeout_id != 0)
  184. g_source_remove (button->priv->blinking_timeout_id);
  185. (*G_OBJECT_CLASS (xfce_arrow_button_parent_class)->finalize) (object);
  186. }
  187. static gboolean
  188. xfce_arrow_button_expose_event (GtkWidget *widget,
  189. GdkEventExpose *event)
  190. {
  191. XfceArrowButton *button = XFCE_ARROW_BUTTON (widget);
  192. GtkWidget *child;
  193. gint x, y, width;
  194. /* draw the button */
  195. (*GTK_WIDGET_CLASS (xfce_arrow_button_parent_class)->expose_event) (widget, event);
  196. if (button->priv->arrow_type != GTK_ARROW_NONE
  197. && GTK_WIDGET_DRAWABLE (widget))
  198. {
  199. child = gtk_bin_get_child (GTK_BIN (widget));
  200. if (child != NULL && GTK_WIDGET_VISIBLE (child))
  201. {
  202. if (button->priv->arrow_type == GTK_ARROW_UP
  203. || button->priv->arrow_type == GTK_ARROW_DOWN)
  204. {
  205. width = ARROW_WIDTH;
  206. x = widget->allocation.x + widget->style->xthickness;
  207. y = widget->allocation.y + (widget->allocation.height - width) / 2;
  208. }
  209. else
  210. {
  211. width = ARROW_WIDTH;
  212. x = widget->allocation.x + (widget->allocation.width - width) / 2;
  213. y = widget->allocation.y + widget->style->ythickness;
  214. }
  215. }
  216. else
  217. {
  218. width = MIN (widget->allocation.height - 2 * widget->style->ythickness,
  219. widget->allocation.width - 2 * widget->style->xthickness);
  220. width = CLAMP (width, 1, ARROW_WIDTH);
  221. x = widget->allocation.x + (widget->allocation.width - width) / 2;
  222. y = widget->allocation.y + (widget->allocation.height - width) / 2;
  223. }
  224. gtk_paint_arrow (widget->style, widget->window,
  225. GTK_WIDGET_STATE (widget), GTK_SHADOW_NONE,
  226. &(event->area), widget, "xfce_arrow_button",
  227. button->priv->arrow_type, FALSE,
  228. x, y, width, width);
  229. }
  230. return TRUE;
  231. }
  232. static void
  233. xfce_arrow_button_size_request (GtkWidget *widget,
  234. GtkRequisition *requisition)
  235. {
  236. XfceArrowButton *button = XFCE_ARROW_BUTTON (widget);
  237. GtkWidget *child;
  238. child = gtk_bin_get_child (GTK_BIN (widget));
  239. if (child != NULL && GTK_WIDGET_VISIBLE (child))
  240. {
  241. /* use gtk for the widget size */
  242. (*GTK_WIDGET_CLASS (xfce_arrow_button_parent_class)->size_request) (widget, requisition);
  243. /* reserve space for the arrow */
  244. switch (button->priv->arrow_type)
  245. {
  246. case GTK_ARROW_UP:
  247. case GTK_ARROW_DOWN:
  248. requisition->width += ARROW_WIDTH;
  249. break;
  250. case GTK_ARROW_LEFT:
  251. case GTK_ARROW_RIGHT:
  252. requisition->height += ARROW_WIDTH;
  253. break;
  254. default:
  255. break;
  256. }
  257. }
  258. else if (button->priv->arrow_type != GTK_ARROW_NONE)
  259. {
  260. requisition->height = ARROW_WIDTH + 2 * widget->style->xthickness;
  261. requisition->width = ARROW_WIDTH + 2 * widget->style->ythickness;
  262. }
  263. }
  264. static void
  265. xfce_arrow_button_size_allocate (GtkWidget *widget,
  266. GtkAllocation *allocation)
  267. {
  268. XfceArrowButton *button = XFCE_ARROW_BUTTON (widget);
  269. GtkWidget *child;
  270. GtkAllocation child_allocation;
  271. /* allocate the button */
  272. (*GTK_WIDGET_CLASS (xfce_arrow_button_parent_class)->size_allocate) (widget, allocation);
  273. if (button->priv->arrow_type != GTK_ARROW_NONE)
  274. {
  275. child = gtk_bin_get_child (GTK_BIN (widget));
  276. if (child != NULL && GTK_WIDGET_VISIBLE (child))
  277. {
  278. /* copy the child allocation */
  279. child_allocation = child->allocation;
  280. /* update the allocation to make space for the arrow */
  281. switch (button->priv->arrow_type)
  282. {
  283. case GTK_ARROW_LEFT:
  284. case GTK_ARROW_RIGHT:
  285. child_allocation.height -= ARROW_WIDTH;
  286. child_allocation.y += ARROW_WIDTH;
  287. break;
  288. default:
  289. child_allocation.width -= ARROW_WIDTH;
  290. child_allocation.x += ARROW_WIDTH;
  291. break;
  292. }
  293. /* set the child allocation again */
  294. gtk_widget_size_allocate (child, &child_allocation);
  295. }
  296. }
  297. }
  298. static gboolean
  299. xfce_arrow_button_blinking_timeout (gpointer user_data)
  300. {
  301. XfceArrowButton *button = XFCE_ARROW_BUTTON (user_data);
  302. GtkStyle *style;
  303. GtkRcStyle *rc;
  304. GDK_THREADS_ENTER ();
  305. rc = gtk_widget_get_modifier_style (GTK_WIDGET (button));
  306. if(PANEL_HAS_FLAG (rc->color_flags[GTK_STATE_NORMAL], GTK_RC_BG)
  307. || button->priv->blinking_timeout_id == 0)
  308. {
  309. gtk_button_set_relief (GTK_BUTTON (button), button->priv->last_relief);
  310. PANEL_UNSET_FLAG (rc->color_flags[GTK_STATE_NORMAL], GTK_RC_BG);
  311. gtk_widget_modify_style (GTK_WIDGET (button), rc);
  312. }
  313. else
  314. {
  315. gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL);
  316. PANEL_SET_FLAG (rc->color_flags[GTK_STATE_NORMAL], GTK_RC_BG);
  317. style = gtk_widget_get_style (GTK_WIDGET (button));
  318. rc->bg[GTK_STATE_NORMAL] = style->bg[GTK_STATE_SELECTED];
  319. gtk_widget_modify_style(GTK_WIDGET (button), rc);
  320. }
  321. GDK_THREADS_LEAVE ();
  322. return (button->priv->blinking_counter++ < MAX_BLINKING_COUNT);
  323. }
  324. static void
  325. xfce_arrow_button_blinking_timeout_destroyed (gpointer user_data)
  326. {
  327. XfceArrowButton *button = XFCE_ARROW_BUTTON (user_data);
  328. button->priv->blinking_timeout_id = 0;
  329. button->priv->blinking_counter = 0;
  330. }
  331. /**
  332. * xfce_arrow_button_new:
  333. * @arrow_type : #GtkArrowType for the arrow button
  334. *
  335. * Creates a new #XfceArrowButton widget.
  336. *
  337. * Returns: The newly created #XfceArrowButton widget.
  338. **/
  339. GtkWidget *
  340. xfce_arrow_button_new (GtkArrowType arrow_type)
  341. {
  342. return g_object_new (XFCE_TYPE_ARROW_BUTTON,
  343. "arrow-type", arrow_type,
  344. NULL);
  345. }
  346. /**
  347. * xfce_arrow_button_get_arrow_type:
  348. * @button : a #XfceArrowButton
  349. *
  350. * Returns the value of the ::arrow-type property.
  351. *
  352. * Returns: the #GtkArrowType of @button.
  353. **/
  354. GtkArrowType
  355. xfce_arrow_button_get_arrow_type (XfceArrowButton *button)
  356. {
  357. g_return_val_if_fail (XFCE_IS_ARROW_BUTTON (button), GTK_ARROW_UP);
  358. return button->priv->arrow_type;
  359. }
  360. /**
  361. * xfce_arrow_button_set_arrow_type:
  362. * @button : a #XfceArrowButton
  363. * @arrow_type : a valid #GtkArrowType
  364. *
  365. * Sets the arrow type for @button.
  366. **/
  367. void
  368. xfce_arrow_button_set_arrow_type (XfceArrowButton *button,
  369. GtkArrowType arrow_type)
  370. {
  371. g_return_if_fail (XFCE_IS_ARROW_BUTTON (button));
  372. if (G_LIKELY (button->priv->arrow_type != arrow_type))
  373. {
  374. /* store the new arrow type */
  375. button->priv->arrow_type = arrow_type;
  376. /* emit signal */
  377. g_signal_emit (G_OBJECT (button),
  378. arrow_button_signals[ARROW_TYPE_CHANGED],
  379. 0, arrow_type);
  380. /* notify property change */
  381. g_object_notify (G_OBJECT (button), "arrow-type");
  382. /* redraw the arrow button */
  383. gtk_widget_queue_resize (GTK_WIDGET (button));
  384. }
  385. }
  386. /**
  387. * xfce_arrow_button_get_blinking:
  388. * @button : a #XfceArrowButton
  389. *
  390. * Whether the button is blinking. If the blink timeout is finished
  391. * and the button is still highlighted, this functions returns %FALSE.
  392. *
  393. * Returns: %TRUE when @button is blinking.
  394. *
  395. * Since: 4.8
  396. **/
  397. gboolean
  398. xfce_arrow_button_get_blinking (XfceArrowButton *button)
  399. {
  400. g_return_val_if_fail (XFCE_IS_ARROW_BUTTON (button), FALSE);
  401. return !!(button->priv->blinking_timeout_id != 0);
  402. }
  403. /**
  404. * xfce_arrow_button_set_blinking:
  405. * @button : a #XfceArrowButton
  406. * @blinking : %TRUE when the button should start blinking, %FALSE to
  407. * stop the blinking.
  408. *
  409. * Make the button blink.
  410. *
  411. * Since: 4.8
  412. **/
  413. void
  414. xfce_arrow_button_set_blinking (XfceArrowButton *button,
  415. gboolean blinking)
  416. {
  417. g_return_if_fail (XFCE_IS_ARROW_BUTTON (button));
  418. if (blinking)
  419. {
  420. /* store the relief of the button */
  421. button->priv->last_relief = gtk_button_get_relief (GTK_BUTTON (button));
  422. if (button->priv->blinking_timeout_id == 0)
  423. {
  424. /* start blinking timeout */
  425. button->priv->blinking_timeout_id =
  426. g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 500,
  427. xfce_arrow_button_blinking_timeout, button,
  428. xfce_arrow_button_blinking_timeout_destroyed);
  429. }
  430. }
  431. else if (button->priv->blinking_timeout_id != 0)
  432. {
  433. /* stop the blinking timeout */
  434. g_source_remove (button->priv->blinking_timeout_id);
  435. }
  436. /* start with a blinking or make sure the button is normal */
  437. xfce_arrow_button_blinking_timeout (button);
  438. }
  439. #define __XFCE_ARROW_BUTTON_C__
  440. #include <libxfce4panel/libxfce4panel-aliasdef.c>