Add support for monitors "All" to span panel over all monitors.
[lxde/lxpanel.git] / src / icon-grid.c
1 /**
2 * Copyright (c) 2009-2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <gtk/gtk.h>
20 #include <string.h>
21
22 #include "icon-grid.h"
23
24 /* Properties */
25 enum {
26 PROP_0,
27 PROP_ORIENTATION,
28 PROP_SPACING,
29 PROP_CONSTRAIN_WIDTH,
30 PROP_ASPECT_WIDTH
31 //PROP_FILL_WIDTH
32 };
33
34 /* Representative of an icon grid. This is a manager that packs widgets into a rectangular grid whose size adapts to conditions. */
35 struct _PanelIconGrid
36 {
37 GtkContainer container; /* Parent widget */
38 GList * children; /* List of icon grid elements */
39 GtkOrientation orientation; /* Desired orientation */
40 gint child_width; /* Desired child width */
41 gint child_height; /* Desired child height */
42 gint spacing; /* Desired spacing between grid elements */
43 gint target_dimension; /* Desired dimension perpendicular to orientation */
44 gboolean constrain_width : 1; /* True if width should be constrained by allocated space */
45 gboolean aspect_width : 1; /* True if children should maintain aspect */
46 gboolean fill_width : 1; /* True if children should fill unused width */
47 int rows; /* Computed layout rows */
48 int columns; /* Computed layout columns */
49 int constrained_child_width; /* Child width constrained by allocation */
50 GdkWindow *event_window; /* Event window if NO_WINDOW is set */
51 };
52
53 struct _PanelIconGridClass
54 {
55 GtkContainerClass parent_class;
56 };
57
58 static void panel_icon_grid_size_request(GtkWidget *widget, GtkRequisition *requisition);
59
60 /* Establish the widget placement of an icon grid. */
61 static void panel_icon_grid_size_allocate(GtkWidget *widget,
62 GtkAllocation *allocation)
63 {
64 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
65 GtkRequisition req;
66 GtkAllocation child_allocation;
67 int child_width;
68 int child_height;
69 GtkTextDirection direction;
70 guint border;
71 int x_delta;
72 guint next_coord;
73 guint x, y;
74 GList *ige;
75 GtkWidget *child;
76
77 /* Apply given allocation */
78 gtk_widget_set_allocation(widget, allocation);
79 border = gtk_container_get_border_width(GTK_CONTAINER(widget));
80 #if GTK_CHECK_VERSION(2, 20, 0)
81 if (gtk_widget_get_realized(widget))
82 #else
83 if (GTK_WIDGET_REALIZED(widget))
84 #endif
85 {
86 if (!gtk_widget_get_has_window(widget))
87 {
88 child_allocation.x = allocation->x + border;
89 child_allocation.y = allocation->y + border;
90 }
91 else
92 {
93 child_allocation.x = 0;
94 child_allocation.y = 0;
95 }
96 child_allocation.width = MAX(allocation->width - border * 2, 0);
97 child_allocation.height = MAX(allocation->height - border * 2, 0);
98 if (ig->event_window != NULL)
99 gdk_window_move_resize(ig->event_window,
100 child_allocation.x,
101 child_allocation.y,
102 child_allocation.width,
103 child_allocation.height);
104 if (gtk_widget_get_has_window(widget))
105 gdk_window_move_resize(gtk_widget_get_window(widget),
106 allocation->x + border,
107 allocation->y + border,
108 child_allocation.width,
109 child_allocation.height);
110 }
111
112 /* Get and save the desired container geometry. */
113 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL && allocation->height > 1)
114 ig->target_dimension = allocation->height;
115 else if (ig->orientation == GTK_ORIENTATION_VERTICAL && allocation->width > 1)
116 ig->target_dimension = allocation->width;
117 child_width = ig->child_width;
118 child_height = ig->child_height;
119
120 /* Calculate required size without borders */
121 ig->constrained_child_width = 1;
122 panel_icon_grid_size_request(widget, &req);
123 req.width -= 2 * border;
124 req.height -= 2 * border;
125
126 /* Get the constrained child geometry if the allocated geometry is insufficient.
127 * All children are still the same size and share equally in the deficit. */
128 ig->constrained_child_width = ig->child_width;
129 if (ig->aspect_width && !ig->constrain_width)
130 ig->constrained_child_width = allocation->width;
131 if ((ig->columns != 0) && (ig->rows != 0) && (allocation->width > 1))
132 {
133 if (req.width > allocation->width && ig->constrain_width)
134 ig->constrained_child_width = child_width = (allocation->width + ig->spacing - 2 * border) / ig->columns - ig->spacing;
135 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL && req.height < allocation->height)
136 child_height = (allocation->height + ig->spacing - 2 * border) / ig->rows - ig->spacing;
137 }
138
139 /* Initialize parameters to control repositioning each visible child. */
140 direction = gtk_widget_get_direction(widget);
141 x = (direction == GTK_TEXT_DIR_RTL) ? allocation->width - border : border;
142 y = border;
143 x_delta = 0;
144 next_coord = border;
145
146 /* Reposition each visible child. */
147 for (ige = ig->children; ige != NULL; ige = ige->next)
148 {
149 child = ige->data;
150 if (gtk_widget_get_visible(child))
151 {
152 /* Do necessary operations on the child. */
153 gtk_widget_get_requisition(child, &req);
154 child_allocation.width = MIN(req.width, ig->constrained_child_width);
155 child_allocation.height = MIN(req.height, child_height);
156
157 /* Check this grid position */
158 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
159 {
160 y = next_coord;
161 if (y + child_height > allocation->height - border && y > border)
162 {
163 y = border;
164 if (direction == GTK_TEXT_DIR_RTL)
165 x -= (x_delta + ig->spacing);
166 else
167 x += (x_delta + ig->spacing);
168 x_delta = 0;
169 // FIXME: if fill_width and rows = 1 then allocate whole column
170 }
171 next_coord = y + child_height + ig->spacing;
172 x_delta = MAX(x_delta, child_allocation.width);
173 }
174 else
175 {
176 // FIXME: if fill_width then use aspect to check delta
177 if (direction == GTK_TEXT_DIR_RTL)
178 {
179 next_coord = x - child_allocation.width;
180 if (x < allocation->width - border)
181 {
182 next_coord -= ig->spacing;
183 if (next_coord < border)
184 {
185 next_coord = allocation->width - border;
186 y += child_height + ig->spacing;
187 }
188 }
189 x = next_coord;
190 }
191 else
192 {
193 x = next_coord;
194 if (x + child_allocation.width > allocation->width - border && x > border)
195 {
196 x = border;
197 y += child_height + ig->spacing;
198 }
199 next_coord = x + child_allocation.width + ig->spacing;
200 }
201 }
202 child_allocation.x = x;
203 if (req.height < child_height - 1)
204 y += (child_height - req.height) / 2;
205 child_allocation.y = y;
206
207 if (!gtk_widget_get_has_window (widget))
208 {
209 child_allocation.x += allocation->x;
210 child_allocation.y += allocation->y;
211 }
212 // FIXME: if fill_width and rows > 1 then delay allocation
213 gtk_widget_size_allocate(child, &child_allocation);
214 }
215 }
216 }
217
218 /* Establish the geometry of an icon grid. */
219 static void panel_icon_grid_size_request(GtkWidget *widget,
220 GtkRequisition *requisition)
221 {
222 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
223 GList *ige;
224 int target_dimension = ig->target_dimension;
225 guint border = gtk_container_get_border_width(GTK_CONTAINER(widget));
226 gint old_rows = ig->rows;
227 gint old_columns = ig->columns;
228 gint row = 0, w = 0;
229 GtkRequisition child_requisition;
230
231 requisition->width = 0;
232 requisition->height = 0;
233 ig->rows = 0;
234 ig->columns = 0;
235 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
236 {
237 /* In horizontal orientation, fit as many rows into the available height as possible.
238 * Then allocate as many columns as necessary. Guard against zerodivides. */
239 if ((ig->child_height + ig->spacing) != 0)
240 ig->rows = (target_dimension + ig->spacing - border * 2) / (ig->child_height + ig->spacing);
241 if (ig->rows == 0)
242 ig->rows = 1;
243 /* Count visible children and columns. */
244 for (ige = ig->children; ige != NULL; ige = ige->next)
245 if (gtk_widget_get_visible(ige->data))
246 {
247 gtk_widget_size_request(ige->data, &child_requisition);
248 if (row == 0)
249 ig->columns++;
250 w = MAX(w, child_requisition.width);
251 row++;
252 if (row == ig->rows)
253 {
254 row = 0;
255 if (requisition->width > 0)
256 requisition->width += ig->spacing;
257 requisition->width += w;
258 row = w = 0;
259 }
260 }
261 if (row > 0)
262 requisition->width += w;
263 /* if ((ig->columns == 1) && (ig->rows > visible_children))
264 ig->rows = visible_children; */
265 }
266 else
267 {
268 /* In vertical orientation, fit as many columns into the available width as possible.
269 * Then allocate as many rows as necessary. Guard against zerodivides. */
270 if ((ig->child_width + ig->spacing) != 0)
271 ig->columns = (target_dimension + ig->spacing - border * 2) / (ig->child_width + ig->spacing);
272 if (ig->columns == 0)
273 ig->columns = 1;
274 /* Count visible children and rows. */
275 for (ige = ig->children; ige != NULL; ige = ige->next)
276 if (gtk_widget_get_visible(ige->data))
277 {
278 gtk_widget_size_request(ige->data, &child_requisition);
279 if (w > 0 && w + child_requisition.width > target_dimension)
280 {
281 w = 0;
282 ig->rows++;
283 }
284 w += child_requisition.width;
285 requisition->width = MAX(requisition->width, w);
286 }
287 if (w > 0)
288 ig->rows++;
289 }
290
291 /* Compute the requisition. */
292 if ((ig->columns == 0) || (ig->rows == 0))
293 requisition->height = 0;
294 else
295 requisition->height = (ig->child_height + ig->spacing) * ig->rows - ig->spacing + 2 * border;
296
297 if (ig->rows != old_rows || ig->columns != old_columns)
298 gtk_widget_queue_resize(widget);
299 }
300
301 /* Handler for "size-request" event on the icon grid element. */
302 static void icon_grid_element_size_request(GtkWidget * widget, GtkRequisition * requisition, PanelIconGrid * ig)
303 {
304 /* This is our opportunity to request space for the element. */
305 if (ig->aspect_width && !ig->constrain_width &&
306 requisition->width > 1 && requisition->height > 1)
307 {
308 /* calculate width from aspect */
309 gdouble ratio = (gdouble)requisition->width / requisition->height;
310 requisition->width = ig->child_height * ratio;
311 }
312 else
313 {
314 requisition->width = ig->child_width;
315 if ((ig->constrain_width) && (ig->constrained_child_width > 1))
316 requisition->width = ig->constrained_child_width;
317 }
318 requisition->height = ig->child_height;
319 }
320
321 /* Add an icon grid element and establish its initial visibility. */
322 static void panel_icon_grid_add(GtkContainer *container, GtkWidget *widget)
323 {
324 PanelIconGrid *ig = PANEL_ICON_GRID(container);
325
326 /* Insert at the tail of the child list. This keeps the graphics in the order they were added. */
327 ig->children = g_list_append(ig->children, widget);
328
329 /* Add the widget to the layout container. */
330 g_signal_connect(G_OBJECT(widget), "size-request",
331 G_CALLBACK(icon_grid_element_size_request), container);
332 gtk_widget_set_parent(widget, GTK_WIDGET(container));
333 // gtk_widget_queue_resize(GTK_WIDGET(container));
334 }
335
336 void panel_icon_grid_set_constrain_width(PanelIconGrid * ig, gboolean constrain_width)
337 {
338 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
339
340 if ((!ig->constrain_width && !constrain_width) ||
341 (ig->constrain_width && constrain_width))
342 return;
343
344 ig->constrain_width = !!constrain_width;
345 gtk_widget_queue_resize(GTK_WIDGET(ig));
346 }
347
348 void panel_icon_grid_set_aspect_width(PanelIconGrid * ig, gboolean aspect_width)
349 {
350 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
351
352 if ((!ig->aspect_width && !aspect_width) || (ig->aspect_width && aspect_width))
353 return;
354
355 ig->aspect_width = !!aspect_width;
356 gtk_widget_queue_resize(GTK_WIDGET(ig));
357 }
358
359 /* void panel_icon_grid_set_fill_width(PanelIconGrid * ig, gboolean fill_width)
360 {
361 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
362
363 if ((!ig->fill_width && !fill_width) || (ig->fill_width && fill_width))
364 return;
365
366 ig->fill_width = !!fill_width;
367 gtk_widget_queue_resize(GTK_WIDGET(ig));
368 } */
369
370 /* Remove an icon grid element. */
371 static void panel_icon_grid_remove(GtkContainer *container, GtkWidget *widget)
372 {
373 PanelIconGrid *ig = PANEL_ICON_GRID(container);
374 GList *children = ig->children;
375 GtkWidget *child;
376
377 while (children)
378 {
379 child = children->data;
380 if (widget == child)
381 {
382 gboolean was_visible = gtk_widget_get_visible(widget);
383
384 /* The child is found. Remove from child list and layout container. */
385 g_signal_handlers_disconnect_by_func(widget,
386 icon_grid_element_size_request,
387 container);
388 gtk_widget_unparent (widget);
389 ig->children = g_list_remove_link(ig->children, children);
390 g_list_free(children);
391
392 /* Do a relayout if needed. */
393 if (was_visible)
394 gtk_widget_queue_resize(GTK_WIDGET(ig));
395 break;
396 }
397 children = children->next;
398 }
399 }
400
401 /* Get the index of an icon grid element. */
402 gint panel_icon_grid_get_child_position(PanelIconGrid * ig, GtkWidget * child)
403 {
404 g_return_val_if_fail(PANEL_IS_ICON_GRID(ig), -1);
405
406 return g_list_index(ig->children, child);
407 }
408
409 /* Reorder an icon grid element. */
410 void panel_icon_grid_reorder_child(PanelIconGrid * ig, GtkWidget * child, gint position)
411 {
412 GList *old_link;
413 GList *new_link;
414 gint old_position;
415
416 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
417 g_return_if_fail(GTK_IS_WIDGET(child));
418
419 old_link = ig->children;
420 old_position = 0;
421 while (old_link)
422 {
423 if (old_link->data == child)
424 break;
425 old_link = old_link->next;
426 old_position++;
427 }
428
429 g_return_if_fail(old_link != NULL);
430
431 if (position == old_position)
432 return;
433
434 /* Remove the child from its current position. */
435 ig->children = g_list_delete_link(ig->children, old_link);
436 if (position < 0)
437 new_link = NULL;
438 else
439 new_link = g_list_nth(ig->children, position);
440
441 /* If the child was found, insert it at the new position. */
442 ig->children = g_list_insert_before(ig->children, new_link, child);
443
444 /* Do a relayout. */
445 if (gtk_widget_get_visible(child) && gtk_widget_get_visible(GTK_WIDGET(ig)))
446 gtk_widget_queue_resize(child);
447 }
448
449 /* Change the geometry of an icon grid. */
450 void panel_icon_grid_set_geometry(PanelIconGrid * ig,
451 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
452 {
453 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
454
455 gtk_container_set_border_width(GTK_CONTAINER(ig), border);
456
457 if (ig->orientation == orientation && ig->child_width == child_width &&
458 ig->child_height == child_height && ig->spacing == spacing &&
459 ig->target_dimension == target_dimension)
460 return;
461
462 ig->orientation = orientation;
463 ig->child_width = child_width;
464 ig->constrained_child_width = child_width;
465 ig->child_height = child_height;
466 ig->spacing = spacing;
467 ig->target_dimension = target_dimension;
468 gtk_widget_queue_resize(GTK_WIDGET(ig));
469 }
470
471 G_DEFINE_TYPE_WITH_CODE(PanelIconGrid, panel_icon_grid, GTK_TYPE_CONTAINER,
472 G_IMPLEMENT_INTERFACE(GTK_TYPE_ORIENTABLE, NULL));
473
474 static void panel_icon_grid_set_property(GObject *object, guint prop_id,
475 const GValue *value, GParamSpec *pspec)
476 {
477 PanelIconGrid *ig = PANEL_ICON_GRID(object);
478 gint spacing;
479 GtkOrientation orientation;
480
481 switch (prop_id)
482 {
483 case PROP_ORIENTATION:
484 orientation = g_value_get_enum(value);
485 if (orientation != ig->orientation)
486 {
487 ig->orientation = orientation;
488 gtk_widget_queue_resize(GTK_WIDGET(ig));
489 }
490 break;
491 case PROP_SPACING:
492 spacing = g_value_get_int(value);
493 if (spacing != ig->spacing)
494 {
495 ig->spacing = spacing;
496 g_object_notify(object, "spacing");
497 gtk_widget_queue_resize(GTK_WIDGET(ig));
498 }
499 break;
500 case PROP_CONSTRAIN_WIDTH:
501 panel_icon_grid_set_constrain_width(ig, g_value_get_boolean(value));
502 break;
503 case PROP_ASPECT_WIDTH:
504 panel_icon_grid_set_aspect_width(ig, g_value_get_boolean(value));
505 break;
506 /* case PROP_FILL_WIDTH:
507 panel_icon_grid_set_fill_width(ig, g_value_get_boolean(value));
508 break; */
509 default:
510 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
511 break;
512 }
513 }
514
515 static void panel_icon_grid_get_property(GObject *object, guint prop_id,
516 GValue *value, GParamSpec *pspec)
517 {
518 PanelIconGrid *ig = PANEL_ICON_GRID(object);
519
520 switch (prop_id)
521 {
522 case PROP_ORIENTATION:
523 g_value_set_enum(value, ig->orientation);
524 break;
525 case PROP_SPACING:
526 g_value_set_int(value, ig->spacing);
527 break;
528 case PROP_CONSTRAIN_WIDTH:
529 g_value_set_boolean(value, ig->constrain_width);
530 break;
531 case PROP_ASPECT_WIDTH:
532 g_value_set_boolean(value, ig->aspect_width);
533 break;
534 /* case PROP_FILL_WIDTH:
535 g_value_set_boolean(value, ig->fill_width);
536 break; */
537 default:
538 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
539 break;
540 }
541 }
542
543 /* realize()...expose() are taken from GtkEventBox implementation */
544 static void panel_icon_grid_realize(GtkWidget *widget)
545 {
546 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
547 GdkWindow *window;
548 GtkStyle *style;
549 GtkAllocation allocation;
550 GdkWindowAttr attributes;
551 guint border = gtk_container_get_border_width(GTK_CONTAINER(widget));
552 gint attributes_mask;
553 gboolean visible_window;
554
555 #if GTK_CHECK_VERSION(2, 20, 0)
556 gtk_widget_set_realized(widget, TRUE);
557 #else
558 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
559 #endif
560
561 gtk_widget_get_allocation(widget, &allocation);
562 attributes.x = allocation.x + border;
563 attributes.y = allocation.y + border;
564 attributes.width = allocation.width - 2*border;
565 attributes.height = allocation.height - 2*border;
566 attributes.window_type = GDK_WINDOW_CHILD;
567 attributes.event_mask = gtk_widget_get_events(widget)
568 | GDK_BUTTON_MOTION_MASK
569 | GDK_BUTTON_PRESS_MASK
570 | GDK_BUTTON_RELEASE_MASK
571 | GDK_EXPOSURE_MASK
572 | GDK_ENTER_NOTIFY_MASK
573 | GDK_LEAVE_NOTIFY_MASK;
574
575 visible_window = gtk_widget_get_has_window(widget);
576 if (visible_window)
577 {
578 attributes.visual = gtk_widget_get_visual(widget);
579 attributes.colormap = gtk_widget_get_colormap(widget);
580 attributes.wclass = GDK_INPUT_OUTPUT;
581
582 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
583
584 window = gdk_window_new(gtk_widget_get_parent_window(widget),
585 &attributes, attributes_mask);
586 gtk_widget_set_window(widget, window);
587 gdk_window_set_user_data(window, widget);
588 }
589 else
590 {
591 window = gtk_widget_get_parent_window(widget);
592 gtk_widget_set_window(widget, window);
593 g_object_ref(window);
594
595 attributes.wclass = GDK_INPUT_ONLY;
596 attributes_mask = GDK_WA_X | GDK_WA_Y;
597
598 ig->event_window = gdk_window_new(window, &attributes, attributes_mask);
599 gdk_window_set_user_data(ig->event_window, widget);
600 }
601
602 style = gtk_style_attach(gtk_widget_get_style(widget), window);
603 gtk_widget_set_style(widget, style);
604
605 if (visible_window)
606 gtk_style_set_background(style, window, GTK_STATE_NORMAL);
607 }
608
609 static void panel_icon_grid_unrealize(GtkWidget *widget)
610 {
611 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
612
613 if (ig->event_window != NULL)
614 {
615 gdk_window_set_user_data(ig->event_window, NULL);
616 gdk_window_destroy(ig->event_window);
617 ig->event_window = NULL;
618 }
619
620 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->unrealize(widget);
621 }
622
623 static void panel_icon_grid_map(GtkWidget *widget)
624 {
625 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
626
627 if (ig->event_window != NULL)
628 gdk_window_show(ig->event_window);
629 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->map(widget);
630 }
631
632 static void panel_icon_grid_unmap(GtkWidget *widget)
633 {
634 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
635
636 if (ig->event_window != NULL)
637 gdk_window_hide(ig->event_window);
638 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->unmap(widget);
639 }
640
641 static gboolean panel_icon_grid_expose(GtkWidget *widget, GdkEventExpose *event)
642 {
643 if (gtk_widget_is_drawable(widget))
644 {
645 if (gtk_widget_get_has_window(widget) &&
646 !gtk_widget_get_app_paintable(widget))
647 gtk_paint_flat_box(gtk_widget_get_style(widget),
648 gtk_widget_get_window(widget),
649 gtk_widget_get_state(widget), GTK_SHADOW_NONE,
650 &event->area, widget, "panelicongrid",
651 0, 0, -1, -1);
652
653 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->expose_event(widget, event);
654 }
655 return FALSE;
656 }
657
658 static void panel_icon_grid_forall(GtkContainer *container,
659 gboolean include_internals,
660 GtkCallback callback,
661 gpointer callback_data)
662 {
663 PanelIconGrid *ig = PANEL_ICON_GRID(container);
664 GList *children = ig->children;
665 GtkWidget *child;
666
667 while (children)
668 {
669 child = children->data;
670 children = children->next;
671 (* callback)(child, callback_data);
672 }
673 }
674
675 static GType panel_icon_grid_child_type(GtkContainer *container)
676 {
677 return GTK_TYPE_WIDGET;
678 }
679
680 static void panel_icon_grid_class_init(PanelIconGridClass *class)
681 {
682 GObjectClass *object_class = G_OBJECT_CLASS(class);
683 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
684 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class);
685
686 object_class->set_property = panel_icon_grid_set_property;
687 object_class->get_property = panel_icon_grid_get_property;
688
689 widget_class->size_request = panel_icon_grid_size_request;
690 widget_class->size_allocate = panel_icon_grid_size_allocate;
691 widget_class->realize = panel_icon_grid_realize;
692 widget_class->unrealize = panel_icon_grid_unrealize;
693 widget_class->map = panel_icon_grid_map;
694 widget_class->unmap = panel_icon_grid_unmap;
695 widget_class->expose_event = panel_icon_grid_expose;
696
697 container_class->add = panel_icon_grid_add;
698 container_class->remove = panel_icon_grid_remove;
699 container_class->forall = panel_icon_grid_forall;
700 container_class->child_type = panel_icon_grid_child_type;
701
702 g_object_class_override_property(object_class,
703 PROP_ORIENTATION,
704 "orientation");
705 g_object_class_install_property(object_class,
706 PROP_SPACING,
707 g_param_spec_int("spacing",
708 "Spacing",
709 "The amount of space between children",
710 0,
711 G_MAXINT,
712 0,
713 G_PARAM_READWRITE));
714 g_object_class_install_property(object_class,
715 PROP_CONSTRAIN_WIDTH,
716 g_param_spec_boolean("constrain-width",
717 "Constrain width",
718 "Whether to constrain width by allocated space",
719 FALSE, G_PARAM_READWRITE));
720 g_object_class_install_property(object_class,
721 PROP_ASPECT_WIDTH,
722 g_param_spec_boolean("aspect-width",
723 "Maintain children aspect",
724 "Whether to set children width to maintain their aspect",
725 FALSE, G_PARAM_READWRITE));
726 }
727
728 static void panel_icon_grid_init(PanelIconGrid *ig)
729 {
730 gtk_widget_set_has_window(GTK_WIDGET(ig), FALSE);
731 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(ig), FALSE);
732
733 ig->orientation = GTK_ORIENTATION_HORIZONTAL;
734 }
735
736 /* Establish an icon grid in a specified container widget.
737 * The icon grid manages the contents of the container.
738 * The orientation, geometry of the elements, and spacing can be varied. All elements are the same size. */
739 GtkWidget * panel_icon_grid_new(
740 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
741 {
742 /* Create a structure representing the icon grid and collect the parameters. */
743 PanelIconGrid * ig = g_object_new(PANEL_TYPE_ICON_GRID,
744 "orientation", orientation,
745 "spacing", spacing, NULL);
746
747 ig->child_width = child_width;
748 ig->constrained_child_width = child_width;
749 ig->child_height = child_height;
750 ig->target_dimension = target_dimension;
751 gtk_container_set_border_width(GTK_CONTAINER(ig), border);
752
753 return (GtkWidget *)ig;
754 }