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