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