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