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