Adding upstream version 0.9.0.
[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>
7a1c5048 4 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
0688b017
AG
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
7a1c5048
AG
39/* Child properties */
40enum {
41 CHILD_PROP_0,
42 CHILD_PROP_POSITION
43};
44
6b775dbb
AG
45/* Representative of an icon grid. This is a manager that packs widgets into a rectangular grid whose size adapts to conditions. */
46struct _PanelIconGrid
47{
48 GtkContainer container; /* Parent widget */
49 GList * children; /* List of icon grid elements */
50 GtkOrientation orientation; /* Desired orientation */
51 gint child_width; /* Desired child width */
52 gint child_height; /* Desired child height */
7a1c5048 53 guint spacing; /* Desired spacing between grid elements */
6b775dbb
AG
54 gint target_dimension; /* Desired dimension perpendicular to orientation */
55 gboolean constrain_width : 1; /* True if width should be constrained by allocated space */
f7ecd6ce 56 gboolean aspect_width : 1; /* True if children should maintain aspect */
6b775dbb
AG
57 gboolean fill_width : 1; /* True if children should fill unused width */
58 int rows; /* Computed layout rows */
59 int columns; /* Computed layout columns */
6b775dbb 60 GdkWindow *event_window; /* Event window if NO_WINDOW is set */
7a1c5048
AG
61 GtkWidget *dest_item; /* Drag destination to draw focus */
62 PanelIconGridDropPosition dest_pos; /* Position to draw focus */
6b775dbb
AG
63};
64
65struct _PanelIconGridClass
66{
67 GtkContainerClass parent_class;
68};
69
f7ecd6ce
AG
70static void icon_grid_element_check_requisition(PanelIconGrid *ig,
71 GtkRequisition *requisition)
72{
73 if (ig->aspect_width && !ig->constrain_width &&
74 requisition->width > 1 && requisition->height > 1)
75 {
76 /* calculate width from aspect */
77 gdouble ratio = (gdouble)requisition->width / requisition->height;
7a1c5048 78 requisition->width = MAX(ig->child_height * ratio, ig->child_width);
f7ecd6ce
AG
79 }
80 else
81 {
82 requisition->width = ig->child_width;
83 }
84 requisition->height = ig->child_height;
85}
2ba86315 86
7a1c5048
AG
87static void panel_icon_grid_calculate_size(PanelIconGrid *ig, GtkRequisition *requisition);
88
2ba86315 89/* Establish the widget placement of an icon grid. */
6b775dbb
AG
90static void panel_icon_grid_size_allocate(GtkWidget *widget,
91 GtkAllocation *allocation)
2ba86315 92{
6b775dbb
AG
93 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
94 GtkRequisition req;
95 GtkAllocation child_allocation;
96 int child_width;
97 int child_height;
98 GtkTextDirection direction;
99 guint border;
7a1c5048 100 guint x_border, y_border;
6b775dbb 101 int x_delta;
f7ecd6ce
AG
102 guint next_coord;
103 guint x, y;
7a1c5048 104 gboolean need_recalc = FALSE;
6b775dbb
AG
105 GList *ige;
106 GtkWidget *child;
107
108 /* Apply given allocation */
109 gtk_widget_set_allocation(widget, allocation);
110 border = gtk_container_get_border_width(GTK_CONTAINER(widget));
7a1c5048
AG
111 x_border = y_border = border;
112 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
113 x_border = MAX(border, ig->spacing / 2);
114 else
115 y_border = MAX(border, ig->spacing / 2);
116 child_allocation.width = MAX(allocation->width - 2 * border, 0);
117 child_allocation.height = MAX(allocation->height - 2 * border, 0);
6b775dbb
AG
118 if (gtk_widget_get_realized(widget))
119 {
120 if (!gtk_widget_get_has_window(widget))
121 {
122 child_allocation.x = allocation->x + border;
123 child_allocation.y = allocation->y + border;
124 }
125 else
126 {
127 child_allocation.x = 0;
128 child_allocation.y = 0;
129 }
6b775dbb
AG
130 if (ig->event_window != NULL)
131 gdk_window_move_resize(ig->event_window,
132 child_allocation.x,
133 child_allocation.y,
134 child_allocation.width,
135 child_allocation.height);
136 if (gtk_widget_get_has_window(widget))
137 gdk_window_move_resize(gtk_widget_get_window(widget),
138 allocation->x + border,
139 allocation->y + border,
140 child_allocation.width,
141 child_allocation.height);
142 }
2ba86315
DB
143
144 /* Get and save the desired container geometry. */
7a1c5048
AG
145 child_width = ig->child_width;
146 child_height = ig->child_height;
6b775dbb 147 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL && allocation->height > 1)
7a1c5048
AG
148 {
149 if (ig->target_dimension != allocation->height)
150 need_recalc = TRUE;
6b775dbb 151 ig->target_dimension = allocation->height;
7a1c5048
AG
152 /* Don't allow children go out of the grid */
153 if ((child_height + (int)border * 2) > allocation->height)
154 child_height = MAX(1, allocation->height - 2 * border);
155 }
6b775dbb 156 else if (ig->orientation == GTK_ORIENTATION_VERTICAL && allocation->width > 1)
7a1c5048
AG
157 {
158 if (ig->target_dimension != allocation->width)
159 need_recalc = TRUE;
6b775dbb 160 ig->target_dimension = allocation->width;
7a1c5048
AG
161 /* Don't allow children go out of the grid */
162 if ((child_width + (int)border * 2) > allocation->width)
163 child_width = MAX(1, allocation->width - 2 * border);
164 }
6b775dbb 165
f7ecd6ce
AG
166 /* FIXME: is there any sense to recheck rows and columns again?
167 GTK+ should have it done right before this call. */
7a1c5048
AG
168 if (need_recalc)
169 panel_icon_grid_calculate_size(ig, &req);
2ba86315
DB
170
171 /* Get the constrained child geometry if the allocated geometry is insufficient.
172 * All children are still the same size and share equally in the deficit. */
f7ecd6ce 173 if ((ig->columns != 0) && (ig->rows != 0) && (child_allocation.width > 0))
2ba86315 174 {
f7ecd6ce
AG
175 if (ig->constrain_width &&
176 (x_delta = (child_allocation.width + ig->spacing) / ig->columns - ig->spacing) < child_width)
177 child_width = MAX(2, x_delta);
178 /* fill vertical space evenly in horisontal orientation */
179 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL &&
180 (x_delta = (child_allocation.height + ig->spacing) / ig->rows - ig->spacing) > child_height)
181 child_height = MAX(2, x_delta);
2ba86315
DB
182 }
183
184 /* Initialize parameters to control repositioning each visible child. */
6b775dbb 185 direction = gtk_widget_get_direction(widget);
7a1c5048
AG
186 x = (direction == GTK_TEXT_DIR_RTL) ? allocation->width - x_border : x_border;
187 y = y_border;
f7ecd6ce
AG
188 x_delta = 0;
189 next_coord = border;
2ba86315
DB
190
191 /* Reposition each visible child. */
6b775dbb 192 for (ige = ig->children; ige != NULL; ige = ige->next)
2ba86315 193 {
6b775dbb
AG
194 child = ige->data;
195 if (gtk_widget_get_visible(child))
2ba86315
DB
196 {
197 /* Do necessary operations on the child. */
f7ecd6ce
AG
198 gtk_widget_get_child_requisition(child, &req);
199 icon_grid_element_check_requisition(ig, &req);
200 child_allocation.width = MIN(req.width, child_width);
6b775dbb 201 child_allocation.height = MIN(req.height, child_height);
2ba86315 202
f7ecd6ce 203 /* Check this grid position */
2ba86315
DB
204 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
205 {
f7ecd6ce 206 y = next_coord;
7a1c5048 207 if (y + child_height > allocation->height - y_border && y > y_border)
2ba86315 208 {
7a1c5048 209 y = y_border;
f7ecd6ce
AG
210 if (direction == GTK_TEXT_DIR_RTL)
211 x -= (x_delta + ig->spacing);
212 else
213 x += (x_delta + ig->spacing);
214 x_delta = 0;
6b775dbb 215 // FIXME: if fill_width and rows = 1 then allocate whole column
2ba86315 216 }
f7ecd6ce
AG
217 next_coord = y + child_height + ig->spacing;
218 x_delta = MAX(x_delta, child_allocation.width);
2ba86315
DB
219 }
220 else
221 {
6b775dbb 222 // FIXME: if fill_width then use aspect to check delta
7a1c5048 223 x = next_coord;
f7ecd6ce 224 if (direction == GTK_TEXT_DIR_RTL)
2ba86315 225 {
7a1c5048 226 if (x < allocation->width - x_border && x - child_allocation.width < x_border)
f7ecd6ce 227 {
7a1c5048
AG
228 x = allocation->width - x_border;
229 y += child_height + ig->spacing;
f7ecd6ce 230 }
7a1c5048 231 next_coord = x - child_allocation.width - ig->spacing;
2ba86315 232 }
f7ecd6ce
AG
233 else
234 {
7a1c5048 235 if (x + child_allocation.width > allocation->width - x_border && x > x_border)
f7ecd6ce 236 {
7a1c5048 237 x = x_border;
f7ecd6ce
AG
238 y += child_height + ig->spacing;
239 }
240 next_coord = x + child_allocation.width + ig->spacing;
241 }
242 }
7a1c5048
AG
243 if (direction == GTK_TEXT_DIR_RTL)
244 child_allocation.x = x - child_allocation.width;
245 else
246 child_allocation.x = x;
f7ecd6ce
AG
247 if (req.height < child_height - 1)
248 y += (child_height - req.height) / 2;
249 child_allocation.y = y;
250
251 if (!gtk_widget_get_has_window (widget))
252 {
253 child_allocation.x += allocation->x;
254 child_allocation.y += allocation->y;
2ba86315 255 }
f7ecd6ce
AG
256 // FIXME: if fill_width and rows > 1 then delay allocation
257 gtk_widget_size_allocate(child, &child_allocation);
2ba86315
DB
258 }
259 }
2ba86315
DB
260}
261
262/* Establish the geometry of an icon grid. */
7a1c5048
AG
263static void panel_icon_grid_calculate_size(PanelIconGrid *ig,
264 GtkRequisition *requisition)
2ba86315 265{
6b775dbb 266 GList *ige;
7a1c5048
AG
267 int target_dimension = MAX(ig->target_dimension, 0);
268 guint border = gtk_container_get_border_width(GTK_CONTAINER(ig));
269 guint target_borders = MAX(2 * border, ig->spacing);
f7ecd6ce
AG
270 gint row = 0, w = 0;
271 GtkRequisition child_requisition;
6b775dbb 272
f7ecd6ce
AG
273 requisition->width = 0;
274 requisition->height = 0;
275 ig->rows = 0;
276 ig->columns = 0;
6b775dbb 277 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
2ba86315
DB
278 {
279 /* In horizontal orientation, fit as many rows into the available height as possible.
280 * Then allocate as many columns as necessary. Guard against zerodivides. */
2ba86315 281 if ((ig->child_height + ig->spacing) != 0)
7a1c5048 282 ig->rows = (target_dimension + ig->spacing + border * 2) / (ig->child_height + ig->spacing);
2ba86315
DB
283 if (ig->rows == 0)
284 ig->rows = 1;
f7ecd6ce
AG
285 /* Count visible children and columns. */
286 for (ige = ig->children; ige != NULL; ige = ige->next)
287 if (gtk_widget_get_visible(ige->data))
288 {
289 gtk_widget_size_request(ige->data, &child_requisition);
290 icon_grid_element_check_requisition(ig, &child_requisition);
291 if (row == 0)
292 ig->columns++;
293 w = MAX(w, child_requisition.width);
294 row++;
295 if (row == ig->rows)
296 {
f7ecd6ce 297 if (requisition->width > 0)
7a1c5048 298 requisition->width += ig->spacing;
f7ecd6ce
AG
299 requisition->width += w;
300 row = w = 0;
301 }
302 }
7a1c5048
AG
303 if (w > 0)
304 {
305 if (requisition->width > 0)
306 requisition->width += ig->spacing;
f7ecd6ce 307 requisition->width += w;
7a1c5048
AG
308 }
309 if (requisition->width > 0)
310 requisition->width += target_borders;
6b775dbb
AG
311 /* if ((ig->columns == 1) && (ig->rows > visible_children))
312 ig->rows = visible_children; */
7a1c5048
AG
313 if (ig->columns > 0)
314 requisition->height = (ig->child_height + ig->spacing) * ig->rows - ig->spacing + 2 * border;
2ba86315
DB
315 }
316 else
317 {
318 /* In vertical orientation, fit as many columns into the available width as possible.
319 * Then allocate as many rows as necessary. Guard against zerodivides. */
2ba86315 320 if ((ig->child_width + ig->spacing) != 0)
7a1c5048 321 ig->columns = (target_dimension + ig->spacing + border * 2) / (ig->child_width + ig->spacing);
2ba86315
DB
322 if (ig->columns == 0)
323 ig->columns = 1;
f7ecd6ce
AG
324 /* Count visible children and rows. */
325 for (ige = ig->children; ige != NULL; ige = ige->next)
326 if (gtk_widget_get_visible(ige->data))
327 {
328 gtk_widget_size_request(ige->data, &child_requisition);
329 icon_grid_element_check_requisition(ig, &child_requisition);
8713e384 330 if (w > 0)
7a1c5048 331 {
8713e384 332 w += ig->spacing;
7a1c5048
AG
333 if (w + child_requisition.width + (int)border > target_dimension)
334 {
335 w = 0;
336 ig->rows++;
337 }
338 }
f7ecd6ce
AG
339 w += child_requisition.width;
340 requisition->width = MAX(requisition->width, w);
341 }
342 if (w > 0)
343 ig->rows++;
7a1c5048
AG
344 if (requisition->width > 0)
345 requisition->width += 2 * border;
346 if (ig->rows > 0)
347 requisition->height = (ig->child_height + ig->spacing) * ig->rows - ig->spacing + target_borders;
2ba86315 348 }
7a1c5048 349}
2ba86315 350
7a1c5048
AG
351static void panel_icon_grid_size_request(GtkWidget *widget,
352 GtkRequisition *requisition)
353{
354 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
355 gint old_rows = ig->rows;
356 gint old_columns = ig->columns;
357
358 panel_icon_grid_calculate_size(ig, requisition);
f7ecd6ce 359
7a1c5048 360 /* Apply the requisition. */
6b775dbb
AG
361 if (ig->rows != old_rows || ig->columns != old_columns)
362 gtk_widget_queue_resize(widget);
2ba86315
DB
363}
364
f7ecd6ce
AG
365#if GTK_CHECK_VERSION(3, 0, 0)
366static void panel_icon_grid_get_preferred_width(GtkWidget *widget,
367 gint *minimal_width,
368 gint *natural_width)
2ba86315 369{
f7ecd6ce
AG
370 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
371 GtkRequisition requisition;
372
373 if (ig->orientation == GTK_ORIENTATION_VERTICAL)
374 {
375 if (minimal_width)
376 *minimal_width = MIN(ig->target_dimension, ig->child_width);
377 if (natural_width)
378 *natural_width = ig->target_dimension;
379 return;
380 }
381 panel_icon_grid_size_request(widget, &requisition);
382 if (minimal_width)
383 *minimal_width = requisition.width;
384 if (natural_width)
385 *natural_width = requisition.width;
2ba86315
DB
386}
387
f7ecd6ce
AG
388static void panel_icon_grid_get_preferred_height(GtkWidget *widget,
389 gint *minimal_height,
390 gint *natural_height)
391{
392 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
393 GtkRequisition requisition;
394
395 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
396 requisition.height = ig->target_dimension;
397 else
398 panel_icon_grid_size_request(widget, &requisition);
399 if (minimal_height)
400 *minimal_height = requisition.height;
401 if (natural_height)
402 *natural_height = requisition.height;
403}
404#endif
405
6b775dbb
AG
406/* Add an icon grid element and establish its initial visibility. */
407static void panel_icon_grid_add(GtkContainer *container, GtkWidget *widget)
2ba86315 408{
6b775dbb 409 PanelIconGrid *ig = PANEL_ICON_GRID(container);
2ba86315 410
6b775dbb
AG
411 /* Insert at the tail of the child list. This keeps the graphics in the order they were added. */
412 ig->children = g_list_append(ig->children, widget);
2ba86315 413
6b775dbb 414 /* Add the widget to the layout container. */
6b775dbb
AG
415 gtk_widget_set_parent(widget, GTK_WIDGET(container));
416// gtk_widget_queue_resize(GTK_WIDGET(container));
2ba86315
DB
417}
418
6b775dbb 419void panel_icon_grid_set_constrain_width(PanelIconGrid * ig, gboolean constrain_width)
2ba86315 420{
6b775dbb 421 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
2ba86315 422
6b775dbb
AG
423 if ((!ig->constrain_width && !constrain_width) ||
424 (ig->constrain_width && constrain_width))
425 return;
2ba86315 426
6b775dbb
AG
427 ig->constrain_width = !!constrain_width;
428 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315
DB
429}
430
f7ecd6ce
AG
431void panel_icon_grid_set_aspect_width(PanelIconGrid * ig, gboolean aspect_width)
432{
433 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
434
435 if ((!ig->aspect_width && !aspect_width) || (ig->aspect_width && aspect_width))
436 return;
437
438 ig->aspect_width = !!aspect_width;
439 gtk_widget_queue_resize(GTK_WIDGET(ig));
440}
441
6b775dbb 442/* void panel_icon_grid_set_fill_width(PanelIconGrid * ig, gboolean fill_width)
2ba86315 443{
6b775dbb
AG
444 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
445
446 if ((!ig->fill_width && !fill_width) || (ig->fill_width && fill_width))
447 return;
448
449 ig->fill_width = !!fill_width;
450 gtk_widget_queue_resize(GTK_WIDGET(ig));
451} */
2ba86315
DB
452
453/* Remove an icon grid element. */
6b775dbb 454static void panel_icon_grid_remove(GtkContainer *container, GtkWidget *widget)
2ba86315 455{
6b775dbb
AG
456 PanelIconGrid *ig = PANEL_ICON_GRID(container);
457 GList *children = ig->children;
458 GtkWidget *child;
459
460 while (children)
2ba86315 461 {
6b775dbb
AG
462 child = children->data;
463 if (widget == child)
2ba86315 464 {
6b775dbb 465 gboolean was_visible = gtk_widget_get_visible(widget);
2ba86315 466
6b775dbb 467 /* The child is found. Remove from child list and layout container. */
6b775dbb
AG
468 gtk_widget_unparent (widget);
469 ig->children = g_list_remove_link(ig->children, children);
470 g_list_free(children);
471
472 /* Do a relayout if needed. */
473 if (was_visible)
474 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315
DB
475 break;
476 }
6b775dbb 477 children = children->next;
2ba86315
DB
478 }
479}
480
7a1c5048
AG
481/* Get the index of an icon grid element. Actually it's
482 the same as gtk_container_child_get(ig, child, "position", &pos, NULL)
483 but more convenient to use. */
6b775dbb
AG
484gint panel_icon_grid_get_child_position(PanelIconGrid * ig, GtkWidget * child)
485{
486 g_return_val_if_fail(PANEL_IS_ICON_GRID(ig), -1);
487
488 return g_list_index(ig->children, child);
489}
490
7a1c5048
AG
491/* Reorder an icon grid element.
492 Equivalent to gtk_container_child_set(ig, child, "position", pos, NULL) */
6b775dbb 493void panel_icon_grid_reorder_child(PanelIconGrid * ig, GtkWidget * child, gint position)
2ba86315 494{
6b775dbb
AG
495 GList *old_link;
496 GList *new_link;
497 gint old_position;
498
499 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
500 g_return_if_fail(GTK_IS_WIDGET(child));
501
502 old_link = ig->children;
503 old_position = 0;
504 while (old_link)
2ba86315 505 {
6b775dbb 506 if (old_link->data == child)
2ba86315 507 break;
6b775dbb
AG
508 old_link = old_link->next;
509 old_position++;
2ba86315
DB
510 }
511
6b775dbb
AG
512 g_return_if_fail(old_link != NULL);
513
514 if (position == old_position)
515 return;
516
517 /* Remove the child from its current position. */
518 ig->children = g_list_delete_link(ig->children, old_link);
519 if (position < 0)
520 new_link = NULL;
521 else
522 new_link = g_list_nth(ig->children, position);
523
2ba86315 524 /* If the child was found, insert it at the new position. */
6b775dbb 525 ig->children = g_list_insert_before(ig->children, new_link, child);
2ba86315 526
6b775dbb
AG
527 /* Do a relayout. */
528 if (gtk_widget_get_visible(child) && gtk_widget_get_visible(GTK_WIDGET(ig)))
529 gtk_widget_queue_resize(child);
2ba86315
DB
530}
531
7a1c5048
AG
532guint panel_icon_grid_get_n_children(PanelIconGrid * ig)
533{
534 g_return_val_if_fail(PANEL_IS_ICON_GRID(ig), 0);
535
536 return g_list_length(ig->children);
537}
538
2ba86315 539/* Change the geometry of an icon grid. */
6b775dbb 540void panel_icon_grid_set_geometry(PanelIconGrid * ig,
2ba86315
DB
541 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
542{
6b775dbb
AG
543 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
544
545 gtk_container_set_border_width(GTK_CONTAINER(ig), border);
546
547 if (ig->orientation == orientation && ig->child_width == child_width &&
7a1c5048 548 ig->child_height == child_height && (gint)ig->spacing == spacing &&
6b775dbb
AG
549 ig->target_dimension == target_dimension)
550 return;
551
2ba86315
DB
552 ig->orientation = orientation;
553 ig->child_width = child_width;
2ba86315 554 ig->child_height = child_height;
7a1c5048
AG
555 ig->spacing = MAX(spacing, 1);
556 ig->target_dimension = MAX(target_dimension, 0);
6b775dbb 557 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315
DB
558}
559
7a1c5048
AG
560/* get position for coordinates, return FALSE if it's outside of icon grid */
561gboolean panel_icon_grid_get_dest_at_pos(PanelIconGrid * ig, gint x, gint y,
562 GtkWidget ** child, PanelIconGridDropPosition * pos)
563{
564 GtkAllocation allocation;
565 PanelIconGridDropPosition drop_pos;
566 GtkWidget *widget;
567 GList *ige;
568 gboolean rtl, upper = TRUE;
569
570 g_return_val_if_fail(PANEL_IS_ICON_GRID(ig), FALSE);
571
572 widget = GTK_WIDGET(ig);
573 if (!gtk_widget_get_realized(widget))
574 return FALSE;
575 if (!gtk_widget_get_has_window(widget))
576 return FALSE;
577
578 rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
579 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
580 {
581 for (ige = ig->children; ige != NULL; ige = ige->next)
582 {
583 gtk_widget_get_allocation(ige->data, &allocation);
584 if (x < allocation.x)
585 {
586 if (!rtl)
587 {
588 /* reached next column */
589 drop_pos = PANEL_ICON_GRID_DROP_LEFT_BEFORE;
590 break;
591 }
592 }
593 else if (x < (allocation.x + allocation.width))
594 {
595 /* within this column */
596 if (y < allocation.y)
597 {
598 /* reached next row */
599 if (upper)
600 drop_pos = rtl ? PANEL_ICON_GRID_DROP_RIGHT_BEFORE : PANEL_ICON_GRID_DROP_LEFT_BEFORE;
601 else
602 drop_pos = PANEL_ICON_GRID_DROP_ABOVE;
603 break;
604 }
605 else if (y < (allocation.y + allocation.height))
606 {
607 /* within this row */
608 drop_pos = PANEL_ICON_GRID_DROP_INTO;
609 break;
610 }
611 upper = FALSE;
612 }
613 else if (rtl)
614 {
615 /* reached next column */
616 drop_pos = PANEL_ICON_GRID_DROP_RIGHT_BEFORE;
617 break;
618 }
619 }
620 }
621 else
622 {
623 for (ige = ig->children; ige != NULL; ige = ige->next)
624 {
625 gtk_widget_get_allocation(ige->data, &allocation);
626 if (y < allocation.y)
627 {
628 /* reached next row */
629 drop_pos = PANEL_ICON_GRID_DROP_ABOVE;
630 break;
631 }
632 else if (y < (allocation.y + allocation.height))
633 {
634 /* within this row */
635 if (x < allocation.x)
636 {
637 if (!rtl)
638 {
639 /* reached next column */
640 if (upper)
641 drop_pos = PANEL_ICON_GRID_DROP_ABOVE;
642 else
643 drop_pos = PANEL_ICON_GRID_DROP_LEFT_BEFORE;
644 break;
645 }
646 }
647 else if (x < (allocation.x + allocation.width))
648 {
649 /* within this column */
650 drop_pos = PANEL_ICON_GRID_DROP_INTO;
651 break;
652 }
653 else if (rtl)
654 {
655 /* reached next column */
656 if (upper)
657 drop_pos = PANEL_ICON_GRID_DROP_ABOVE;
658 else
659 drop_pos = PANEL_ICON_GRID_DROP_RIGHT_BEFORE;
660 break;
661 }
662 upper = FALSE;
663 }
664 }
665 }
666 if (ige == NULL)
667 {
668 /* not within allocated space */
669 ige = g_list_last(ig->children);
670 if (ig->orientation != GTK_ORIENTATION_HORIZONTAL)
671 drop_pos = PANEL_ICON_GRID_DROP_BELOW;
672 else if (rtl)
673 drop_pos = PANEL_ICON_GRID_DROP_LEFT_AFTER;
674 else
675 drop_pos = PANEL_ICON_GRID_DROP_RIGHT_AFTER;
676 }
677 if (child)
678 *child = (ige == NULL) ? NULL : ige->data;
679 if (pos)
680 *pos = drop_pos;
681 return TRUE;
682}
683
684static void panel_icon_grid_queue_draw_child(PanelIconGrid * ig, GtkWidget * child)
685{
686 GtkWidget *widget = GTK_WIDGET(ig);
687 GtkAllocation allocation;
688 GdkRectangle rect;
689
690 if (!gtk_widget_get_realized(widget))
691 return;
692 if (!gtk_widget_get_has_window(widget))
693 return;
694
695 gtk_widget_get_allocation(child, &allocation);
696
697 switch (ig->dest_pos)
698 {
699 case PANEL_ICON_GRID_DROP_LEFT_AFTER:
700 case PANEL_ICON_GRID_DROP_LEFT_BEFORE:
701 rect.x = allocation.x - 2;
702 rect.width = 2;
703 rect.y = allocation.y;
704 rect.height = allocation.height;
705 break;
706 case PANEL_ICON_GRID_DROP_RIGHT_AFTER:
707 case PANEL_ICON_GRID_DROP_RIGHT_BEFORE:
708 rect.x = allocation.x + allocation.width;
709 rect.width = 2;
710 rect.y = allocation.y;
711 rect.height = allocation.height;
712 break;
713 case PANEL_ICON_GRID_DROP_BELOW:
714 rect.x = allocation.x;
715 rect.width = allocation.width;
716 rect.y = allocation.y + allocation.height;
717 rect.height = 2;
718 break;
719 case PANEL_ICON_GRID_DROP_ABOVE:
720 rect.x = allocation.x;
721 rect.width = allocation.width;
722 rect.y = allocation.y - 2;
723 rect.height = 2;
724 break;
725 case PANEL_ICON_GRID_DROP_INTO:
726 default:
727 rect.x = allocation.x - 1;
728 rect.width = allocation.width + 2;
729 rect.y = allocation.y - 1;
730 rect.height = allocation.height + 2;
731 }
732
733 if (rect.width > 0 && rect.height > 0)
734 gdk_window_invalidate_rect(gtk_widget_get_window(widget), &rect, TRUE);
735}
736
737/* sets data and renders widget appropriately, need be drawable and realized */
738void panel_icon_grid_set_drag_dest(PanelIconGrid * ig, GtkWidget * child,
739 PanelIconGridDropPosition pos)
740{
741 GtkWidget *widget;
742 GtkWidget *current_dest;
743
744 g_return_if_fail(PANEL_IS_ICON_GRID(ig));
745
746 widget = GTK_WIDGET(ig);
747
748 if (!gtk_widget_get_realized(widget))
749 return;
750 if (!gtk_widget_get_has_window(widget))
751 return;
752
753 // reset previous state
754 current_dest = ig->dest_item;
755 if (current_dest)
756 {
757 ig->dest_item = NULL;
758 panel_icon_grid_queue_draw_child(ig, current_dest);
759 }
760
761 // need a special support for empty grid?
762 ig->dest_pos = pos;
763
764 // remember new state
765 if (child && g_list_find(ig->children, child))
766 {
767 ig->dest_item = child;
768 panel_icon_grid_queue_draw_child(ig, child);
769 }
770}
771
772PanelIconGridDropPosition panel_icon_grid_get_drag_dest(PanelIconGrid * ig,
773 GtkWidget ** child)
774{
775 g_return_val_if_fail(PANEL_IS_ICON_GRID(ig), 0);
776
777 if (child)
778 *child = ig->dest_item;
779 return ig->dest_pos;
780}
781
782
6b775dbb
AG
783G_DEFINE_TYPE_WITH_CODE(PanelIconGrid, panel_icon_grid, GTK_TYPE_CONTAINER,
784 G_IMPLEMENT_INTERFACE(GTK_TYPE_ORIENTABLE, NULL));
785
786static void panel_icon_grid_set_property(GObject *object, guint prop_id,
787 const GValue *value, GParamSpec *pspec)
2ba86315 788{
6b775dbb 789 PanelIconGrid *ig = PANEL_ICON_GRID(object);
7a1c5048 790 guint spacing;
6b775dbb
AG
791 GtkOrientation orientation;
792
793 switch (prop_id)
2ba86315 794 {
6b775dbb
AG
795 case PROP_ORIENTATION:
796 orientation = g_value_get_enum(value);
797 if (orientation != ig->orientation)
2ba86315 798 {
6b775dbb
AG
799 ig->orientation = orientation;
800 gtk_widget_queue_resize(GTK_WIDGET(ig));
801 }
802 break;
803 case PROP_SPACING:
7a1c5048 804 spacing = g_value_get_uint(value);
6b775dbb
AG
805 if (spacing != ig->spacing)
806 {
807 ig->spacing = spacing;
808 g_object_notify(object, "spacing");
809 gtk_widget_queue_resize(GTK_WIDGET(ig));
2ba86315 810 }
6b775dbb
AG
811 break;
812 case PROP_CONSTRAIN_WIDTH:
813 panel_icon_grid_set_constrain_width(ig, g_value_get_boolean(value));
814 break;
f7ecd6ce
AG
815 case PROP_ASPECT_WIDTH:
816 panel_icon_grid_set_aspect_width(ig, g_value_get_boolean(value));
817 break;
6b775dbb
AG
818 /* case PROP_FILL_WIDTH:
819 panel_icon_grid_set_fill_width(ig, g_value_get_boolean(value));
820 break; */
821 default:
822 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
823 break;
824 }
825}
826
827static void panel_icon_grid_get_property(GObject *object, guint prop_id,
828 GValue *value, GParamSpec *pspec)
829{
830 PanelIconGrid *ig = PANEL_ICON_GRID(object);
831
832 switch (prop_id)
833 {
834 case PROP_ORIENTATION:
835 g_value_set_enum(value, ig->orientation);
836 break;
837 case PROP_SPACING:
7a1c5048 838 g_value_set_uint(value, ig->spacing);
6b775dbb
AG
839 break;
840 case PROP_CONSTRAIN_WIDTH:
841 g_value_set_boolean(value, ig->constrain_width);
842 break;
f7ecd6ce
AG
843 case PROP_ASPECT_WIDTH:
844 g_value_set_boolean(value, ig->aspect_width);
845 break;
6b775dbb
AG
846 /* case PROP_FILL_WIDTH:
847 g_value_set_boolean(value, ig->fill_width);
848 break; */
849 default:
850 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
851 break;
852 }
853}
854
855/* realize()...expose() are taken from GtkEventBox implementation */
856static void panel_icon_grid_realize(GtkWidget *widget)
857{
858 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
859 GdkWindow *window;
860 GtkStyle *style;
861 GtkAllocation allocation;
862 GdkWindowAttr attributes;
863 guint border = gtk_container_get_border_width(GTK_CONTAINER(widget));
864 gint attributes_mask;
865 gboolean visible_window;
866
19ab5cea 867#if GTK_CHECK_VERSION(2, 20, 0)
6b775dbb 868 gtk_widget_set_realized(widget, TRUE);
19ab5cea
AG
869#else
870 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
871#endif
6b775dbb
AG
872
873 gtk_widget_get_allocation(widget, &allocation);
874 attributes.x = allocation.x + border;
875 attributes.y = allocation.y + border;
7a1c5048
AG
876 attributes.width = allocation.width - 2 * border;
877 attributes.height = allocation.height - 2 * border;
6b775dbb
AG
878 attributes.window_type = GDK_WINDOW_CHILD;
879 attributes.event_mask = gtk_widget_get_events(widget)
880 | GDK_BUTTON_MOTION_MASK
881 | GDK_BUTTON_PRESS_MASK
882 | GDK_BUTTON_RELEASE_MASK
883 | GDK_EXPOSURE_MASK
884 | GDK_ENTER_NOTIFY_MASK
885 | GDK_LEAVE_NOTIFY_MASK;
886
887 visible_window = gtk_widget_get_has_window(widget);
888 if (visible_window)
889 {
890 attributes.visual = gtk_widget_get_visual(widget);
f7ecd6ce 891#if !GTK_CHECK_VERSION(3, 0, 0)
6b775dbb 892 attributes.colormap = gtk_widget_get_colormap(widget);
f7ecd6ce 893#endif
6b775dbb
AG
894 attributes.wclass = GDK_INPUT_OUTPUT;
895
f7ecd6ce
AG
896#if GTK_CHECK_VERSION(3, 0, 0)
897 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
898#else
6b775dbb 899 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
f7ecd6ce 900#endif
6b775dbb
AG
901
902 window = gdk_window_new(gtk_widget_get_parent_window(widget),
903 &attributes, attributes_mask);
904 gtk_widget_set_window(widget, window);
905 gdk_window_set_user_data(window, widget);
906 }
907 else
908 {
909 window = gtk_widget_get_parent_window(widget);
910 gtk_widget_set_window(widget, window);
911 g_object_ref(window);
912
913 attributes.wclass = GDK_INPUT_ONLY;
914 attributes_mask = GDK_WA_X | GDK_WA_Y;
915
916 ig->event_window = gdk_window_new(window, &attributes, attributes_mask);
917 gdk_window_set_user_data(ig->event_window, widget);
918 }
919
920 style = gtk_style_attach(gtk_widget_get_style(widget), window);
921 gtk_widget_set_style(widget, style);
922
923 if (visible_window)
924 gtk_style_set_background(style, window, GTK_STATE_NORMAL);
925}
926
927static void panel_icon_grid_unrealize(GtkWidget *widget)
928{
929 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
930
931 if (ig->event_window != NULL)
932 {
933 gdk_window_set_user_data(ig->event_window, NULL);
934 gdk_window_destroy(ig->event_window);
935 ig->event_window = NULL;
936 }
937
938 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->unrealize(widget);
939}
940
941static void panel_icon_grid_map(GtkWidget *widget)
942{
943 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
944
945 if (ig->event_window != NULL)
946 gdk_window_show(ig->event_window);
947 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->map(widget);
948}
949
950static void panel_icon_grid_unmap(GtkWidget *widget)
951{
952 PanelIconGrid *ig = PANEL_ICON_GRID(widget);
953
954 if (ig->event_window != NULL)
955 gdk_window_hide(ig->event_window);
956 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->unmap(widget);
957}
958
f7ecd6ce
AG
959#if GTK_CHECK_VERSION(3, 0, 0)
960static gboolean panel_icon_grid_draw(GtkWidget *widget, cairo_t *cr)
961#else
6b775dbb 962static gboolean panel_icon_grid_expose(GtkWidget *widget, GdkEventExpose *event)
f7ecd6ce 963#endif
6b775dbb
AG
964{
965 if (gtk_widget_is_drawable(widget))
966 {
7a1c5048
AG
967 PanelIconGrid *ig;
968
6b775dbb
AG
969 if (gtk_widget_get_has_window(widget) &&
970 !gtk_widget_get_app_paintable(widget))
f7ecd6ce
AG
971#if GTK_CHECK_VERSION(3, 0, 0)
972 gtk_render_background(gtk_widget_get_style_context(widget), cr, 0, 0,
973 gtk_widget_get_allocated_width(widget),
974 gtk_widget_get_allocated_height(widget));
975#else
6b775dbb
AG
976 gtk_paint_flat_box(gtk_widget_get_style(widget),
977 gtk_widget_get_window(widget),
978 gtk_widget_get_state(widget), GTK_SHADOW_NONE,
979 &event->area, widget, "panelicongrid",
980 0, 0, -1, -1);
f7ecd6ce 981#endif
6b775dbb 982
7a1c5048
AG
983 ig = PANEL_ICON_GRID(widget);
984 if (ig->dest_item && gtk_widget_get_has_window(widget))
985 {
986 GtkAllocation allocation;
987 GdkRectangle rect;
988#if GTK_CHECK_VERSION(3, 0, 0)
989 GtkStyleContext *context;
990#endif
991
992 gtk_widget_get_allocation(ig->dest_item, &allocation);
993#if GTK_CHECK_VERSION(3, 0, 0)
994 cairo_save(cr);
995 //gtk_cairo_transform_to_window(cr, widget, gtk_widget_get_window(widget));
996#endif
997 switch(ig->dest_pos)
998 {
999 case PANEL_ICON_GRID_DROP_LEFT_AFTER:
1000 case PANEL_ICON_GRID_DROP_LEFT_BEFORE:
1001 rect.x = allocation.x - 2;
1002 rect.width = 2;
1003 rect.y = allocation.y;
1004 rect.height = allocation.height;
1005 break;
1006 case PANEL_ICON_GRID_DROP_RIGHT_AFTER:
1007 case PANEL_ICON_GRID_DROP_RIGHT_BEFORE:
1008 rect.x = allocation.x + allocation.width;
1009 rect.width = 2;
1010 rect.y = allocation.y;
1011 rect.height = allocation.height;
1012 break;
1013 case PANEL_ICON_GRID_DROP_BELOW:
1014 rect.x = allocation.x;
1015 rect.width = allocation.width;
1016 rect.y = allocation.y + allocation.height;
1017 rect.height = 2;
1018 break;
1019 case PANEL_ICON_GRID_DROP_ABOVE:
1020 rect.x = allocation.x;
1021 rect.width = allocation.width;
1022 rect.y = allocation.y - 2;
1023 rect.height = 2;
1024 break;
1025 case PANEL_ICON_GRID_DROP_INTO:
1026 default:
1027 rect.x = allocation.x - 1;
1028 rect.width = allocation.width + 2;
1029 rect.y = allocation.y - 1;
1030 rect.height = allocation.height + 2;
1031 }
1032#if GTK_CHECK_VERSION(3, 0, 0)
1033 context = gtk_widget_get_style_context(widget);
1034 gtk_style_context_set_state(context, gtk_widget_get_state_flags(widget));
1035 gtk_render_focus(context, cr, rect.x, rect.y, rect.width, rect.height);
1036 cairo_restore(cr);
1037#else
1038 gtk_paint_focus(gtk_widget_get_style(widget),
1039 gtk_widget_get_window(widget),
1040 gtk_widget_get_state(widget),
1041 NULL, widget,
1042 "panelicongrid-drop-indicator",
1043 rect.x, rect.y, rect.width, rect.height);
1044#endif
1045 }
1046
f7ecd6ce
AG
1047#if GTK_CHECK_VERSION(3, 0, 0)
1048 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->draw(widget, cr);
1049#else
6b775dbb 1050 GTK_WIDGET_CLASS(panel_icon_grid_parent_class)->expose_event(widget, event);
f7ecd6ce 1051#endif
2ba86315 1052 }
6b775dbb 1053 return FALSE;
2ba86315
DB
1054}
1055
6b775dbb
AG
1056static void panel_icon_grid_forall(GtkContainer *container,
1057 gboolean include_internals,
1058 GtkCallback callback,
1059 gpointer callback_data)
2ba86315 1060{
6b775dbb
AG
1061 PanelIconGrid *ig = PANEL_ICON_GRID(container);
1062 GList *children = ig->children;
1063 GtkWidget *child;
2ba86315 1064
6b775dbb 1065 while (children)
2ba86315 1066 {
6b775dbb
AG
1067 child = children->data;
1068 children = children->next;
1069 (* callback)(child, callback_data);
2ba86315 1070 }
6b775dbb
AG
1071}
1072
1073static GType panel_icon_grid_child_type(GtkContainer *container)
1074{
1075 return GTK_TYPE_WIDGET;
1076}
1077
7a1c5048
AG
1078static void panel_icon_grid_set_child_property(GtkContainer *container,
1079 GtkWidget *child,
1080 guint prop_id,
1081 const GValue *value,
1082 GParamSpec *pspec)
1083{
1084 PanelIconGrid *ig = PANEL_ICON_GRID(container);
1085
1086 switch (prop_id)
1087 {
1088 case CHILD_PROP_POSITION:
1089 panel_icon_grid_reorder_child(ig, child, g_value_get_int(value));
1090 break;
1091 default:
1092 G_OBJECT_WARN_INVALID_PROPERTY_ID(container, prop_id, pspec);
1093 break;
1094 }
1095}
1096
1097static void panel_icon_grid_get_child_property(GtkContainer *container,
1098 GtkWidget *child,
1099 guint prop_id,
1100 GValue *value,
1101 GParamSpec *pspec)
1102{
1103 PanelIconGrid *ig = PANEL_ICON_GRID(container);
1104
1105 switch (prop_id)
1106 {
1107 case CHILD_PROP_POSITION:
1108 g_value_set_int(value, panel_icon_grid_get_child_position(ig, child));
1109 break;
1110 default:
1111 G_OBJECT_WARN_INVALID_PROPERTY_ID(container, prop_id, pspec);
1112 break;
1113 }
1114}
1115
8713e384 1116static void panel_icon_grid_class_init(PanelIconGridClass *klass)
6b775dbb 1117{
8713e384
AG
1118 GObjectClass *object_class = G_OBJECT_CLASS(klass);
1119 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1120 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
6b775dbb
AG
1121
1122 object_class->set_property = panel_icon_grid_set_property;
1123 object_class->get_property = panel_icon_grid_get_property;
1124
f7ecd6ce
AG
1125#if GTK_CHECK_VERSION(3, 0, 0)
1126 widget_class->get_preferred_width = panel_icon_grid_get_preferred_width;
1127 widget_class->get_preferred_height = panel_icon_grid_get_preferred_height;
1128#else
6b775dbb 1129 widget_class->size_request = panel_icon_grid_size_request;
f7ecd6ce 1130#endif
6b775dbb
AG
1131 widget_class->size_allocate = panel_icon_grid_size_allocate;
1132 widget_class->realize = panel_icon_grid_realize;
1133 widget_class->unrealize = panel_icon_grid_unrealize;
1134 widget_class->map = panel_icon_grid_map;
1135 widget_class->unmap = panel_icon_grid_unmap;
f7ecd6ce
AG
1136#if GTK_CHECK_VERSION(3, 0, 0)
1137 widget_class->draw = panel_icon_grid_draw;
1138#else
6b775dbb 1139 widget_class->expose_event = panel_icon_grid_expose;
f7ecd6ce 1140#endif
6b775dbb
AG
1141
1142 container_class->add = panel_icon_grid_add;
1143 container_class->remove = panel_icon_grid_remove;
1144 container_class->forall = panel_icon_grid_forall;
1145 container_class->child_type = panel_icon_grid_child_type;
7a1c5048
AG
1146 container_class->get_child_property = panel_icon_grid_get_child_property;
1147 container_class->set_child_property = panel_icon_grid_set_child_property;
6b775dbb
AG
1148
1149 g_object_class_override_property(object_class,
1150 PROP_ORIENTATION,
1151 "orientation");
7a1c5048 1152 //FIXME: override border width to min = 1
6b775dbb
AG
1153 g_object_class_install_property(object_class,
1154 PROP_SPACING,
7a1c5048
AG
1155 g_param_spec_uint("spacing",
1156 "Spacing",
1157 "The amount of space between children",
1158 1,
1159 G_MAXINT,
1160 1,
1161 G_PARAM_READWRITE));
6b775dbb
AG
1162 g_object_class_install_property(object_class,
1163 PROP_CONSTRAIN_WIDTH,
1164 g_param_spec_boolean("constrain-width",
1165 "Constrain width",
1166 "Whether to constrain width by allocated space",
1167 FALSE, G_PARAM_READWRITE));
f7ecd6ce
AG
1168 g_object_class_install_property(object_class,
1169 PROP_ASPECT_WIDTH,
1170 g_param_spec_boolean("aspect-width",
1171 "Maintain children aspect",
1172 "Whether to set children width to maintain their aspect",
1173 FALSE, G_PARAM_READWRITE));
7a1c5048
AG
1174
1175 gtk_container_class_install_child_property(container_class,
1176 CHILD_PROP_POSITION,
1177 g_param_spec_int("position",
1178 "Position",
1179 "The index of the child in the parent",
1180 -1, G_MAXINT, 0,
1181 G_PARAM_READWRITE));
6b775dbb
AG
1182}
1183
1184static void panel_icon_grid_init(PanelIconGrid *ig)
1185{
6b775dbb
AG
1186 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(ig), FALSE);
1187
1188 ig->orientation = GTK_ORIENTATION_HORIZONTAL;
1189}
1190
1191/* Establish an icon grid in a specified container widget.
1192 * The icon grid manages the contents of the container.
1193 * The orientation, geometry of the elements, and spacing can be varied. All elements are the same size. */
1194GtkWidget * panel_icon_grid_new(
1195 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
1196{
1197 /* Create a structure representing the icon grid and collect the parameters. */
1198 PanelIconGrid * ig = g_object_new(PANEL_TYPE_ICON_GRID,
1199 "orientation", orientation,
7a1c5048 1200 "spacing", MAX(spacing, 1),
f7ecd6ce
AG
1201 "border-width", border,
1202 NULL);
6b775dbb
AG
1203
1204 ig->child_width = child_width;
6b775dbb 1205 ig->child_height = child_height;
7a1c5048 1206 ig->target_dimension = MAX(target_dimension, 0);
6b775dbb
AG
1207
1208 return (GtkWidget *)ig;
2ba86315 1209}