Merging upstream version 0.8.2 (Closes: #786485, #784725, #801319).
[debian/lxpanel.git] / src / icon-grid-old.c
CommitLineData
38c4c1ba
AG
1/*
2 * Copyright (C) 2009-2010 Marty Jack <martyj19@comcast.net>
3 * 2009-2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
4 * 2014 Andriy Grytsenko <andrej@rep.kiev.ua>
5 * 2014 Vladimír Pýcha <vpycha@gmail.com>
00916e98
AG
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include <gtk/gtk.h>
23#include <string.h>
24
25#include "icon-grid-old.h"
26#include "private.h"
27
89173f95
AG
28#if GTK_CHECK_VERSION(3, 0, 0)
29#include <gtk/gtkx.h>
30#endif
31
00916e98
AG
32static gboolean icon_grid_placement(IconGrid * ig);
33static void icon_grid_geometry(IconGrid * ig, gboolean layout);
34static void icon_grid_element_size_request(GtkWidget * widget, GtkRequisition * requisition, IconGridElement * ige);
35static void icon_grid_size_request(GtkWidget * widget, GtkRequisition * requisition, IconGrid * ig);
36static void icon_grid_size_allocate(GtkWidget * widget, GtkAllocation * allocation, IconGrid * ig);
37static void icon_grid_demand_resize(IconGrid * ig);
38
39/* Establish the widget placement of an icon grid. */
40static gboolean icon_grid_placement(IconGrid * ig)
41{
42 GtkAllocation allocation;
43
44 if (ig->widget == NULL)
45 return FALSE;
46
47 /* Make sure the container is visible. */
48 gtk_widget_show(ig->container);
49
50 /* Erase the window. */
51 GdkWindow * window = gtk_widget_get_window(ig->widget);
52 if (window != NULL)
53 panel_determine_background_pixmap(ig->panel, ig->widget, window);
54
55 /* Get and save the desired container geometry. */
56 gtk_widget_get_allocation(ig->container, &allocation);
57 ig->container_width = allocation.width;
58 ig->container_height = allocation.height;
59 int child_width = ig->child_width;
60 int child_height = ig->child_height;
61
62 /* Get the required container geometry if all elements get the client's desired allocation. */
63 int container_width_needed = (ig->columns * (child_width + ig->spacing)) - ig->spacing;
64 int container_height_needed = (ig->rows * (child_height + ig->spacing)) - ig->spacing;
65
66 /* Get the constrained child geometry if the allocated geometry is insufficient.
67 * All children are still the same size and share equally in the deficit. */
68 ig->constrained_child_width = ig->child_width;
69 if ((ig->columns != 0) && (ig->rows != 0) && (ig->container_width > 1))
70 {
71 if (container_width_needed > ig->container_width)
72 ig->constrained_child_width = child_width = (ig->container_width - ((ig->columns - 1) * ig->spacing)) / ig->columns;
73 if (container_height_needed > ig->container_height)
74 child_height = (ig->container_height - ((ig->rows - 1) * ig->spacing)) / ig->rows;
75 }
76
77 /* Initialize parameters to control repositioning each visible child. */
78 GtkTextDirection direction = gtk_widget_get_direction(ig->container);
79 int limit = ig->border + ((ig->orientation == GTK_ORIENTATION_HORIZONTAL)
80 ? (ig->rows * (child_height + ig->spacing))
81 : (ig->columns * (child_width + ig->spacing)));
82 int x_initial = ((direction == GTK_TEXT_DIR_RTL)
83 ? allocation.width - child_width - ig->border
84 : ig->border);
85 int x_delta = child_width + ig->spacing;
86 if (direction == GTK_TEXT_DIR_RTL) x_delta = - x_delta;
87
88 /* Reposition each visible child. */
89 int x = x_initial;
90 int y = ig->border;
91 gboolean contains_sockets = FALSE;
92 IconGridElement * ige;
93 for (ige = ig->child_list; ige != NULL; ige = ige->flink)
94 {
95 if (ige->visible)
96 {
97 /* Do necessary operations on the child. */
98 gtk_widget_show(ige->widget);
99 gtk_widget_get_allocation(ige->widget, &allocation);
100 if (((child_width != allocation.width) || (child_height != allocation.height))
101 && (child_width > 0) && (child_height > 0))
102 {
103 GtkAllocation alloc;
104 alloc.x = x;
105 alloc.y = y;
106 alloc.width = child_width;
107 alloc.height = child_height;
108 gtk_widget_size_allocate(ige->widget, &alloc);
109 gtk_widget_queue_resize(ige->widget); /* Get labels to redraw ellipsized */
110 }
111 gtk_fixed_move(GTK_FIXED(ig->widget), ige->widget, x, y);
112 gtk_widget_queue_draw(ige->widget);
113
114 /* Note if a socket is placed. */
115 if (GTK_IS_SOCKET(ige->widget))
116 contains_sockets = TRUE;
117
118 /* Advance to the next grid position. */
119 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
120 {
121 y += child_height + ig->spacing;
122 if (y >= limit)
123 {
124 y = ig->border;
125 x += x_delta;
126 }
127 }
128 else
129 {
130 x += x_delta;
131 if ((direction == GTK_TEXT_DIR_RTL) ? (x <= 0) : (x >= limit))
132 {
133 x = x_initial;
134 y += child_height + ig->spacing;
135 }
136 }
137 }
138 }
139
140 /* Redraw the container. */
141 if (window != NULL)
142 gdk_window_invalidate_rect(window, NULL, TRUE);
143 gtk_widget_queue_draw(ig->container);
144
145 /* If the icon grid contains sockets, do special handling to get the background erased. */
146 if (contains_sockets)
147 plugin_widget_set_background(ig->widget, ig->panel->topgwin);
148 return FALSE;
149}
150
151/* Establish the geometry of an icon grid. */
152static void icon_grid_geometry(IconGrid * ig, gboolean layout)
153{
154 /* Count visible children. */
155 int visible_children = 0;
156 IconGridElement * ige;
157 GtkAllocation allocation;
158
159 for (ige = ig->child_list; ige != NULL; ige = ige->flink)
160 if (ige->visible)
161 visible_children += 1;
162
163 int original_rows = ig->rows;
164 int original_columns = ig->columns;
165 int target_dimension = ig->target_dimension;
166 gtk_widget_get_allocation(ig->container, &allocation);
167 if (ig->orientation == GTK_ORIENTATION_HORIZONTAL)
168 {
169 /* In horizontal orientation, fit as many rows into the available height as possible.
170 * Then allocate as many columns as necessary. Guard against zerodivides. */
171 if (allocation.height > 1)
172 target_dimension = allocation.height;
173 ig->rows = 0;
174 if ((ig->child_height + ig->spacing) != 0)
175 ig->rows = (target_dimension + ig->spacing - ig->border * 2) / (ig->child_height + ig->spacing);
176 if (ig->rows == 0)
177 ig->rows = 1;
178 ig->columns = (visible_children + (ig->rows - 1)) / ig->rows;
179 if ((ig->columns == 1) && (ig->rows > visible_children))
180 ig->rows = visible_children;
181 }
182 else
183 {
184 /* In vertical orientation, fit as many columns into the available width as possible.
185 * Then allocate as many rows as necessary. Guard against zerodivides. */
186 if (allocation.width > 1)
187 target_dimension = allocation.width;
188 ig->columns = 0;
189 if ((ig->child_width + ig->spacing) != 0)
190 ig->columns = (target_dimension + ig->spacing - ig->border * 2) / (ig->child_width + ig->spacing);
191 if (ig->columns == 0)
192 ig->columns = 1;
193 ig->rows = (visible_children + (ig->columns - 1)) / ig->columns;
194 if ((ig->rows == 1) && (ig->columns > visible_children))
195 ig->columns = visible_children;
196 }
197
198 /* If the table geometry or child composition changed, redo the placement of children in table cells.
199 * This is gated by having a valid table allocation and by the "layout" parameter, which prevents a recursive loop.
200 * We do the placement later, also to prevent a recursive loop. */
201 if ((layout)
202 && (( ! ig->actual_dimension)
203 || (ig->rows != original_rows)
204 || (ig->columns != original_columns)
205 || (ig->container_width != allocation.width)
206 || (ig->container_height != allocation.height)
207 || (ig->children_changed)))
208 {
209 ig->actual_dimension = TRUE;
210 ig->children_changed = FALSE;
211 g_idle_add((GSourceFunc) icon_grid_placement, ig);
212 }
213}
214
215/* Handler for "size-request" event on the icon grid element. */
216static void icon_grid_element_size_request(GtkWidget * widget, GtkRequisition * requisition, IconGridElement * ige)
217{
218 /* This is our opportunity to request space for the element. */
219 IconGrid * ig = ige->ig;
220 requisition->width = ig->child_width;
221 if ((ig->constrain_width) && (ig->actual_dimension) && (ig->constrained_child_width > 1))
222 requisition->width = ig->constrained_child_width;
223 requisition->height = ig->child_height;
224}
225
226/* Handler for "size-request" event on the icon grid's container. */
227static void icon_grid_size_request(GtkWidget * widget, GtkRequisition * requisition, IconGrid * ig)
228{
229 /* This is our opportunity to request space for the layout container.
230 * Compute the geometry. Do not lay out children at this time to avoid a recursive loop. */
231 icon_grid_geometry(ig, FALSE);
232
233 /* Compute the requisition. */
234 if ((ig->columns == 0) || (ig->rows == 0))
235 {
236 requisition->width = 1;
237 requisition->height = 1;
238 gtk_widget_hide(ig->widget); /* Necessary to get the plugin to disappear */
239 }
240 else
241 {
242 int column_spaces = ig->columns - 1;
243 int row_spaces = ig->rows - 1;
244 if (column_spaces < 0) column_spaces = 0;
245 if (row_spaces < 0) row_spaces = 0;
246 requisition->width = ig->child_width * ig->columns + column_spaces * ig->spacing + 2 * ig->border;
247 requisition->height = ig->child_height * ig->rows + row_spaces * ig->spacing + 2 * ig->border;
248 gtk_widget_show(ig->widget);
249 }
250}
251
252/* Handler for "size-allocate" event on the icon grid's container. */
253static void icon_grid_size_allocate(GtkWidget * widget, GtkAllocation * allocation, IconGrid * ig)
254{
255 /* This is our notification that there is a resize of the entire panel.
256 * Compute the geometry and recompute layout if the geometry changed. */
257 icon_grid_geometry(ig, TRUE);
258}
259
260/* Initiate a resize. */
261static void icon_grid_demand_resize(IconGrid * ig)
262{
263 ig->children_changed = TRUE;
264 GtkRequisition req;
265 icon_grid_size_request(NULL, &req, ig);
266
267 if ((ig->rows != 0) || (ig->columns != 0))
268 icon_grid_placement(ig);
269}
270
271/* Establish an icon grid in a specified container widget.
272 * The icon grid manages the contents of the container.
273 * The orientation, geometry of the elements, and spacing can be varied. All elements are the same size. */
274IconGrid * icon_grid_new(
275 Panel * panel, GtkWidget * container,
276 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
277{
278 /* Create a structure representing the icon grid and collect the parameters. */
279 IconGrid * ig = g_new0(IconGrid, 1);
280 ig->panel = panel;
281 ig->container = container;
282 ig->orientation = orientation;
283 ig->child_width = child_width;
284 ig->constrained_child_width = child_width;
285 ig->child_height = child_height;
286 ig->spacing = spacing;
287 ig->border = border;
288 ig->target_dimension = target_dimension;
289
290 /* Create a layout container. */
291 ig->widget = gtk_fixed_new();
292 g_object_add_weak_pointer(G_OBJECT(ig->widget), (gpointer*)&ig->widget);
293 gtk_widget_set_has_window(ig->widget, FALSE);
294 gtk_widget_set_redraw_on_allocate(ig->widget, FALSE);
295 gtk_container_add(GTK_CONTAINER(ig->container), ig->widget);
296 gtk_widget_show(ig->widget);
297
298 /* Connect signals. */
299 g_signal_connect(G_OBJECT(ig->widget), "size-request", G_CALLBACK(icon_grid_size_request), (gpointer) ig);
300 g_signal_connect(G_OBJECT(container), "size-request", G_CALLBACK(icon_grid_size_request), (gpointer) ig);
301 g_signal_connect(G_OBJECT(container), "size-allocate", G_CALLBACK(icon_grid_size_allocate), (gpointer) ig);
302 return ig;
303}
304
305/* Add an icon grid element and establish its initial visibility. */
306void icon_grid_add(IconGrid * ig, GtkWidget * child, gboolean visible)
307{
308 /* Create and initialize a structure representing the child. */
309 IconGridElement * ige = g_new0(IconGridElement, 1);
310 ige->ig = ig;
311 ige->widget = child;
312 ige->visible = visible;
313
314 /* Insert at the tail of the child list. This keeps the graphics in the order they were added. */
315 if (ig->child_list == NULL)
316 ig->child_list = ige;
317 else
318 {
319 IconGridElement * ige_cursor;
320 for (ige_cursor = ig->child_list; ige_cursor->flink != NULL; ige_cursor = ige_cursor->flink) ;
321 ige_cursor->flink = ige;
322 }
323
324 /* Add the widget to the layout container. */
325 if (visible)
326 gtk_widget_show(ige->widget);
327 gtk_fixed_put(GTK_FIXED(ig->widget), ige->widget, 0, 0);
328 g_signal_connect(G_OBJECT(child), "size-request", G_CALLBACK(icon_grid_element_size_request), (gpointer) ige);
329
330 /* Do a relayout. */
331 icon_grid_demand_resize(ig);
332}
333
334extern void icon_grid_set_constrain_width(IconGrid * ig, gboolean constrain_width)
335{
336 ig->constrain_width = constrain_width;
337}
338
339/* Remove an icon grid element. */
340void icon_grid_remove(IconGrid * ig, GtkWidget * child)
341{
342 IconGridElement * ige_pred = NULL;
343 IconGridElement * ige;
344 for (ige = ig->child_list; ige != NULL; ige_pred = ige, ige = ige->flink)
345 {
346 if (ige->widget == child)
347 {
348 /* The child is found. Remove from child list and layout container. */
349 gtk_widget_hide(ige->widget);
350 gtk_container_remove(GTK_CONTAINER(ig->widget), ige->widget);
351
352 if (ige_pred == NULL)
353 ig->child_list = ige->flink;
354 else
355 ige_pred->flink = ige->flink;
356
357 /* Do a relayout. */
358 icon_grid_demand_resize(ig);
359 break;
360 }
361 }
362}
363
364/* Get the index of an icon grid element. */
365extern gint icon_grid_get_child_position(IconGrid * ig, GtkWidget * child)
366{
367 gint i;
368 IconGridElement * ige;
369 for (ige = ig->child_list, i = 0; ige != NULL; ige = ige->flink, i++)
370 {
371 if (ige->widget == child)
372 {
373 return i;
374 break;
375 }
376 }
377
378 return -1;
379}
380
381/* Reorder an icon grid element. */
382extern void icon_grid_reorder_child(IconGrid * ig, GtkWidget * child, gint position)
383{
384 /* Remove the child from its current position. */
385 IconGridElement * ige_pred = NULL;
386 IconGridElement * ige;
387 for (ige = ig->child_list; ige != NULL; ige_pred = ige, ige = ige->flink)
388 {
389 if (ige->widget == child)
390 {
391 if (ige_pred == NULL)
392 ig->child_list = ige->flink;
393 else
394 ige_pred->flink = ige->flink;
395 break;
396 }
397 }
398
399 /* If the child was found, insert it at the new position. */
400 if (ige != NULL)
401 {
402 if (ig->child_list == NULL)
403 {
404 ige->flink = NULL;
405 ig->child_list = ige;
406 }
407 else if (position == 0)
408 {
409 ige->flink = ig->child_list;
410 ig->child_list = ige;
411 }
412 else
413 {
414 int local_position = position - 1;
415 IconGridElement * ige_pred;
416 for (
417 ige_pred = ig->child_list;
418 ((ige_pred != NULL) && (local_position > 0));
419 local_position -= 1, ige_pred = ige_pred->flink) ;
420 ige->flink = ige_pred->flink;
421 ige_pred->flink = ige;
422 }
423
424 /* Do a relayout. */
425 if (ige->visible)
426 icon_grid_demand_resize(ig);
427 }
428}
429
430/* Change the geometry of an icon grid. */
431void icon_grid_set_geometry(IconGrid * ig,
432 GtkOrientation orientation, gint child_width, gint child_height, gint spacing, gint border, gint target_dimension)
433{
434 ig->orientation = orientation;
435 ig->child_width = child_width;
436 ig->constrained_child_width = child_width;
437 ig->child_height = child_height;
438 ig->spacing = spacing;
439 ig->border = border;
440 ig->target_dimension = target_dimension;
441 icon_grid_demand_resize(ig);
442}
443
444/* Change the visibility of an icon grid element. */
445void icon_grid_set_visible(IconGrid * ig, GtkWidget * child, gboolean visible)
446{
447 IconGridElement * ige;
448 for (ige = ig->child_list; ige != NULL; ige = ige->flink)
449 {
450 if (ige->widget == child)
451 {
452 if (ige->visible != visible)
453 {
454 /* Found, and the visibility changed. Do a relayout. */
455 ige->visible = visible;
456 if ( ! ige->visible)
457 gtk_widget_hide(ige->widget);
458 icon_grid_demand_resize(ig);
459 }
460 break;
461 }
462 }
463}
464
465/* Deallocate the icon grid structures. */
466void icon_grid_free(IconGrid * ig)
467{
468 /* Hide the layout container. */
469 if (ig->widget != NULL)
470 {
471 g_object_remove_weak_pointer(G_OBJECT(ig->widget), (gpointer*)&ig->widget);
472 gtk_widget_hide(ig->widget);
473 }
474
475 /* Free all memory. */
476 IconGridElement * ige = ig->child_list;
477 while (ige != NULL)
478 {
479 IconGridElement * ige_succ = ige->flink;
480 g_free(ige);
481 ige = ige_succ;
482 }
483 g_free(ig);
484}