Merging upstream version 0.8.0 (Closes: #639729, #761971).
[debian/lxpanel.git] / src / icon-grid.c
CommitLineData
1ea75322 1/**
00916e98 2 * Copyright (c) 2009-2014 LxDE Developers, see the file AUTHORS for details.
1ea75322
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>
1ea75322
DB
20#include <string.h>
21
22#include "icon-grid.h"
89173f95 23#include "gtk-compat.h"
1ea75322 24
00916e98
AG
25/* Properties */
26enum {
27 PROP_0,
28 PROP_ORIENTATION,
29 PROP_SPACING,
89173f95
AG
30 PROP_CONSTRAIN_WIDTH,
31 PROP_ASPECT_WIDTH
00916e98
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 */
89173f95 46 gboolean aspect_width : 1; /* True if children should maintain aspect */
00916e98
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 */
00916e98
AG
50 GdkWindow *event_window; /* Event window if NO_WINDOW is set */
51};
52
53struct _PanelIconGridClass
54{
55 GtkContainerClass parent_class;
56};
57
89173f95
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}
1ea75322
DB
74
75/* Establish the widget placement of an icon grid. */
00916e98
AG
76static void panel_icon_grid_size_allocate(GtkWidget *widget,
77 GtkAllocation *allocation)
1ea75322 78{
00916e98
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;
00916e98 86 int x_delta;
89173f95
AG
87 guint next_coord;
88 guint x, y;
00916e98
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));
89173f95
AG
95 child_allocation.width = MAX(allocation->width - border * 2, 0);
96 child_allocation.height = MAX(allocation->height - border * 2, 0);
00916e98
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 }
00916e98
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 }
1ea75322
DB
122
123 /* Get and save the desired container geometry. */
00916e98
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
89173f95
AG
131 /* FIXME: is there any sense to recheck rows and columns again?
132 GTK+ should have it done right before this call. */
1ea75322
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. */
89173f95 136 if ((ig->columns != 0) && (ig->rows != 0) && (child_allocation.width > 0))
1ea75322 137 {
89173f95
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);
1ea75322
DB
145 }
146
147 /* Initialize parameters to control repositioning each visible child. */
00916e98 148 direction = gtk_widget_get_direction(widget);
89173f95
AG
149 x = (direction == GTK_TEXT_DIR_RTL) ? allocation->width - border : border;
150 y = border;
151 x_delta = 0;
152 next_coord = border;
1ea75322
DB
153
154 /* Reposition each visible child. */
00916e98 155 for (ige = ig->children; ige != NULL; ige = ige->next)
1ea75322 156 {
00916e98
AG
157 child = ige->data;
158 if (gtk_widget_get_visible(child))
1ea75322
DB
159 {
160 /* Do necessary operations on the child. */
89173f95
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);
00916e98 164 child_allocation.height = MIN(req.height, child_height);
1ea75322 165
89173f95 166 /* Check this grid position */
1ea75322
DB
167 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
168 {
89173f95
AG
169 y = next_coord;
170 if (y + child_height > allocation->height - border && y > border)
1ea75322 171 {
00916e98 172 y = border;
89173f95
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;
00916e98 178 // FIXME: if fill_width and rows = 1 then allocate whole column
1ea75322 179 }
89173f95
AG
180 next_coord = y + child_height + ig->spacing;
181 x_delta = MAX(x_delta, child_allocation.width);
1ea75322
DB
182 }
183 else
184 {
00916e98 185 // FIXME: if fill_width then use aspect to check delta
89173f95 186 if (direction == GTK_TEXT_DIR_RTL)
1ea75322 187 {
89173f95
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;
1ea75322 199 }
89173f95
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;
1ea75322 220 }
89173f95
AG
221 // FIXME: if fill_width and rows > 1 then delay allocation
222 gtk_widget_size_allocate(child, &child_allocation);
1ea75322
DB
223 }
224 }
1ea75322
DB
225}
226
227/* Establish the geometry of an icon grid. */
00916e98
AG
228static void panel_icon_grid_size_request(GtkWidget *widget,
229 GtkRequisition *requisition)
1ea75322 230{
00916e98 231 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
00916e98
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;
89173f95
AG
237 gint row = 0, w = 0;
238 GtkRequisition child_requisition;
00916e98 239
89173f95
AG
240 requisition->width = 0;
241 requisition->height = 0;
242 ig->rows = 0;
243 ig->columns = 0;
00916e98 244 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
1ea75322
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. */
1ea75322 248 if ((ig->child_height + ig->spacing) != 0)
00916e98 249 ig->rows = (target_dimension + ig->spacing - border * 2) / (ig->child_height + ig->spacing);
1ea75322
DB
250 if (ig->rows == 0)
251 ig->rows = 1;
89173f95
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;
00916e98
AG
273 /* if ((ig->columns == 1) && (ig->rows > visible_children))
274 ig->rows = visible_children; */
1ea75322
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. */
1ea75322 280 if ((ig->child_width + ig->spacing) != 0)
00916e98 281 ig->columns = (target_dimension + ig->spacing - border * 2) / (ig->child_width + ig->spacing);
1ea75322
DB
282 if (ig->columns == 0)
283 ig->columns = 1;
89173f95
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++;
1ea75322
DB
300 }
301
1ea75322
DB
302 /* Compute the requisition. */
303 if ((ig->columns == 0) || (ig->rows == 0))
00916e98 304 requisition->height = 0;
1ea75322 305 else
89173f95
AG
306 requisition->height = (ig->child_height + ig->spacing) * ig->rows - ig->spacing + 2 * border;
307
00916e98
AG
308 if (ig->rows != old_rows || ig->columns != old_columns)
309 gtk_widget_queue_resize(widget);
1ea75322
DB
310}
311
89173f95
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)
1ea75322 316{
89173f95
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;
1ea75322
DB
333}
334
89173f95
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
00916e98
AG
353/* Add an icon grid element and establish its initial visibility. */
354static void panel_icon_grid_add(GtkContainer *container, GtkWidget *widget)
1ea75322 355{
00916e98 356 PanelIconGrid *ig = PANEL_ICON_GRID(container);
1ea75322 357
00916e98
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);
1ea75322 360
00916e98 361 /* Add the widget to the layout container. */
00916e98
AG
362 gtk_widget_set_parent(widget, GTK_WIDGET(container));
363// gtk_widget_queue_resize(GTK_WIDGET(container));
1ea75322
DB
364}
365
00916e98 366void panel_icon_grid_set_constrain_width(PanelIconGrid * ig, gboolean constrain_width)
1ea75322 367{
00916e98 368 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
1ea75322 369
00916e98
AG
370 if ((!ig->constrain_width && !constrain_width) ||
371 (ig->constrain_width && constrain_width))
372 return;
1ea75322 373
00916e98
AG
374 ig->constrain_width = !!constrain_width;
375 gtk_widget_queue_resize(GTK_WIDGET(ig));
1ea75322
DB
376}
377
89173f95
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
00916e98 389/* void panel_icon_grid_set_fill_width(PanelIconGrid * ig, gboolean fill_width)
1ea75322 390{
00916e98
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} */
1ea75322
DB
399
400/* Remove an icon grid element. */
00916e98 401static void panel_icon_grid_remove(GtkContainer *container, GtkWidget *widget)
1ea75322 402{
00916e98
AG
403 PanelIconGrid *ig = PANEL_ICON_GRID(container);
404 GList *children = ig->children;
405 GtkWidget *child;
406
407 while (children)
1ea75322 408 {
00916e98
AG
409 child = children->data;
410 if (widget == child)
1ea75322 411 {
00916e98 412 gboolean was_visible = gtk_widget_get_visible(widget);
1ea75322 413
00916e98 414 /* The child is found. Remove from child list and layout container. */
00916e98
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));
1ea75322
DB
422 break;
423 }
00916e98 424 children = children->next;
1ea75322
DB
425 }
426}
427
00916e98
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
1ea75322 436/* Reorder an icon grid element. */
00916e98 437void panel_icon_grid_reorder_child(PanelIconGrid * ig, GtkWidget * child, gint position)
1ea75322 438{
00916e98
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)
1ea75322 449 {
00916e98 450 if (old_link->data == child)
1ea75322 451 break;
00916e98
AG
452 old_link = old_link->next;
453 old_position++;
1ea75322
DB
454 }
455
00916e98
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
1ea75322 468 /* If the child was found, insert it at the new position. */
00916e98 469 ig->children = g_list_insert_before(ig->children, new_link, child);
1ea75322 470
00916e98
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);
1ea75322
DB
474}
475
476/* Change the geometry of an icon grid. */
00916e98 477void panel_icon_grid_set_geometry(PanelIconGrid * ig,
1ea75322
DB
478 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
479{
00916e98
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
1ea75322
DB
489 ig->orientation = orientation;
490 ig->child_width = child_width;
1ea75322
DB
491 ig->child_height = child_height;
492 ig->spacing = spacing;
1ea75322 493 ig->target_dimension = target_dimension;
00916e98 494 gtk_widget_queue_resize(GTK_WIDGET(ig));
1ea75322
DB
495}
496
00916e98
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)
1ea75322 502{
00916e98
AG
503 PanelIconGrid *ig = PANEL_ICON_GRID(object);
504 gint spacing;
505 GtkOrientation orientation;
506
507 switch (prop_id)
1ea75322 508 {
00916e98
AG
509 case PROP_ORIENTATION:
510 orientation = g_value_get_enum(value);
511 if (orientation != ig->orientation)
1ea75322 512 {
00916e98
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));
1ea75322 524 }
00916e98
AG
525 break;
526 case PROP_CONSTRAIN_WIDTH:
527 panel_icon_grid_set_constrain_width(ig, g_value_get_boolean(value));
528 break;
89173f95
AG
529 case PROP_ASPECT_WIDTH:
530 panel_icon_grid_set_aspect_width(ig, g_value_get_boolean(value));
531 break;
00916e98
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;
89173f95
AG
557 case PROP_ASPECT_WIDTH:
558 g_value_set_boolean(value, ig->aspect_width);
559 break;
00916e98
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
64ea8d44 581#if GTK_CHECK_VERSION(2, 20, 0)
00916e98 582 gtk_widget_set_realized(widget, TRUE);
64ea8d44
AG
583#else
584 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
585#endif
00916e98
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);
89173f95 605#if !GTK_CHECK_VERSION(3, 0, 0)
00916e98 606 attributes.colormap = gtk_widget_get_colormap(widget);
89173f95 607#endif
00916e98
AG
608 attributes.wclass = GDK_INPUT_OUTPUT;
609
89173f95
AG
610#if GTK_CHECK_VERSION(3, 0, 0)
611 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
612#else
00916e98 613 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
89173f95 614#endif
00916e98
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
89173f95
AG
673#if GTK_CHECK_VERSION(3, 0, 0)
674static gboolean panel_icon_grid_draw(GtkWidget *widget, cairo_t *cr)
675#else
00916e98 676static gboolean panel_icon_grid_expose(GtkWidget *widget, GdkEventExpose *event)
89173f95 677#endif
00916e98
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))
89173f95
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
00916e98
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);
89173f95 693#endif
00916e98 694
89173f95
AG
695#if GTK_CHECK_VERSION(3, 0, 0)
696 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->draw(widget, cr);
697#else
00916e98 698 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->expose_event(widget, event);
89173f95 699#endif
1ea75322 700 }
00916e98 701 return FALSE;
1ea75322
DB
702}
703
00916e98
AG
704static void panel_icon_grid_forall(GtkContainer *container,
705 gboolean include_internals,
706 GtkCallback callback,
707 gpointer callback_data)
1ea75322 708{
00916e98
AG
709 PanelIconGrid *ig = PANEL_ICON_GRID(container);
710 GList *children = ig->children;
711 GtkWidget *child;
1ea75322 712
00916e98 713 while (children)
1ea75322 714 {
00916e98
AG
715 child = children->data;
716 children = children->next;
717 (* callback)(child, callback_data);
1ea75322 718 }
00916e98
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
89173f95
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
00916e98 739 widget_class->size_request = panel_icon_grid_size_request;
89173f95 740#endif
00916e98
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;
89173f95
AG
746#if GTK_CHECK_VERSION(3, 0, 0)
747 widget_class->draw = panel_icon_grid_draw;
748#else
00916e98 749 widget_class->expose_event = panel_icon_grid_expose;
89173f95 750#endif
00916e98
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));
89173f95
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));
00916e98
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,
89173f95
AG
800 "spacing", spacing,
801 "border-width", border,
802 NULL);
00916e98
AG
803
804 ig->child_width = child_width;
00916e98
AG
805 ig->child_height = child_height;
806 ig->target_dimension = target_dimension;
00916e98
AG
807
808 return (GtkWidget *)ig;
1ea75322 809}