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