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.
 
 
 
 
 

1035 lines
30 KiB

  1. /* $Id$ */
  2. /*
  3. * Copyright (C) 2008 Nick Schermer <nick@xfce.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program 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
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #include <gtk/gtk.h>
  23. #include <exo/exo.h>
  24. #include <libxfce4panel/libxfce4panel.h>
  25. #include <panel/panel-private.h>
  26. #include <panel/panel-itembar.h>
  27. #define OFFSCREEN (-9999)
  28. #define HIGHLIGHT_THINKNESS (2)
  29. static void panel_itembar_class_init (PanelItembarClass *klass);
  30. static void panel_itembar_init (PanelItembar *itembar);
  31. static void panel_itembar_set_property (GObject *object,
  32. guint prop_id,
  33. const GValue *value,
  34. GParamSpec *pspec);
  35. static void panel_itembar_finalize (GObject *object);
  36. static void panel_itembar_realize (GtkWidget *widget);
  37. static void panel_itembar_unrealize (GtkWidget *widget);
  38. static void panel_itembar_map (GtkWidget *widget);
  39. static void panel_itembar_unmap (GtkWidget *widget);
  40. static gboolean panel_itembar_expose_event (GtkWidget *widget,
  41. GdkEventExpose *event);
  42. static void panel_itembar_size_request (GtkWidget *widget,
  43. GtkRequisition *requisition);
  44. static void panel_itembar_size_allocate (GtkWidget *widget,
  45. GtkAllocation *allocation);
  46. static gboolean panel_itembar_drag_motion (GtkWidget *widget,
  47. GdkDragContext *drag_context,
  48. gint drag_x,
  49. gint drag_y,
  50. guint time);
  51. static void panel_itembar_drag_leave (GtkWidget *widget,
  52. GdkDragContext *drag_context,
  53. guint time);
  54. static void panel_itembar_add (GtkContainer *container,
  55. GtkWidget *child);
  56. static void panel_itembar_remove (GtkContainer *container,
  57. GtkWidget *child);
  58. static void panel_itembar_forall (GtkContainer *container,
  59. gboolean include_internals,
  60. GtkCallback callback,
  61. gpointer callback_data);
  62. static GType panel_itembar_child_type (GtkContainer *container);
  63. static void panel_itembar_set_orientation (PanelItembar *itembar,
  64. GtkOrientation orientation);
  65. struct _PanelItembarClass
  66. {
  67. GtkContainerClass __parent__;
  68. };
  69. struct _PanelItembar
  70. {
  71. GtkContainer __parent__;
  72. /* window to send all events to the itembar */
  73. GdkWindow *event_window;
  74. /* dnd highlight line */
  75. GdkWindow *highlight_window;
  76. /* orientation of the itembar */
  77. GtkOrientation orientation;
  78. /* internal list of children */
  79. GSList *children;
  80. /* current sensitivity state */
  81. guint sensitive : 1;
  82. };
  83. struct _PanelItembarChild
  84. {
  85. GtkWidget *widget;
  86. guint expand : 1;
  87. };
  88. enum
  89. {
  90. PROP_0,
  91. PROP_ORIENTATION
  92. };
  93. G_DEFINE_TYPE (PanelItembar, panel_itembar, GTK_TYPE_CONTAINER);
  94. /* drop targets */
  95. static const GtkTargetEntry drop_targets[] =
  96. {
  97. { "application/x-xfce-panel-plugin-name", 0, PANEL_ITEMBAR_TARGET_PLUGIN_NAME },
  98. { "application/x-xfce-panel-plugin-widget", 0, PANEL_ITEMBAR_TARGET_PLUGIN_WIDGET },
  99. };
  100. static void
  101. panel_itembar_class_init (PanelItembarClass *klass)
  102. {
  103. GObjectClass *gobject_class;
  104. GtkWidgetClass *gtkwidget_class;
  105. GtkContainerClass *gtkcontainer_class;
  106. gobject_class = G_OBJECT_CLASS (klass);
  107. gobject_class->set_property = panel_itembar_set_property;
  108. gobject_class->finalize = panel_itembar_finalize;
  109. gtkwidget_class = GTK_WIDGET_CLASS (klass);
  110. gtkwidget_class->realize = panel_itembar_realize;
  111. gtkwidget_class->unrealize = panel_itembar_unrealize;
  112. gtkwidget_class->map = panel_itembar_map;
  113. gtkwidget_class->unmap = panel_itembar_unmap;
  114. gtkwidget_class->expose_event = panel_itembar_expose_event;
  115. gtkwidget_class->size_request = panel_itembar_size_request;
  116. gtkwidget_class->size_allocate = panel_itembar_size_allocate;
  117. gtkwidget_class->drag_motion = panel_itembar_drag_motion;
  118. gtkwidget_class->drag_leave = panel_itembar_drag_leave;
  119. gtkcontainer_class = GTK_CONTAINER_CLASS (klass);
  120. gtkcontainer_class->add = panel_itembar_add;
  121. gtkcontainer_class->remove = panel_itembar_remove;
  122. gtkcontainer_class->forall = panel_itembar_forall;
  123. gtkcontainer_class->child_type = panel_itembar_child_type;
  124. gtkcontainer_class->get_child_property = NULL;
  125. gtkcontainer_class->set_child_property = NULL;
  126. /**
  127. * PanelItembar::orientation:
  128. *
  129. * The orientation of the itembar. This property is synced with the
  130. * orientation property of the PanelWindow. That's also the reason
  131. * the itembar has no public panel_itembar_[sg]et_orientation
  132. * functions: it should be read from the panel window.
  133. **/
  134. g_object_class_install_property (gobject_class,
  135. PROP_ORIENTATION,
  136. g_param_spec_enum ("orientation", "orientation", "orientation",
  137. GTK_TYPE_ORIENTATION,
  138. GTK_ORIENTATION_HORIZONTAL,
  139. EXO_PARAM_WRITABLE));
  140. }
  141. static void
  142. panel_itembar_init (PanelItembar *itembar)
  143. {
  144. /* initialize */
  145. itembar->children = NULL;
  146. itembar->event_window = NULL;
  147. itembar->highlight_window = NULL;
  148. itembar->sensitive = TRUE;
  149. itembar->orientation = GTK_ORIENTATION_HORIZONTAL;
  150. /* setup */
  151. GTK_WIDGET_SET_FLAGS (GTK_WIDGET (itembar), GTK_NO_WINDOW);
  152. /* don't redraw on allocation */
  153. gtk_widget_set_redraw_on_allocate (GTK_WIDGET (itembar), FALSE);
  154. /* set the itembar drag destination targets */
  155. gtk_drag_dest_set (GTK_WIDGET (itembar), GTK_DEST_DEFAULT_MOTION,
  156. drop_targets, G_N_ELEMENTS (drop_targets),
  157. GDK_ACTION_COPY | GDK_ACTION_MOVE);
  158. }
  159. static void
  160. panel_itembar_set_property (GObject *object,
  161. guint prop_id,
  162. const GValue *value,
  163. GParamSpec *pspec)
  164. {
  165. PanelItembar *itembar = PANEL_ITEMBAR (object);
  166. switch (prop_id)
  167. {
  168. case PROP_ORIENTATION:
  169. panel_itembar_set_orientation (itembar, g_value_get_enum (value));
  170. break;
  171. default:
  172. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  173. break;
  174. }
  175. }
  176. static void
  177. panel_itembar_finalize (GObject *object)
  178. {
  179. panel_return_if_fail (PANEL_ITEMBAR (object)->children == NULL);
  180. (*G_OBJECT_CLASS (panel_itembar_parent_class)->finalize) (object);
  181. }
  182. static void
  183. panel_itembar_realize (GtkWidget *widget)
  184. {
  185. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  186. GdkWindowAttr attributes;
  187. /* let gtk handle it's own realation first */
  188. (*GTK_WIDGET_CLASS (panel_itembar_parent_class)->realize) (widget);
  189. /* setup the window attributes */
  190. attributes.x = widget->allocation.x;
  191. attributes.y = widget->allocation.y;
  192. attributes.width = widget->allocation.width;
  193. attributes.height = widget->allocation.height;
  194. attributes.wclass = GDK_INPUT_ONLY;
  195. attributes.window_type = GDK_WINDOW_CHILD;
  196. attributes.event_mask = gtk_widget_get_events (widget);
  197. /* allocate the event window */
  198. itembar->event_window = gdk_window_new (GDK_WINDOW (widget->window), &attributes, GDK_WA_X | GDK_WA_Y);
  199. /* set the window user data */
  200. gdk_window_set_user_data (GDK_WINDOW (itembar->event_window), widget);
  201. }
  202. static void
  203. panel_itembar_unrealize (GtkWidget *widget)
  204. {
  205. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  206. /* destroy the event window */
  207. if (G_LIKELY (itembar->event_window))
  208. {
  209. gdk_window_set_user_data (itembar->event_window, NULL);
  210. gdk_window_destroy (itembar->event_window);
  211. itembar->event_window = NULL;
  212. }
  213. (*GTK_WIDGET_CLASS (panel_itembar_parent_class)->unrealize) (widget);
  214. }
  215. static void
  216. panel_itembar_map (GtkWidget *widget)
  217. {
  218. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  219. /* show the event window */
  220. if (G_LIKELY (itembar->event_window))
  221. gdk_window_show (itembar->event_window);
  222. (*GTK_WIDGET_CLASS (panel_itembar_parent_class)->map) (widget);
  223. /* raise the window if we're in insensitive mode */
  224. if (G_UNLIKELY (!itembar->sensitive && itembar->event_window))
  225. gdk_window_raise (itembar->event_window);
  226. }
  227. static void
  228. panel_itembar_unmap (GtkWidget *widget)
  229. {
  230. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  231. /* hide the event window */
  232. if (G_LIKELY (itembar->event_window))
  233. gdk_window_hide (itembar->event_window);
  234. (*GTK_WIDGET_CLASS (panel_itembar_parent_class)->unmap) (widget);
  235. }
  236. static gboolean
  237. panel_itembar_expose_event (GtkWidget *widget,
  238. GdkEventExpose *event)
  239. {
  240. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  241. (*GTK_WIDGET_CLASS (panel_itembar_parent_class)->expose_event) (widget, event);
  242. /* keep our event window on top */
  243. if (itembar->sensitive == FALSE && itembar->event_window)
  244. gdk_window_raise (itembar->event_window);
  245. return TRUE;
  246. }
  247. static void
  248. panel_itembar_size_request (GtkWidget *widget,
  249. GtkRequisition *requisition)
  250. {
  251. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  252. GSList *li;
  253. PanelItembarChild *child;
  254. GtkRequisition child_requisition;
  255. /* initialize */
  256. requisition->width = GTK_CONTAINER (widget)->border_width * 2;
  257. requisition->height = requisition->width;
  258. /* walk the childeren */
  259. for (li = itembar->children; li != NULL; li = li->next)
  260. {
  261. child = li->data;
  262. if (G_LIKELY (GTK_WIDGET_VISIBLE (child->widget)))
  263. {
  264. /* get the child size request */
  265. gtk_widget_size_request (child->widget, &child_requisition);
  266. /* update the itembar requisition */
  267. if (itembar->orientation == GTK_ORIENTATION_HORIZONTAL)
  268. {
  269. requisition->width += child_requisition.width;
  270. requisition->height = MAX (requisition->height, child_requisition.height);
  271. }
  272. else
  273. {
  274. requisition->height += child_requisition.height;
  275. requisition->width = MAX (requisition->width, child_requisition.width);
  276. }
  277. }
  278. }
  279. }
  280. static void
  281. panel_itembar_size_allocate (GtkWidget *widget,
  282. GtkAllocation *allocation)
  283. {
  284. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  285. GSList *li;
  286. PanelItembarChild *child;
  287. GtkRequisition child_requisition;
  288. GtkAllocation child_allocation;
  289. guint n_expand_children = 0;
  290. gint alloc_expandable_size;
  291. gint req_expandable_size = 0;
  292. gint x, y;
  293. gint border_width;
  294. gboolean horizontal;
  295. gint alloc_size, req_size;
  296. gboolean expandable_children_fit;
  297. /* set widget allocation */
  298. widget->allocation = *allocation;
  299. /* allocate the event window */
  300. if (G_LIKELY (itembar->event_window))
  301. gdk_window_move_resize (GDK_WINDOW (itembar->event_window), allocation->x,
  302. allocation->y, allocation->width, allocation->height);
  303. /* get the border width */
  304. border_width = GTK_CONTAINER (widget)->border_width;
  305. /* boolean for orientation */
  306. horizontal = (itembar->orientation == GTK_ORIENTATION_HORIZONTAL);
  307. /* get the itembar size */
  308. if (horizontal)
  309. alloc_expandable_size = allocation->width - 2 * border_width;
  310. else
  311. alloc_expandable_size = allocation->height - 2 * border_width;
  312. /* walk the children to get the (remaining) expandable length */
  313. for (li = itembar->children; li != NULL; li = li->next)
  314. {
  315. child = li->data;
  316. /* skip hidden widgets */
  317. if (GTK_WIDGET_VISIBLE (child->widget) == FALSE)
  318. continue;
  319. /* get the child size request */
  320. gtk_widget_get_child_requisition (child->widget, &child_requisition);
  321. if (G_UNLIKELY (child->expand))
  322. {
  323. /* increase counter */
  324. n_expand_children++;
  325. /* update the size requested by the expanding children */
  326. if (horizontal)
  327. req_expandable_size += child_requisition.width;
  328. else
  329. req_expandable_size += child_requisition.height;
  330. }
  331. else
  332. {
  333. /* update the size avaible for allocation */
  334. if (horizontal)
  335. alloc_expandable_size -= child_requisition.width;
  336. else
  337. alloc_expandable_size -= child_requisition.height;
  338. }
  339. }
  340. /* set coordinates */
  341. x = allocation->x + border_width;
  342. y = allocation->y + border_width;
  343. /* check if the expandable childs fit in the available expandable size */
  344. expandable_children_fit = (req_expandable_size == alloc_expandable_size || req_expandable_size <= 0);
  345. /* make sure the allocated expandable size is not below zero */
  346. alloc_expandable_size = MAX (0, alloc_expandable_size);
  347. /* allocate the children */
  348. for (li = itembar->children; li != NULL; li = li->next)
  349. {
  350. child = li->data;
  351. /* still skip hidden widgets */
  352. if (GTK_WIDGET_VISIBLE (child->widget) == FALSE)
  353. continue;
  354. /* get the child size request */
  355. gtk_widget_get_child_requisition (child->widget, &child_requisition);
  356. /* set coordinates for the child allocation */
  357. child_allocation.x = x;
  358. child_allocation.y = y;
  359. /* set the width or height of the child */
  360. if (G_UNLIKELY (child->expand && expandable_children_fit == FALSE))
  361. {
  362. /* get requested size */
  363. req_size = horizontal ? child_requisition.width : child_requisition.height;
  364. /* calculate allocated size */
  365. alloc_size = alloc_expandable_size * req_size / req_expandable_size;
  366. /* set the calculated expanding size */
  367. if (horizontal)
  368. child_allocation.width = alloc_size;
  369. else
  370. child_allocation.height = alloc_size;
  371. /* update total sizes */
  372. alloc_expandable_size -= alloc_size;
  373. req_expandable_size -= req_size;
  374. }
  375. else
  376. {
  377. /* set the requested size in the allocation */
  378. if (horizontal)
  379. child_allocation.width = child_requisition.width;
  380. else
  381. child_allocation.height = child_requisition.height;
  382. }
  383. /* update the coordinates and set the allocated (user defined) panel size */
  384. if (horizontal)
  385. {
  386. x += child_allocation.width;
  387. child_allocation.height = allocation->height;
  388. /* check if everything fits in the allocated size */
  389. if (G_UNLIKELY (x > allocation->width + allocation->x))
  390. {
  391. /* draw the next plugin offscreen */
  392. x = OFFSCREEN;
  393. /* make this plugin fit exactly on the panel */
  394. child_allocation.width = MAX (0, allocation->width + allocation->x - child_allocation.x);
  395. }
  396. }
  397. else
  398. {
  399. y += child_allocation.height;
  400. child_allocation.width = allocation->width;
  401. /* check if everything fits in the allocated size */
  402. if (G_UNLIKELY (y > allocation->height + allocation->y))
  403. {
  404. /* draw the next plugin offscreen */
  405. y = OFFSCREEN;
  406. /* make this plugin fit exactly on the panel */
  407. child_allocation.height = MAX (0, allocation->height + allocation->y - child_allocation.y);
  408. }
  409. }
  410. /* allocate the child */
  411. gtk_widget_size_allocate (child->widget, &child_allocation);
  412. }
  413. }
  414. static gboolean
  415. panel_itembar_drag_motion (GtkWidget *widget,
  416. GdkDragContext *drag_context,
  417. gint drag_x,
  418. gint drag_y,
  419. guint time)
  420. {
  421. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  422. GdkWindowAttr attributes;
  423. gint drop_index;
  424. GtkWidget *child = NULL;
  425. gint x = 0, y = 0;
  426. gint width, height;
  427. gboolean is_horizontal;
  428. if (G_UNLIKELY (itembar->highlight_window == NULL))
  429. {
  430. /* setup window attributes */
  431. attributes.window_type = GDK_WINDOW_CHILD;
  432. attributes.wclass = GDK_INPUT_OUTPUT;
  433. attributes.visual = gtk_widget_get_visual (widget);
  434. attributes.colormap = gtk_widget_get_colormap (widget);
  435. attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
  436. attributes.width = HIGHLIGHT_THINKNESS;
  437. attributes.height = HIGHLIGHT_THINKNESS;
  438. /* allocate window */
  439. itembar->highlight_window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes,
  440. GDK_WA_VISUAL | GDK_WA_COLORMAP);
  441. /* set user data */
  442. gdk_window_set_user_data (itembar->highlight_window, widget);
  443. /* set window background */
  444. gdk_window_set_background (itembar->highlight_window, &widget->style->fg[widget->state]);
  445. }
  446. /* get orientaion */
  447. is_horizontal = !!(itembar->orientation == GTK_ORIENTATION_HORIZONTAL);
  448. /* get the drop index */
  449. drop_index = panel_itembar_get_drop_index (itembar, drag_x, drag_y);
  450. /* get the nth child */
  451. child = panel_itembar_get_nth_child (itembar, drop_index);
  452. if (G_LIKELY (child))
  453. {
  454. /* get child start coordinate */
  455. if (is_horizontal)
  456. x = child->allocation.x;
  457. else
  458. y = child->allocation.y;
  459. }
  460. else if (itembar->children)
  461. {
  462. /* get the last child */
  463. child = panel_itembar_get_nth_child (itembar, g_slist_length (itembar->children) - 1);
  464. /* get coordinate at end of the child */
  465. if (is_horizontal)
  466. x = child->allocation.x + child->allocation.width;
  467. else
  468. y = child->allocation.y + child->allocation.height;
  469. }
  470. /* get size of the hightlight */
  471. width = is_horizontal ? HIGHLIGHT_THINKNESS : widget->allocation.width;
  472. height = is_horizontal ? widget->allocation.height : HIGHLIGHT_THINKNESS;
  473. /* show line between the two children */
  474. x += HIGHLIGHT_THINKNESS / 2;
  475. y += HIGHLIGHT_THINKNESS / 2;
  476. /* keep the heightlight window inside the itembar */
  477. x = CLAMP (x, widget->allocation.x, widget->allocation.x + widget->allocation.width - HIGHLIGHT_THINKNESS);
  478. y = CLAMP (y, widget->allocation.y, widget->allocation.y + widget->allocation.height - HIGHLIGHT_THINKNESS);
  479. /* move the hightlight window */
  480. gdk_window_move_resize (itembar->highlight_window, x, y, width, height);
  481. /* show the window */
  482. gdk_window_show (itembar->highlight_window);
  483. return TRUE;
  484. }
  485. static void
  486. panel_itembar_drag_leave (GtkWidget *widget,
  487. GdkDragContext *drag_context,
  488. guint time)
  489. {
  490. PanelItembar *itembar = PANEL_ITEMBAR (widget);
  491. /* destroy the drag highlight window */
  492. if (G_LIKELY (itembar->highlight_window))
  493. {
  494. gdk_window_set_user_data (itembar->highlight_window, NULL);
  495. gdk_window_destroy (itembar->highlight_window);
  496. itembar->highlight_window = NULL;
  497. }
  498. }
  499. static void
  500. panel_itembar_add (GtkContainer *container,
  501. GtkWidget *child)
  502. {
  503. /* append the item */
  504. panel_itembar_append (PANEL_ITEMBAR (container), child);
  505. }
  506. static void
  507. panel_itembar_remove (GtkContainer *container,
  508. GtkWidget *widget)
  509. {
  510. PanelItembar *itembar = PANEL_ITEMBAR (container);
  511. GSList *li;
  512. PanelItembarChild *child;
  513. gboolean was_visible;
  514. panel_return_if_fail (PANEL_IS_ITEMBAR (itembar));
  515. panel_return_if_fail (GTK_IS_CONTAINER (container));
  516. panel_return_if_fail (GTK_IS_WIDGET (widget));
  517. panel_return_if_fail (widget->parent == GTK_WIDGET (itembar));
  518. panel_return_if_fail (itembar->children != NULL);
  519. for (li = itembar->children; li != NULL; li = li->next)
  520. {
  521. child = li->data;
  522. if (child->widget == widget)
  523. {
  524. /* remove the child from the list */
  525. itembar->children = g_slist_delete_link (itembar->children, li);
  526. /* whether the widget is currently visible */
  527. was_visible = GTK_WIDGET_VISIBLE (widget);
  528. /* remove from the itembar */
  529. gtk_widget_unparent (child->widget);
  530. /* cleanup the slice */
  531. g_slice_free (PanelItembarChild, child);
  532. /* queue a resize if needed */
  533. if (G_LIKELY (was_visible))
  534. gtk_widget_queue_resize (GTK_WIDGET (container));
  535. /* done */
  536. break;
  537. }
  538. }
  539. }
  540. static void
  541. panel_itembar_forall (GtkContainer *container,
  542. gboolean include_internals,
  543. GtkCallback callback,
  544. gpointer callback_data)
  545. {
  546. PanelItembar *itembar = PANEL_ITEMBAR (container);
  547. GSList *children = itembar->children;
  548. PanelItembarChild *child;
  549. panel_return_if_fail (PANEL_IS_ITEMBAR (container));
  550. while (children != NULL)
  551. {
  552. child = children->data;
  553. children = children->next;
  554. (* callback) (child->widget, callback_data);
  555. }
  556. }
  557. static GType
  558. panel_itembar_child_type (GtkContainer *container)
  559. {
  560. return GTK_TYPE_WIDGET;
  561. }
  562. static void
  563. panel_itembar_set_orientation (PanelItembar *itembar,
  564. GtkOrientation orientation)
  565. {
  566. panel_return_if_fail (PANEL_IS_ITEMBAR (itembar));
  567. if (itembar->orientation != orientation)
  568. {
  569. /* set new orientation */
  570. itembar->orientation = orientation;
  571. /* queue a resize */
  572. gtk_widget_queue_resize (GTK_WIDGET (itembar));
  573. }
  574. }
  575. GtkWidget *
  576. panel_itembar_new (void)
  577. {
  578. return g_object_new (PANEL_TYPE_ITEMBAR, NULL);
  579. }
  580. void
  581. panel_itembar_set_sensitive (PanelItembar *itembar,
  582. gboolean sensitive)
  583. {
  584. PanelItembarChild *child;
  585. GSList *li;
  586. panel_return_if_fail (PANEL_IS_ITEMBAR (itembar));
  587. panel_return_if_fail (itembar->event_window == NULL || GDK_IS_WINDOW (itembar->event_window));
  588. /* set internal value */
  589. itembar->sensitive = !!sensitive;
  590. /* raise or lower the event window */
  591. if (G_LIKELY (itembar->event_window))
  592. {
  593. if (sensitive)
  594. gdk_window_lower (itembar->event_window);
  595. else
  596. gdk_window_raise (itembar->event_window);
  597. }
  598. /* walk the children */
  599. for (li = itembar->children; li != NULL; li = li->next)
  600. {
  601. /* get child */
  602. child = li->data;
  603. /* set widget sensitivity */
  604. gtk_widget_set_sensitive (child->widget, sensitive);
  605. }
  606. }
  607. void
  608. panel_itembar_insert (PanelItembar *itembar,
  609. GtkWidget *widget,
  610. gint position)
  611. {
  612. PanelItembarChild *child;
  613. panel_return_if_fail (PANEL_IS_ITEMBAR (itembar));
  614. panel_return_if_fail (GTK_IS_WIDGET (widget));
  615. panel_return_if_fail (widget->parent == NULL);
  616. /* allocate new child */
  617. child = g_slice_new0 (PanelItembarChild);
  618. /* set properties */
  619. child->widget = widget;
  620. child->expand = FALSE;
  621. /* insert in the internal list */
  622. itembar->children = g_slist_insert (itembar->children, child, position);
  623. /* set the parent */
  624. gtk_widget_set_parent (widget, GTK_WIDGET (itembar));
  625. /* sensitivity of the new item */
  626. gtk_widget_set_sensitive (widget, itembar->sensitive);
  627. /* resize the itembar */
  628. gtk_widget_queue_resize (GTK_WIDGET (itembar));
  629. }
  630. void
  631. panel_itembar_reorder_child (PanelItembar *itembar,
  632. GtkWidget *widget,
  633. gint position)
  634. {
  635. GSList *li;
  636. PanelItembarChild *child;
  637. panel_return_if_fail (PANEL_IS_ITEMBAR (itembar));
  638. panel_return_if_fail (GTK_IS_WIDGET (widget));
  639. panel_return_if_fail (widget->parent == GTK_WIDGET (itembar));
  640. for (li = itembar->children; li != NULL; li = li->next)
  641. {
  642. child = li->data;
  643. /* find the widget */
  644. if (child->widget == widget)
  645. {
  646. /* remove the link from the list */
  647. itembar->children = g_slist_delete_link (itembar->children, li);
  648. /* insert the child in the new position */
  649. itembar->children = g_slist_insert (itembar->children, child, position);
  650. /* reallocate the itembar */
  651. gtk_widget_queue_resize (GTK_WIDGET (itembar));
  652. /* we're done */
  653. break;
  654. }
  655. }
  656. }
  657. gboolean
  658. panel_itembar_get_child_expand (PanelItembar *itembar,
  659. GtkWidget *widget)
  660. {
  661. GSList *li;
  662. PanelItembarChild *child;
  663. panel_return_val_if_fail (PANEL_IS_ITEMBAR (itembar), FALSE);
  664. panel_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
  665. panel_return_val_if_fail (widget->parent == GTK_WIDGET (itembar), FALSE);
  666. for (li = itembar->children; li != NULL; li = li->next)
  667. {
  668. child = li->data;
  669. /* find the widget and return the expand mode */
  670. if (child->widget == widget)
  671. return child->expand;
  672. }
  673. return FALSE;
  674. }
  675. void
  676. panel_itembar_set_child_expand (PanelItembar *itembar,
  677. GtkWidget *widget,
  678. gboolean expand)
  679. {
  680. GSList *li;
  681. PanelItembarChild *child;
  682. panel_return_if_fail (PANEL_IS_ITEMBAR (itembar));
  683. panel_return_if_fail (GTK_IS_WIDGET (widget));
  684. panel_return_if_fail (widget->parent == GTK_WIDGET (itembar));
  685. /* find child and set new expand mode */
  686. for (li = itembar->children; li != NULL; li = li->next)
  687. {
  688. child = li->data;
  689. /* find the widget */
  690. if (child->widget == widget)
  691. {
  692. /* only update if the expand mode changed */
  693. if (G_LIKELY (child->expand != expand))
  694. {
  695. /* store new mode */
  696. child->expand = expand;
  697. /* resize the itembar */
  698. gtk_widget_queue_resize (GTK_WIDGET (itembar));
  699. }
  700. /* stop searching */
  701. break;
  702. }
  703. }
  704. }
  705. guint
  706. panel_itembar_get_n_children (PanelItembar *itembar)
  707. {
  708. panel_return_val_if_fail (PANEL_IS_ITEMBAR (itembar), 0);
  709. return g_slist_length (itembar->children);
  710. }
  711. gint
  712. panel_itembar_get_child_index (PanelItembar *itembar,
  713. GtkWidget *widget)
  714. {
  715. GSList *li;
  716. PanelItembarChild *child;
  717. gint idx;
  718. panel_return_val_if_fail (PANEL_IS_ITEMBAR (itembar), -1);
  719. panel_return_val_if_fail (GTK_IS_WIDGET (widget), -1);
  720. panel_return_val_if_fail (widget->parent == GTK_WIDGET (itembar), -1);
  721. /* walk the children to find the child widget */
  722. for (idx = 0, li = itembar->children; li != NULL; li = li->next, idx++)
  723. {
  724. child = li->data;
  725. /* check if this is the widget */
  726. if (child->widget == widget)
  727. return idx;
  728. }
  729. /* nothing found */
  730. return -1;
  731. }
  732. GtkWidget *
  733. panel_itembar_get_nth_child (PanelItembar *itembar,
  734. guint idx)
  735. {
  736. PanelItembarChild *child;
  737. panel_return_val_if_fail (PANEL_IS_ITEMBAR (itembar), NULL);
  738. /* get the nth item */
  739. child = g_slist_nth_data (itembar->children, idx);
  740. /* return the widget */
  741. return (child != NULL ? child->widget : NULL);
  742. }
  743. guint
  744. panel_itembar_get_drop_index (PanelItembar *itembar,
  745. gint x,
  746. gint y)
  747. {
  748. PanelItembarChild *child;
  749. GSList *li;
  750. GtkAllocation *allocation;
  751. gboolean is_horizontal;
  752. guint idx;
  753. panel_return_val_if_fail (PANEL_IS_ITEMBAR (itembar), 0);
  754. /* add the itembar position */
  755. x += GTK_WIDGET (itembar)->allocation.x;
  756. y += GTK_WIDGET (itembar)->allocation.y;
  757. /* whether the itembar is horizontal */
  758. is_horizontal = !!(itembar->orientation == GTK_ORIENTATION_HORIZONTAL);
  759. for (li = itembar->children, idx = 0; li != NULL; li = li->next, idx++)
  760. {
  761. child = li->data;
  762. /* get the child allocation */
  763. allocation = &(child->widget->allocation);
  764. /* check if the drop index is before the half of the allocation */
  765. if ((is_horizontal && x < (allocation->x + allocation->width / 2))
  766. || (!is_horizontal && y < (allocation->y + allocation->height / 2)))
  767. break;
  768. }
  769. return idx;
  770. }
  771. GtkWidget *
  772. panel_itembar_get_child_at_position (PanelItembar *itembar,
  773. gint x,
  774. gint y)
  775. {
  776. PanelItembarChild *child;
  777. GSList *li;
  778. GtkAllocation *allocation;
  779. panel_return_val_if_fail (PANEL_IS_ITEMBAR (itembar), NULL);
  780. for (li = itembar->children; li != NULL; li = li->next)
  781. {
  782. child = li->data;
  783. /* get the child allocation */
  784. allocation = &(child->widget->allocation);
  785. /* check if the coordinate is inside the allocation */
  786. if (x >= allocation->x && x <= (allocation->x + allocation->width)
  787. && y >= allocation->y && y <= (allocation->y + allocation->height))
  788. return child->widget;
  789. }
  790. return NULL;
  791. }