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