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