Adding upstream version 0.8.0.
[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 }
295 w += child_requisition.width;
296 requisition->width = MAX(requisition->width, w);
297 }
298 if (w > 0)
299 ig->rows++;
2ba86315
DB
300 }
301
2ba86315
DB
302 /* Compute the requisition. */
303 if ((ig->columns == 0) || (ig->rows == 0))
6b775dbb 304 requisition->height = 0;
2ba86315 305 else
f7ecd6ce
AG
306 requisition->height = (ig->child_height + ig->spacing) * ig->rows - ig->spacing + 2 * border;
307
6b775dbb
AG
308 if (ig->rows != old_rows || ig->columns != old_columns)
309 gtk_widget_queue_resize(widget);
2ba86315
DB
310}
311
f7ecd6ce
AG
312#if GTK_CHECK_VERSION(3, 0, 0)
313static void panel_icon_grid_get_preferred_width(GtkWidget *widget,
314 gint *minimal_width,
315 gint *natural_width)
2ba86315 316{
f7ecd6ce
AG
317 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
318 GtkRequisition requisition;
319
320 if (ig->orientation == GTK_ORIENTATION_VERTICAL)
321 {
322 if (minimal_width)
323 *minimal_width = MIN(ig->target_dimension, ig->child_width);
324 if (natural_width)
325 *natural_width = ig->target_dimension;
326 return;
327 }
328 panel_icon_grid_size_request(widget, &requisition);
329 if (minimal_width)
330 *minimal_width = requisition.width;
331 if (natural_width)
332 *natural_width = requisition.width;
2ba86315
DB
333}
334
f7ecd6ce
AG
335static void panel_icon_grid_get_preferred_height(GtkWidget *widget,
336 gint *minimal_height,
337 gint *natural_height)
338{
339 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
340 GtkRequisition requisition;
341
342 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
343 requisition.height = ig->target_dimension;
344 else
345 panel_icon_grid_size_request(widget, &requisition);
346 if (minimal_height)
347 *minimal_height = requisition.height;
348 if (natural_height)
349 *natural_height = requisition.height;
350}
351#endif
352
6b775dbb
AG
353/* Add an icon grid element and establish its initial visibility. */
354static void panel_icon_grid_add(GtkContainer *container, GtkWidget *widget)
2ba86315 355{
6b775dbb 356 PanelIconGrid *ig = PANEL_ICON_GRID(container);
2ba86315 357
6b775dbb
AG
358 /* Insert at the tail of the child list. This keeps the graphics in the order they were added. */
359 ig->children = g_list_append(ig->children, widget);
2ba86315 360
6b775dbb 361 /* Add the widget to the layout container. */
6b775dbb
AG
362 gtk_widget_set_parent(widget, GTK_WIDGET(container));
363// gtk_widget_queue_resize(GTK_WIDGET(container));
2ba86315
DB
364}
365
6b775dbb 366void panel_icon_grid_set_constrain_width(PanelIconGrid * ig, gboolean constrain_width)
2ba86315 367{
6b775dbb 368 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
2ba86315 369
6b775dbb
AG
370 if ((!ig->constrain_width && !constrain_width) ||
371 (ig->constrain_width && constrain_width))
372 return;
2ba86315 373
6b775dbb
AG
374 ig->constrain_width = !!constrain_width;
375 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315
DB
376}
377
f7ecd6ce
AG
378void panel_icon_grid_set_aspect_width(PanelIconGrid * ig, gboolean aspect_width)
379{
380 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
381
382 if ((!ig->aspect_width && !aspect_width) || (ig->aspect_width && aspect_width))
383 return;
384
385 ig->aspect_width = !!aspect_width;
386 gtk_widget_queue_resize(GTK_WIDGET(ig));
387}
388
6b775dbb 389/* void panel_icon_grid_set_fill_width(PanelIconGrid * ig, gboolean fill_width)
2ba86315 390{
6b775dbb
AG
391 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
392
393 if ((!ig->fill_width && !fill_width) || (ig->fill_width && fill_width))
394 return;
395
396 ig->fill_width = !!fill_width;
397 gtk_widget_queue_resize(GTK_WIDGET(ig));
398} */
2ba86315
DB
399
400/* Remove an icon grid element. */
6b775dbb 401static void panel_icon_grid_remove(GtkContainer *container, GtkWidget *widget)
2ba86315 402{
6b775dbb
AG
403 PanelIconGrid *ig = PANEL_ICON_GRID(container);
404 GList *children = ig->children;
405 GtkWidget *child;
406
407 while (children)
2ba86315 408 {
6b775dbb
AG
409 child = children->data;
410 if (widget == child)
2ba86315 411 {
6b775dbb 412 gboolean was_visible = gtk_widget_get_visible(widget);
2ba86315 413
6b775dbb 414 /* The child is found. Remove from child list and layout container. */
6b775dbb
AG
415 gtk_widget_unparent (widget);
416 ig->children = g_list_remove_link(ig->children, children);
417 g_list_free(children);
418
419 /* Do a relayout if needed. */
420 if (was_visible)
421 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315
DB
422 break;
423 }
6b775dbb 424 children = children->next;
2ba86315
DB
425 }
426}
427
6b775dbb
AG
428/* Get the index of an icon grid element. */
429gint panel_icon_grid_get_child_position(PanelIconGrid * ig, GtkWidget * child)
430{
431 g_return_val_if_fail(PANEL_IS_ICON_GRID(ig), -1);
432
433 return g_list_index(ig->children, child);
434}
435
2ba86315 436/* Reorder an icon grid element. */
6b775dbb 437void panel_icon_grid_reorder_child(PanelIconGrid * ig, GtkWidget * child, gint position)
2ba86315 438{
6b775dbb
AG
439 GList *old_link;
440 GList *new_link;
441 gint old_position;
442
443 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
444 g_return_if_fail(GTK_IS_WIDGET(child));
445
446 old_link = ig->children;
447 old_position = 0;
448 while (old_link)
2ba86315 449 {
6b775dbb 450 if (old_link->data == child)
2ba86315 451 break;
6b775dbb
AG
452 old_link = old_link->next;
453 old_position++;
2ba86315
DB
454 }
455
6b775dbb
AG
456 g_return_if_fail(old_link != NULL);
457
458 if (position == old_position)
459 return;
460
461 /* Remove the child from its current position. */
462 ig->children = g_list_delete_link(ig->children, old_link);
463 if (position < 0)
464 new_link = NULL;
465 else
466 new_link = g_list_nth(ig->children, position);
467
2ba86315 468 /* If the child was found, insert it at the new position. */
6b775dbb 469 ig->children = g_list_insert_before(ig->children, new_link, child);
2ba86315 470
6b775dbb
AG
471 /* Do a relayout. */
472 if (gtk_widget_get_visible(child) && gtk_widget_get_visible(GTK_WIDGET(ig)))
473 gtk_widget_queue_resize(child);
2ba86315
DB
474}
475
476/* Change the geometry of an icon grid. */
6b775dbb 477void panel_icon_grid_set_geometry(PanelIconGrid * ig,
2ba86315
DB
478 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
479{
6b775dbb
AG
480 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
481
482 gtk_container_set_border_width(GTK_CONTAINER(ig), border);
483
484 if (ig->orientation == orientation && ig->child_width == child_width &&
485 ig->child_height == child_height && ig->spacing == spacing &&
486 ig->target_dimension == target_dimension)
487 return;
488
2ba86315
DB
489 ig->orientation = orientation;
490 ig->child_width = child_width;
2ba86315
DB
491 ig->child_height = child_height;
492 ig->spacing = spacing;
2ba86315 493 ig->target_dimension = target_dimension;
6b775dbb 494 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315
DB
495}
496
6b775dbb
AG
497G_DEFINE_TYPE_WITH_CODE(PanelIconGrid, panel_icon_grid, GTK_TYPE_CONTAINER,
498 G_IMPLEMENT_INTERFACE(GTK_TYPE_ORIENTABLE, NULL));
499
500static void panel_icon_grid_set_property(GObject *object, guint prop_id,
501 const GValue *value, GParamSpec *pspec)
2ba86315 502{
6b775dbb
AG
503 PanelIconGrid *ig = PANEL_ICON_GRID(object);
504 gint spacing;
505 GtkOrientation orientation;
506
507 switch (prop_id)
2ba86315 508 {
6b775dbb
AG
509 case PROP_ORIENTATION:
510 orientation = g_value_get_enum(value);
511 if (orientation != ig->orientation)
2ba86315 512 {
6b775dbb
AG
513 ig->orientation = orientation;
514 gtk_widget_queue_resize(GTK_WIDGET(ig));
515 }
516 break;
517 case PROP_SPACING:
518 spacing = g_value_get_int(value);
519 if (spacing != ig->spacing)
520 {
521 ig->spacing = spacing;
522 g_object_notify(object, "spacing");
523 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315 524 }
6b775dbb
AG
525 break;
526 case PROP_CONSTRAIN_WIDTH:
527 panel_icon_grid_set_constrain_width(ig, g_value_get_boolean(value));
528 break;
f7ecd6ce
AG
529 case PROP_ASPECT_WIDTH:
530 panel_icon_grid_set_aspect_width(ig, g_value_get_boolean(value));
531 break;
6b775dbb
AG
532 /* case PROP_FILL_WIDTH:
533 panel_icon_grid_set_fill_width(ig, g_value_get_boolean(value));
534 break; */
535 default:
536 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
537 break;
538 }
539}
540
541static void panel_icon_grid_get_property(GObject *object, guint prop_id,
542 GValue *value, GParamSpec *pspec)
543{
544 PanelIconGrid *ig = PANEL_ICON_GRID(object);
545
546 switch (prop_id)
547 {
548 case PROP_ORIENTATION:
549 g_value_set_enum(value, ig->orientation);
550 break;
551 case PROP_SPACING:
552 g_value_set_int(value, ig->spacing);
553 break;
554 case PROP_CONSTRAIN_WIDTH:
555 g_value_set_boolean(value, ig->constrain_width);
556 break;
f7ecd6ce
AG
557 case PROP_ASPECT_WIDTH:
558 g_value_set_boolean(value, ig->aspect_width);
559 break;
6b775dbb
AG
560 /* case PROP_FILL_WIDTH:
561 g_value_set_boolean(value, ig->fill_width);
562 break; */
563 default:
564 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
565 break;
566 }
567}
568
569/* realize()...expose() are taken from GtkEventBox implementation */
570static void panel_icon_grid_realize(GtkWidget *widget)
571{
572 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
573 GdkWindow *window;
574 GtkStyle *style;
575 GtkAllocation allocation;
576 GdkWindowAttr attributes;
577 guint border = gtk_container_get_border_width(GTK_CONTAINER(widget));
578 gint attributes_mask;
579 gboolean visible_window;
580
19ab5cea 581#if GTK_CHECK_VERSION(2, 20, 0)
6b775dbb 582 gtk_widget_set_realized(widget, TRUE);
19ab5cea
AG
583#else
584 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
585#endif
6b775dbb
AG
586
587 gtk_widget_get_allocation(widget, &allocation);
588 attributes.x = allocation.x + border;
589 attributes.y = allocation.y + border;
590 attributes.width = allocation.width - 2*border;
591 attributes.height = allocation.height - 2*border;
592 attributes.window_type = GDK_WINDOW_CHILD;
593 attributes.event_mask = gtk_widget_get_events(widget)
594 | GDK_BUTTON_MOTION_MASK
595 | GDK_BUTTON_PRESS_MASK
596 | GDK_BUTTON_RELEASE_MASK
597 | GDK_EXPOSURE_MASK
598 | GDK_ENTER_NOTIFY_MASK
599 | GDK_LEAVE_NOTIFY_MASK;
600
601 visible_window = gtk_widget_get_has_window(widget);
602 if (visible_window)
603 {
604 attributes.visual = gtk_widget_get_visual(widget);
f7ecd6ce 605#if !GTK_CHECK_VERSION(3, 0, 0)
6b775dbb 606 attributes.colormap = gtk_widget_get_colormap(widget);
f7ecd6ce 607#endif
6b775dbb
AG
608 attributes.wclass = GDK_INPUT_OUTPUT;
609
f7ecd6ce
AG
610#if GTK_CHECK_VERSION(3, 0, 0)
611 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
612#else
6b775dbb 613 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
f7ecd6ce 614#endif
6b775dbb
AG
615
616 window = gdk_window_new(gtk_widget_get_parent_window(widget),
617 &attributes, attributes_mask);
618 gtk_widget_set_window(widget, window);
619 gdk_window_set_user_data(window, widget);
620 }
621 else
622 {
623 window = gtk_widget_get_parent_window(widget);
624 gtk_widget_set_window(widget, window);
625 g_object_ref(window);
626
627 attributes.wclass = GDK_INPUT_ONLY;
628 attributes_mask = GDK_WA_X | GDK_WA_Y;
629
630 ig->event_window = gdk_window_new(window, &attributes, attributes_mask);
631 gdk_window_set_user_data(ig->event_window, widget);
632 }
633
634 style = gtk_style_attach(gtk_widget_get_style(widget), window);
635 gtk_widget_set_style(widget, style);
636
637 if (visible_window)
638 gtk_style_set_background(style, window, GTK_STATE_NORMAL);
639}
640
641static void panel_icon_grid_unrealize(GtkWidget *widget)
642{
643 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
644
645 if (ig->event_window != NULL)
646 {
647 gdk_window_set_user_data(ig->event_window, NULL);
648 gdk_window_destroy(ig->event_window);
649 ig->event_window = NULL;
650 }
651
652 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->unrealize(widget);
653}
654
655static void panel_icon_grid_map(GtkWidget *widget)
656{
657 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
658
659 if (ig->event_window != NULL)
660 gdk_window_show(ig->event_window);
661 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->map(widget);
662}
663
664static void panel_icon_grid_unmap(GtkWidget *widget)
665{
666 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
667
668 if (ig->event_window != NULL)
669 gdk_window_hide(ig->event_window);
670 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->unmap(widget);
671}
672
f7ecd6ce
AG
673#if GTK_CHECK_VERSION(3, 0, 0)
674static gboolean panel_icon_grid_draw(GtkWidget *widget, cairo_t *cr)
675#else
6b775dbb 676static gboolean panel_icon_grid_expose(GtkWidget *widget, GdkEventExpose *event)
f7ecd6ce 677#endif
6b775dbb
AG
678{
679 if (gtk_widget_is_drawable(widget))
680 {
681 if (gtk_widget_get_has_window(widget) &&
682 !gtk_widget_get_app_paintable(widget))
f7ecd6ce
AG
683#if GTK_CHECK_VERSION(3, 0, 0)
684 gtk_render_background(gtk_widget_get_style_context(widget), cr, 0, 0,
685 gtk_widget_get_allocated_width(widget),
686 gtk_widget_get_allocated_height(widget));
687#else
6b775dbb
AG
688 gtk_paint_flat_box(gtk_widget_get_style(widget),
689 gtk_widget_get_window(widget),
690 gtk_widget_get_state(widget), GTK_SHADOW_NONE,
691 &event->area, widget, "panelicongrid",
692 0, 0, -1, -1);
f7ecd6ce 693#endif
6b775dbb 694
f7ecd6ce
AG
695#if GTK_CHECK_VERSION(3, 0, 0)
696 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->draw(widget, cr);
697#else
6b775dbb 698 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->expose_event(widget, event);
f7ecd6ce 699#endif
2ba86315 700 }
6b775dbb 701 return FALSE;
2ba86315
DB
702}
703
6b775dbb
AG
704static void panel_icon_grid_forall(GtkContainer *container,
705 gboolean include_internals,
706 GtkCallback callback,
707 gpointer callback_data)
2ba86315 708{
6b775dbb
AG
709 PanelIconGrid *ig = PANEL_ICON_GRID(container);
710 GList *children = ig->children;
711 GtkWidget *child;
2ba86315 712
6b775dbb 713 while (children)
2ba86315 714 {
6b775dbb
AG
715 child = children->data;
716 children = children->next;
717 (* callback)(child, callback_data);
2ba86315 718 }
6b775dbb
AG
719}
720
721static GType panel_icon_grid_child_type(GtkContainer *container)
722{
723 return GTK_TYPE_WIDGET;
724}
725
726static void panel_icon_grid_class_init(PanelIconGridClass *class)
727{
728 GObjectClass *object_class = G_OBJECT_CLASS(class);
729 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
730 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class);
731
732 object_class->set_property = panel_icon_grid_set_property;
733 object_class->get_property = panel_icon_grid_get_property;
734
f7ecd6ce
AG
735#if GTK_CHECK_VERSION(3, 0, 0)
736 widget_class->get_preferred_width = panel_icon_grid_get_preferred_width;
737 widget_class->get_preferred_height = panel_icon_grid_get_preferred_height;
738#else
6b775dbb 739 widget_class->size_request = panel_icon_grid_size_request;
f7ecd6ce 740#endif
6b775dbb
AG
741 widget_class->size_allocate = panel_icon_grid_size_allocate;
742 widget_class->realize = panel_icon_grid_realize;
743 widget_class->unrealize = panel_icon_grid_unrealize;
744 widget_class->map = panel_icon_grid_map;
745 widget_class->unmap = panel_icon_grid_unmap;
f7ecd6ce
AG
746#if GTK_CHECK_VERSION(3, 0, 0)
747 widget_class->draw = panel_icon_grid_draw;
748#else
6b775dbb 749 widget_class->expose_event = panel_icon_grid_expose;
f7ecd6ce 750#endif
6b775dbb
AG
751
752 container_class->add = panel_icon_grid_add;
753 container_class->remove = panel_icon_grid_remove;
754 container_class->forall = panel_icon_grid_forall;
755 container_class->child_type = panel_icon_grid_child_type;
756
757 g_object_class_override_property(object_class,
758 PROP_ORIENTATION,
759 "orientation");
760 g_object_class_install_property(object_class,
761 PROP_SPACING,
762 g_param_spec_int("spacing",
763 "Spacing",
764 "The amount of space between children",
765 0,
766 G_MAXINT,
767 0,
768 G_PARAM_READWRITE));
769 g_object_class_install_property(object_class,
770 PROP_CONSTRAIN_WIDTH,
771 g_param_spec_boolean("constrain-width",
772 "Constrain width",
773 "Whether to constrain width by allocated space",
774 FALSE, G_PARAM_READWRITE));
f7ecd6ce
AG
775 g_object_class_install_property(object_class,
776 PROP_ASPECT_WIDTH,
777 g_param_spec_boolean("aspect-width",
778 "Maintain children aspect",
779 "Whether to set children width to maintain their aspect",
780 FALSE, G_PARAM_READWRITE));
6b775dbb
AG
781}
782
783static void panel_icon_grid_init(PanelIconGrid *ig)
784{
785 gtk_widget_set_has_window(GTK_WIDGET(ig), FALSE);
786 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(ig), FALSE);
787
788 ig->orientation = GTK_ORIENTATION_HORIZONTAL;
789}
790
791/* Establish an icon grid in a specified container widget.
792 * The icon grid manages the contents of the container.
793 * The orientation, geometry of the elements, and spacing can be varied. All elements are the same size. */
794GtkWidget * panel_icon_grid_new(
795 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
796{
797 /* Create a structure representing the icon grid and collect the parameters. */
798 PanelIconGrid * ig = g_object_new(PANEL_TYPE_ICON_GRID,
799 "orientation", orientation,
f7ecd6ce
AG
800 "spacing", spacing,
801 "border-width", border,
802 NULL);
6b775dbb
AG
803
804 ig->child_width = child_width;
6b775dbb
AG
805 ig->child_height = child_height;
806 ig->target_dimension = target_dimension;
6b775dbb
AG
807
808 return (GtkWidget *)ig;
2ba86315 809}