Index: libnautilus-private/nautilus-icon-container.c =================================================================== RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-icon-container.c,v retrieving revision 1.339 diff -u -r1.339 nautilus-icon-container.c --- libnautilus-private/nautilus-icon-container.c 23 Apr 2003 14:31:21 -0000 1.339 +++ libnautilus-private/nautilus-icon-container.c 3 May 2003 23:21:42 -0000 @@ -101,9 +101,9 @@ #define STANDARD_ICON_GRID_WIDTH 155 /* Desktop layout mode defines */ -#define DESKTOP_PAD_HORIZONTAL 30 +#define DESKTOP_PAD_HORIZONTAL 10 #define DESKTOP_PAD_VERTICAL 10 -#define CELL_SIZE 20 +#define SNAP_SIZE 78 /* Value used to protect against icons being dragged outside of the desktop bounds */ #define DESKTOP_ICON_SAFETY_PAD 10 @@ -116,6 +116,14 @@ #define MINIMUM_EMBEDDED_TEXT_RECT_WIDTH 20 #define MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT 20 +#define SNAP_HORIZONTAL(func,x) ((func ((double)((x) - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE) * SNAP_SIZE) + DESKTOP_PAD_HORIZONTAL) +#define SNAP_VERTICAL(func, y) ((func ((double)((y) - DESKTOP_PAD_VERTICAL) / SNAP_SIZE) * SNAP_SIZE) + DESKTOP_PAD_VERTICAL) + +#define SNAP_NEAREST_HORIZONTAL(x) SNAP_HORIZONTAL (eel_round, x) +#define SNAP_NEAREST_VERTICAL(y) SNAP_VERTICAL (eel_round, y) + +#define SNAP_CEIL_HORIZONTAL(x) SNAP_HORIZONTAL (ceil, x) +#define SNAP_CEIL_VERTICAL(y) SNAP_VERTICAL (ceil, y) enum { NAUTILUS_TYPESELECT_FLUSH_DELAY = 1000000 @@ -220,6 +228,15 @@ CLEARED, LAST_SIGNAL }; + +typedef struct { + int **icon_grid; + int *grid_memory; + int num_rows; + int num_columns; + gboolean tight; +} PlacementGrid; + static guint signals[LAST_SIGNAL]; /* Functions dealing with NautilusIcons. */ @@ -335,6 +352,7 @@ icon_set_size (NautilusIconContainer *container, NautilusIcon *icon, guint icon_size, + gboolean snap, gboolean update_position) { guint old_size; @@ -350,8 +368,8 @@ (container->details->zoom_level); nautilus_icon_container_move_icon (container, icon, icon->x, icon->y, - scale, scale, - FALSE, update_position); + scale, scale, FALSE, + snap, update_position); } static void @@ -398,6 +416,15 @@ if (icon == container->details->stretch_icon) { container->details->stretch_icon = NULL; nautilus_icon_canvas_item_set_show_stretch_handles (icon->item, FALSE); + /* snap the icon if necessary */ + if (container->details->keep_aligned) { + nautilus_icon_container_move_icon (container, + icon, + icon->x, icon->y, + icon->scale_x, icon->scale_y, + FALSE, TRUE, TRUE); + } + emit_stretch_ended (container, icon); } @@ -1004,150 +1031,313 @@ g_array_free (positions, TRUE); } -/* Search for available space at location */ -static gboolean -find_open_grid_space (NautilusIcon *icon, int **icon_grid, int num_rows, - int num_columns, int row, int column) -{ - int row_index, column_index; - int x1, x2, y1, y2; - double width, height; - int qwidth, qheight; - - /* Get icon dimensions */ - icon_get_bounding_box (icon, &x1, &y1, &x2, &y2); - - width = (x2 - x1) + DESKTOP_PAD_HORIZONTAL; - height = (y2 - y1) + DESKTOP_PAD_VERTICAL; - - /* Convert to grid coordinates */ - qwidth = ceil (width / CELL_SIZE); - qheight = ceil (height / CELL_SIZE); +static void +snap_position (NautilusIconContainer *container, + NautilusIcon *icon, + int *x, int *y) +{ + int center_x; + int baseline_y; + int icon_width; + int icon_height; + ArtDRect icon_position; + + if (*x < DESKTOP_PAD_HORIZONTAL) { + *x = DESKTOP_PAD_HORIZONTAL; + } - if ((row + qwidth > num_rows) || (column + qheight > num_columns)) { - return FALSE; + if (*y < DESKTOP_PAD_VERTICAL) { + *y = DESKTOP_PAD_VERTICAL; } - qwidth += row; - qheight += column; + icon_position = nautilus_icon_canvas_item_get_icon_rectangle (icon->item); + icon_width = icon_position.x1 - icon_position.x0; + icon_height = icon_position.y1 - icon_position.y0; - for (row_index = row; row_index < qwidth; row_index++) { - for (column_index = column; column_index < qheight; column_index++) { - if (icon_grid [row_index] [column_index] == 1) { - return FALSE; - } - } - } - return TRUE; + center_x = *x + icon_width / 2; + *x = SNAP_NEAREST_HORIZONTAL (center_x) - (icon_width / 2); + + /* Find the grid position vertically and place on the proper baseline */ + baseline_y = *y + icon_height; + baseline_y = SNAP_NEAREST_VERTICAL (baseline_y); + *y = baseline_y - (icon_position.y1 - icon_position.y0); +} + +static int +compare_icons_by_position (gconstpointer a, gconstpointer b) +{ + NautilusIcon *icon_a, *icon_b; + int x1, y1, x2, y2; + int center_a; + int center_b; + + icon_a = (NautilusIcon*)a; + icon_b = (NautilusIcon*)b; + + icon_get_bounding_box (icon_a, &x1, &y1, &x2, &y2); + center_a = x1 + (x2 - x1) / 2; + icon_get_bounding_box (icon_b, &x1, &y1, &x2, &y2); + center_b = x1 + (x2 - x1) / 2; + + return center_a == center_b ? + icon_a->y - icon_b->y : + center_a - center_b; } +static PlacementGrid * +placement_grid_new (NautilusIconContainer *container, gboolean tight) +{ + PlacementGrid *grid; + int width, height; + int num_columns; + int num_rows; + int i; + + /* Get container dimensions */ + width = GTK_WIDGET (container)->allocation.width / + EEL_CANVAS (container)->pixels_per_unit + - container->details->left_margin + - container->details->right_margin; + height = GTK_WIDGET (container)->allocation.height / + EEL_CANVAS (container)->pixels_per_unit + - container->details->top_margin + - container->details->bottom_margin; + + num_columns = width / SNAP_SIZE; + num_rows = height / SNAP_SIZE; + + if (num_columns == 0 || num_rows == 0) { + return NULL; + } + + grid = g_new0 (PlacementGrid, 1); + grid->tight = tight; + grid->num_columns = num_columns; + grid->num_rows = num_rows; + + grid->grid_memory = g_new0 (int, (num_rows * num_columns)); + grid->icon_grid = g_new0 (int *, num_columns); + + for (i = 0; i < num_columns; i++) { + grid->icon_grid[i] = grid->grid_memory + (i * num_rows); + } + + return grid; +} static void -get_best_empty_grid_location (NautilusIcon *icon, int **icon_grid, int num_rows, - int num_columns, int *x, int *y) +placement_grid_free (PlacementGrid *grid) { - gboolean found_space; - int row, column; + g_free (grid->icon_grid); + g_free (grid->grid_memory); + g_free (grid); +} + +static gboolean +placement_grid_position_is_free (PlacementGrid *grid, ArtIRect pos) +{ + int x, y; - g_assert (icon_grid != NULL); - g_assert (x != NULL); - g_assert (y != NULL); - - found_space = FALSE; - - /* Set up default fallback position */ - *x = num_columns * CELL_SIZE; - *y = num_rows * CELL_SIZE; - - /* Find best empty location */ - for (row = 0; row < num_rows; row++) { - for (column = 0; column < num_columns; column++) { - found_space = find_open_grid_space (icon, icon_grid, num_rows, - num_columns, row, column); - if (found_space) { - *x = row * CELL_SIZE; - *y = column * CELL_SIZE; - - /* Correct for padding */ - if (*x < DESKTOP_PAD_HORIZONTAL) { - *x = DESKTOP_PAD_HORIZONTAL; - } - if (*y < DESKTOP_PAD_VERTICAL) { - *y = DESKTOP_PAD_VERTICAL; - } - return; + g_return_val_if_fail (pos.x0 >= 0 && pos.x0 < grid->num_columns, TRUE); + g_return_val_if_fail (pos.y0 >= 0 && pos.y0 < grid->num_rows, TRUE); + g_return_val_if_fail (pos.x1 >= 0 && pos.x1 < grid->num_columns, TRUE); + g_return_val_if_fail (pos.y1 >= 0 && pos.y1 < grid->num_rows, TRUE); + + for (x = pos.x0; x <= pos.x1; x++) { + for (y = pos.y0; y <= pos.y1; y++) { + if (grid->icon_grid[x][y] != 0) { + return FALSE; } - } + } } + + return TRUE; } static void -mark_icon_location_in_grid (NautilusIcon *icon, int **icon_grid, int num_rows, int num_columns) +placement_grid_mark (PlacementGrid *grid, ArtIRect pos) { - int x1, x2, y1, y2; - double width, height; - int qx, qy, qwidth, qheight, qy_index; - int grid_width, grid_height; - - icon_get_bounding_box (icon, &x1, &y1, &x2, &y2); - - width = (x2 - x1) + DESKTOP_PAD_HORIZONTAL; - height = (y2 - y1) + DESKTOP_PAD_VERTICAL; - - /* Convert x and y to our quantized grid value */ - qx = icon->x / CELL_SIZE; - qy = icon->y / CELL_SIZE; - qwidth = ceil (width / CELL_SIZE); - qheight = ceil (height / CELL_SIZE); - - /* Check and correct for edge conditions */ - grid_width = num_rows; - grid_height = num_columns; + int x, y; - if ((qx + qwidth) > grid_width) { - qwidth = grid_width; - } else { - qwidth = qx + qwidth; + g_return_if_fail (pos.x0 >= 0 && pos.x0 < grid->num_columns); + g_return_if_fail (pos.y0 >= 0 && pos.y0 < grid->num_rows); + g_return_if_fail (pos.x1 >= 0 && pos.x1 < grid->num_columns); + g_return_if_fail (pos.y1 >= 0 && pos.y1 < grid->num_rows); + + for (x = pos.x0; x <= pos.x1; x++) { + for (y = pos.y0; y <= pos.y1; y++) { + grid->icon_grid[x][y] = 1; + } } - if ((qy + qheight) > grid_height) { - qheight = grid_height; +} + +static void +canvas_position_to_grid_position (PlacementGrid *grid, + ArtIRect canvas_position, + ArtIRect *grid_position) +{ + /* The first bit of this block will identify all intersections + * that the icon actually crosses. The second bit will mark + * any intersections that the icon is adjacent to. + * The first causes minimal moving around during a snap, but + * can end up with partially overlapping icons. The second one won't + * allow any overlapping, but can cause more movement to happen + * during a snap. */ + if (grid->tight) { + grid_position->x0 = ceil ((double)(canvas_position.x0 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE); + grid_position->y0 = ceil ((double)(canvas_position.y0 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE); + grid_position->x1 = floor ((double)(canvas_position.x1 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE); + grid_position->y1 = floor ((double)(canvas_position.y1 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE); } else { - qheight = qy + qheight; + grid_position->x0 = floor ((double)(canvas_position.x0 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE); + grid_position->y0 = floor ((double)(canvas_position.y0 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE); + grid_position->x1 = ceil ((double)(canvas_position.x1 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE); + grid_position->y1 = ceil ((double)(canvas_position.y1 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE); } + + grid_position->x0 = CLAMP (grid_position->x0, 0, grid->num_columns - 1); + grid_position->y0 = CLAMP (grid_position->y0, 0, grid->num_rows - 1); + grid_position->x1 = CLAMP (grid_position->x1, grid_position->x0, grid->num_columns - 1); + grid_position->y1 = CLAMP (grid_position->y1, grid_position->y0, grid->num_rows - 1); +} + +static void +placement_grid_mark_icon (PlacementGrid *grid, NautilusIcon *icon) +{ + ArtIRect icon_pos; + ArtIRect grid_pos; - /* Mark location */ - for (; qx < qwidth; qx++) { - for (qy_index = qy; qy_index < qheight; qy_index++) { - icon_grid [qx] [qy_index] = 1; + icon_get_bounding_box (icon, + &icon_pos.x0, &icon_pos.y0, + &icon_pos.x1, &icon_pos.y1); + canvas_position_to_grid_position (grid, + icon_pos, + &grid_pos); + placement_grid_mark (grid, grid_pos); +} + +static void +find_empty_location (NautilusIconContainer *container, + PlacementGrid *grid, + NautilusIcon *icon, + int start_x, + int start_y, + int *x, + int *y) +{ + double icon_width, icon_height; + int canvas_width; + int canvas_height; + ArtIRect icon_position; + ArtDRect pixbuf_rect; + gboolean collision; + + /* Get container dimensions */ + canvas_width = GTK_WIDGET (container)->allocation.width / + EEL_CANVAS (container)->pixels_per_unit + - container->details->left_margin + - container->details->right_margin; + canvas_height = GTK_WIDGET (container)->allocation.height / + EEL_CANVAS (container)->pixels_per_unit + - container->details->top_margin + - container->details->bottom_margin; + + icon_get_bounding_box (icon, + &icon_position.x0, &icon_position.y0, + &icon_position.x1, &icon_position.y1); + icon_width = icon_position.x1 - icon_position.x0; + icon_height = icon_position.y1 - icon_position.y0; + + pixbuf_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item); + + /* Start the icon on a grid location */ + snap_position (container, icon, &start_x, &start_y); + + icon_position.x0 = start_x; + icon_position.y0 = start_y; + icon_position.x1 = icon_position.x0 + icon_width; + icon_position.y1 = icon_position.y0 + icon_height; + + do { + ArtIRect grid_position; + + collision = FALSE; + + canvas_position_to_grid_position (grid, + icon_position, + &grid_position); + + if (!placement_grid_position_is_free (grid, grid_position)) { + icon_position.y0 += SNAP_SIZE; + icon_position.y1 = icon_position.y0 + icon_width; + + if (icon_position.y1 + DESKTOP_PAD_VERTICAL > canvas_height) { + /* Move to the next column */ + icon_position.y0 = DESKTOP_PAD_VERTICAL + SNAP_SIZE - (pixbuf_rect.y1 - pixbuf_rect.y0); + while (icon_position.y0 < DESKTOP_PAD_VERTICAL) { + icon_position.y0 += SNAP_SIZE; + } + icon_position.y1 = icon_position.y0 + icon_width; + + icon_position.x0 += SNAP_SIZE; + icon_position.x1 = icon_position.x0 + icon_height; + } + + collision = TRUE; } - } + } while (collision && (icon_position.x1 < canvas_width)); + + *x = icon_position.x0; + *y = icon_position.y0; } -static void -mark_icon_locations_in_grid (GList *icon_list, int **icon_grid, int num_rows, int num_columns) +static void +align_icons (NautilusIconContainer *container) { - GList *p; - NautilusIcon *icon; + GList *unplaced_icons; + GList *l; + PlacementGrid *grid; + + unplaced_icons = g_list_copy (container->details->icons); - /* Mark filled grid locations */ - for (p = icon_list; p != NULL; p = p->next) { - icon = p->data; - mark_icon_location_in_grid (icon, icon_grid, num_rows, num_columns); + unplaced_icons = g_list_sort (unplaced_icons, + compare_icons_by_position); + + grid = placement_grid_new (container, TRUE); + + if (!grid) { + return; } + + for (l = unplaced_icons; l != NULL; l = l->next) { + NautilusIcon *icon; + int x, y; + + icon = l->data; + x = icon->x; + y = icon->y; + + find_empty_location (container, grid, + icon, x, y, &x, &y); + + icon_set_position (icon, x, y); + + placement_grid_mark_icon (grid, icon); + } + + g_list_free (unplaced_icons); + + placement_grid_free (grid); } static void lay_down_icons_tblr (NautilusIconContainer *container, GList *icons) { GList *p, *placed_icons, *unplaced_icons; - int index, total, new_length, placed; + int total, new_length, placed; NautilusIcon *icon; - int width, height, max_width, icon_width, icon_height; + int width, height, max_width, column_width, icon_width, icon_height; int x, y, x1, x2, y1, y2; - int *grid_memory; - int **icon_grid; - int num_rows, num_columns; - int row, column; ArtDRect icon_rect; /* Get container dimensions */ @@ -1168,6 +1358,7 @@ new_length = g_list_length (icons); placed = total - new_length; if (placed > 0) { + PlacementGrid *grid; /* Add only placed icons in list */ for (p = container->details->icons; p != NULL; p = p->next) { icon = p->data; @@ -1181,54 +1372,40 @@ } placed_icons = g_list_reverse (placed_icons); unplaced_icons = g_list_reverse (unplaced_icons); - - /* Allocate grid array */ - num_rows = width / CELL_SIZE; - num_columns = height / CELL_SIZE; - - /* Allocate array memory */ - grid_memory = malloc (num_rows * num_columns * sizeof (int *)); - g_assert (grid_memory); - - /* Allocate room for the pointers to the rows */ - icon_grid = malloc (num_rows * sizeof (int *)); - g_assert (icon_grid); - - /* Point to array pointers */ - for (index = 0; index < num_rows; index++) { - icon_grid[index] = grid_memory + (index * num_columns); - } - - /* Set all grid values to unfilled */ - for (row = 0; row < num_rows; row++) { - for (column = 0; column < num_columns; column++) { - icon_grid [row] [column] = 0; - } - } - - /* Mark filled grid locations */ - mark_icon_locations_in_grid (placed_icons, icon_grid, num_rows, num_columns); - /* Place unplaced icons in the best locations */ - for (p = unplaced_icons; p != NULL; p = p->next) { - icon = p->data; - get_best_empty_grid_location (icon, icon_grid, num_rows, num_columns, - &x, &y); + grid = placement_grid_new (container, FALSE); - icon_get_bounding_box (icon, &x1, &y1, &x2, &y2); - icon_width = x2 - x1; + if (grid) { + for (p = placed_icons; p != NULL; p = p->next) { + placement_grid_mark_icon + (grid, (NautilusIcon*)p->data); + } - icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item); + /* Place unplaced icons in the best locations */ + for (p = unplaced_icons; p != NULL; p = p->next) { + icon = p->data; + + icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item); + icon_get_bounding_box (icon, + &x1, &y1, &x2, &y2); + + /* Start the icon in the first column */ + x = DESKTOP_PAD_HORIZONTAL + SNAP_SIZE - ((x2 - x1) / 2); + y = DESKTOP_PAD_VERTICAL + SNAP_SIZE - (icon_rect.y1 - icon_rect.y0); + + find_empty_location (container, + grid, + icon, + x, y, + &x, &y); + + icon_set_position (icon, x, y); + placement_grid_mark_icon (grid, icon); + } - icon_set_position (icon, - x + (icon_width - (icon_rect.x1 - icon_rect.x0)) / 2, y); - /* Add newly placed icon to grid */ - mark_icon_location_in_grid (icon, icon_grid, num_rows, num_columns); + placement_grid_free (grid); } - - /* Clean up */ - free (icon_grid); - free (grid_memory); + g_list_free (placed_icons); g_list_free (unplaced_icons); } else { @@ -1236,18 +1413,32 @@ x = DESKTOP_PAD_HORIZONTAL; while (icons != NULL) { + int center_x; + int baseline; + gboolean should_snap; + + should_snap = !(container->details->tighter_layout && !container->details->keep_aligned); + y = DESKTOP_PAD_VERTICAL; - max_width = 0; + max_width = 0; + /* Calculate max width for column */ for (p = icons; p != NULL; p = p->next) { icon = p->data; - icon_get_bounding_box (icon, &x1, &y1, &x2, &y2); icon_width = x2 - x1; icon_height = y2 - y1; - + + if (should_snap) { + /* Snap the baseline to a grid position */ + icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item); + baseline = y + (icon_rect.y1 - icon_rect.y0); + baseline = SNAP_CEIL_VERTICAL (baseline); + y = baseline - (icon_rect.y1 - icon_rect.y0); + } + /* Check and see if we need to move to a new column */ if (y != DESKTOP_PAD_VERTICAL && y > height - icon_height) { break; @@ -1261,6 +1452,15 @@ } y = DESKTOP_PAD_VERTICAL; + + center_x = x + max_width / 2; + column_width = max_width; + if (should_snap) { + /* Find the grid column to center on */ + center_x = SNAP_CEIL_HORIZONTAL (center_x); + column_width = (center_x - x) + (max_width / 2); + } + /* Lay out column */ for (p = icons; p != NULL; p = p->next) { icon = p->data; @@ -1269,15 +1469,21 @@ icon_height = y2 - y1; icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item); + + if (should_snap) { + baseline = y + (icon_rect.y1 - icon_rect.y0); + baseline = SNAP_CEIL_VERTICAL (baseline); + y = baseline - (icon_rect.y1 - icon_rect.y0); + } /* Check and see if we need to move to a new column */ if (y != DESKTOP_PAD_VERTICAL && y > height - icon_height) { - x += max_width + DESKTOP_PAD_HORIZONTAL; + x += column_width + DESKTOP_PAD_HORIZONTAL; break; } icon_set_position (icon, - x + max_width / 2 - (icon_rect.x1 - icon_rect.x0) / 2, + center_x - (icon_rect.x1 - icon_rect.x0) / 2, y); y += icon_height + DESKTOP_PAD_VERTICAL; @@ -1493,6 +1699,7 @@ int x, int y, double scale_x, double scale_y, gboolean raise, + gboolean snap, gboolean update_position) { NautilusIconContainerDetails *details; @@ -1507,13 +1714,6 @@ end_renaming_mode (container, TRUE); } - if (!details->auto_layout) { - if (x != icon->x || y != icon->y) { - icon_set_position (icon, x, y); - emit_signal = update_position; - } - } - if (scale_x != icon->scale_x || scale_y != icon->scale_y) { icon->scale_x = scale_x; icon->scale_y = scale_y; @@ -1522,7 +1722,17 @@ redo_layout (container); emit_signal = TRUE; } + } + + if (!details->auto_layout) { + if (details->keep_aligned && snap) { + snap_position (container, icon, &x, &y); + } + if (x != icon->x || y != icon->y) { + icon_set_position (icon, x, y); + emit_signal = update_position; + } } if (emit_signal) { @@ -2598,6 +2808,12 @@ g_source_remove (container->details->stretch_idle_id); container->details->stretch_idle_id = 0; } + + if (container->details->align_idle_id != 0) { + g_source_remove (container->details->align_idle_id); + container->details->align_idle_id = 0; + } + nautilus_icon_container_flush_typeselect_state (container); @@ -2985,7 +3201,7 @@ &world_x, &world_y); icon_set_position (icon, world_x, world_y); - icon_set_size (container, icon, stretch_state.icon_size, FALSE); + icon_set_size (container, icon, stretch_state.icon_size, FALSE, FALSE); container->details->stretch_idle_id = 0; @@ -3062,6 +3278,7 @@ icon_set_size (container, stretched_icon, container->details->stretch_initial_size, + TRUE, TRUE); container->details->stretch_icon = NULL; @@ -5113,7 +5330,7 @@ nautilus_icon_container_move_icon (container, icon, icon->x, icon->y, 1.0, 1.0, - FALSE, TRUE); + FALSE, TRUE, TRUE); } } } @@ -5251,6 +5468,57 @@ } } +gboolean +nautilus_icon_container_is_keep_aligned (NautilusIconContainer *container) +{ + return container->details->keep_aligned; +} + +static gboolean +align_icons_callback (gpointer callback_data) +{ + NautilusIconContainer *container; + + container = NAUTILUS_ICON_CONTAINER (callback_data); + align_icons (container); + container->details->align_idle_id = 0; + + return FALSE; +} + +static void +unschedule_align_icons (NautilusIconContainer *container) +{ + if (container->details->align_idle_id != 0) { + g_source_remove (container->details->align_idle_id); + container->details->align_idle_id = 0; + } +} + +static void +schedule_align_icons (NautilusIconContainer *container) +{ + if (container->details->align_idle_id == 0 + && container->details->has_been_allocated) { + container->details->align_idle_id = g_idle_add + (align_icons_callback, container); + } +} + +void +nautilus_icon_container_set_keep_aligned (NautilusIconContainer *container, + gboolean keep_aligned) +{ + if (container->details->keep_aligned != keep_aligned) { + container->details->keep_aligned = keep_aligned; + + if (keep_aligned && !container->details->auto_layout) { + schedule_align_icons (container); + } else { + unschedule_align_icons (container); + } + } +} void nautilus_icon_container_set_layout_mode (NautilusIconContainer *container, Index: libnautilus-private/nautilus-icon-container.h =================================================================== RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-icon-container.h,v retrieving revision 1.78 diff -u -r1.78 nautilus-icon-container.h --- libnautilus-private/nautilus-icon-container.h 7 Apr 2003 11:56:03 -0000 1.78 +++ libnautilus-private/nautilus-icon-container.h 3 May 2003 23:21:42 -0000 @@ -205,6 +205,10 @@ gboolean nautilus_icon_container_is_tighter_layout (NautilusIconContainer *container); void nautilus_icon_container_set_tighter_layout (NautilusIconContainer *container, gboolean tighter_layout); + +gboolean nautilus_icon_container_is_keep_aligned (NautilusIconContainer *container); +void nautilus_icon_container_set_keep_aligned (NautilusIconContainer *container, + gboolean keep_aligned); void nautilus_icon_container_set_layout_mode (NautilusIconContainer *container, NautilusIconLayoutMode mode); void nautilus_icon_container_sort (NautilusIconContainer *container); Index: libnautilus-private/nautilus-icon-dnd.c =================================================================== RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-icon-dnd.c,v retrieving revision 1.130 diff -u -r1.130 nautilus-icon-dnd.c --- libnautilus-private/nautilus-icon-dnd.c 23 Apr 2003 09:49:47 -0000 1.130 +++ libnautilus-private/nautilus-icon-dnd.c 3 May 2003 23:21:43 -0000 @@ -811,7 +811,7 @@ (container, icon, world_x + item->icon_x, world_y + item->icon_y, icon->scale_x, icon->scale_y, - TRUE, TRUE); + TRUE, TRUE, TRUE); } moved_icons = g_list_prepend (moved_icons, icon); } Index: libnautilus-private/nautilus-icon-private.h =================================================================== RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-icon-private.h,v retrieving revision 1.71 diff -u -r1.71 nautilus-icon-private.h --- libnautilus-private/nautilus-icon-private.h 27 Mar 2003 12:53:07 -0000 1.71 +++ libnautilus-private/nautilus-icon-private.h 3 May 2003 23:21:43 -0000 @@ -177,6 +177,9 @@ /* Idle handler for stretch code */ guint stretch_idle_id; + /* Align idle id */ + guint align_idle_id; + /* DnD info. */ NautilusIconDndInfo *dnd_info; @@ -215,7 +218,10 @@ /* Layout mode */ NautilusIconLayoutMode layout_mode; - /* Set to TRUE after first allocation has been done */ + /* Should the container keep icons aligned to a grid */ + gboolean keep_aligned; + + /* Set to TRUE after first allocation has been done */ gboolean has_been_allocated; /* Is the container fixed or resizable */ @@ -252,6 +258,7 @@ double scale_x, double scale_y, gboolean raise, + gboolean snap, gboolean update_position); void nautilus_icon_container_select_list_unselect_others (NautilusIconContainer *container, GList *icons); Index: libnautilus-private/nautilus-metadata.h =================================================================== RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-metadata.h,v retrieving revision 1.25 diff -u -r1.25 nautilus-metadata.h --- libnautilus-private/nautilus-metadata.h 19 Dec 2002 19:56:36 -0000 1.25 +++ libnautilus-private/nautilus-metadata.h 3 May 2003 23:21:43 -0000 @@ -53,6 +53,7 @@ #define NAUTILUS_METADATA_KEY_ICON_VIEW_TIGHTER_LAYOUT "icon_view_tighter_layout" #define NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY "icon_view_sort_by" #define NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED "icon_view_sort_reversed" +#define NAUTILUS_METADATA_KEY_ICON_VIEW_KEEP_ALIGNED "icon_view_keep_aligned" #define NAUTILUS_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL "list_view_zoom_level" #define NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN "list_view_sort_column" Index: src/file-manager/fm-desktop-icon-view.c =================================================================== RCS file: /cvs/gnome/nautilus/src/file-manager/fm-desktop-icon-view.c,v retrieving revision 1.202 diff -u -r1.202 fm-desktop-icon-view.c --- src/file-manager/fm-desktop-icon-view.c 23 Apr 2003 10:49:38 -0000 1.202 +++ src/file-manager/fm-desktop-icon-view.c 3 May 2003 23:21:43 -0000 @@ -121,6 +121,7 @@ FMDesktopIconView *icon_view); static void update_desktop_directory (UpdateType type); static gboolean real_supports_auto_layout (FMIconView *view); +static gboolean real_supports_keep_aligned (FMIconView *view); static void real_merge_menus (FMDirectoryView *view); static void real_update_menus (FMDirectoryView *view); static gboolean real_supports_zooming (FMDirectoryView *view); @@ -300,6 +301,7 @@ FM_DIRECTORY_VIEW_CLASS (class)->supports_zooming = real_supports_zooming; FM_ICON_VIEW_CLASS (class)->supports_auto_layout = real_supports_auto_layout; + FM_ICON_VIEW_CLASS (class)->supports_keep_aligned = real_supports_keep_aligned; } static void @@ -1470,6 +1472,12 @@ * fixed-size window. */ return FALSE; +} + +static gboolean +real_supports_keep_aligned (FMIconView *view) +{ + return TRUE; } static gboolean Index: src/file-manager/fm-icon-view.c =================================================================== RCS file: /cvs/gnome/nautilus/src/file-manager/fm-icon-view.c,v retrieving revision 1.278 diff -u -r1.278 fm-icon-view.c --- src/file-manager/fm-icon-view.c 7 Apr 2003 11:56:04 -0000 1.278 +++ src/file-manager/fm-icon-view.c 3 May 2003 23:21:43 -0000 @@ -98,10 +98,12 @@ #define COMMAND_TIGHTER_LAYOUT "/commands/Tighter Layout" #define COMMAND_SORT_REVERSED "/commands/Reversed Order" #define COMMAND_CLEAN_UP "/commands/Clean Up" +#define COMMAND_KEEP_ALIGNED "/commands/Keep Aligned" #define ID_MANUAL_LAYOUT "Manual Layout" #define ID_TIGHTER_LAYOUT "Tighter Layout" #define ID_SORT_REVERSED "Reversed Order" +#define ID_KEEP_ALIGNED "Keep Aligned" typedef struct { NautilusFileSortType sort_type; @@ -578,6 +580,16 @@ supports_auto_layout, (view)); } +static gboolean +fm_icon_view_supports_keep_aligned (FMIconView *view) +{ + g_return_val_if_fail (FM_IS_ICON_VIEW (view), FALSE); + + return EEL_CALL_METHOD_WITH_RETURN_VALUE + (FM_ICON_VIEW_CLASS, view, + supports_keep_aligned, (view)); +} + static void update_layout_menus (FMIconView *view) { @@ -617,6 +629,18 @@ nautilus_bonobo_set_sensitive (view->details->ui, COMMAND_CLEAN_UP, !is_auto_layout); + + nautilus_bonobo_set_hidden (view->details->ui, + COMMAND_KEEP_ALIGNED, + !fm_icon_view_supports_keep_aligned (view)); + + nautilus_bonobo_set_toggle_state + (view->details->ui, COMMAND_KEEP_ALIGNED, + nautilus_icon_container_is_keep_aligned (get_icon_container (view))); + + nautilus_bonobo_set_sensitive + (view->details->ui, COMMAND_KEEP_ALIGNED, !is_auto_layout); + bonobo_ui_component_thaw (view->details->ui, NULL); } @@ -753,6 +777,41 @@ sort_reversed); } +static gboolean +get_default_directory_keep_aligned (void) +{ + return TRUE; +} + +static gboolean +fm_icon_view_get_directory_keep_aligned (FMIconView *icon_view, + NautilusFile *file) +{ + if (!fm_icon_view_supports_keep_aligned (icon_view)) { + return FALSE; + } + + return nautilus_file_get_boolean_metadata + (file, + NAUTILUS_METADATA_KEY_ICON_VIEW_KEEP_ALIGNED, + get_default_directory_keep_aligned ()); +} + +static void +fm_icon_view_set_directory_keep_aligned (FMIconView *icon_view, + NautilusFile *file, + gboolean keep_aligned) +{ + if (!fm_icon_view_supports_keep_aligned (icon_view)) { + return; + } + + nautilus_file_set_boolean_metadata + (file, NAUTILUS_METADATA_KEY_ICON_VIEW_KEEP_ALIGNED, + get_default_directory_keep_aligned (), + keep_aligned); +} + /* maintainence of auto layout boolean */ static gboolean default_directory_manual_layout = FALSE; @@ -880,6 +939,14 @@ } static gboolean +real_supports_keep_aligned (FMIconView *view) +{ + g_return_val_if_fail (FM_IS_ICON_VIEW (view), FALSE); + + return FALSE; +} + +static gboolean set_sort_reversed (FMIconView *icon_view, gboolean new_value) { if (icon_view->details->sort_reversed == new_value) { @@ -1005,6 +1072,9 @@ /* Set the sort direction from the metadata. */ set_sort_reversed (icon_view, fm_icon_view_get_directory_sort_reversed (icon_view, file)); + nautilus_icon_container_set_keep_aligned + (get_icon_container (icon_view), + fm_icon_view_get_directory_keep_aligned (icon_view, file)); nautilus_icon_container_set_tighter_layout (get_icon_container (icon_view), fm_icon_view_get_directory_tighter_layout (icon_view, file)); @@ -1287,6 +1357,37 @@ } static void +keep_aligned_state_changed_callback (BonoboUIComponent *component, + const char *path, + Bonobo_UIComponent_EventType type, + const char *state, + gpointer user_data) +{ + FMIconView *icon_view; + NautilusFile *file; + gboolean keep_aligned; + + g_assert (strcmp (path, ID_KEEP_ALIGNED) == 0); + + icon_view = FM_ICON_VIEW (user_data); + + if (strcmp (state, "") == 0) { + /* State goes blank when component is removed; ignore this. */ + return; + } + + keep_aligned = strcmp (state, "1") == 0 ? TRUE : FALSE; + + file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (icon_view)); + fm_icon_view_set_directory_keep_aligned (icon_view, + file, + keep_aligned); + + nautilus_icon_container_set_keep_aligned (get_icon_container (icon_view), + keep_aligned); +} + +static void switch_to_manual_layout (FMIconView *icon_view) { if (!fm_icon_view_using_auto_layout (icon_view)) { @@ -1393,6 +1494,7 @@ bonobo_ui_component_add_listener (icon_view->details->ui, ID_TIGHTER_LAYOUT, tighter_layout_state_changed_callback, view); bonobo_ui_component_add_listener (icon_view->details->ui, ID_SORT_REVERSED, sort_reversed_state_changed_callback, view); + bonobo_ui_component_add_listener (icon_view->details->ui, ID_KEEP_ALIGNED, keep_aligned_state_changed_callback, view); icon_view->details->menus_ready = TRUE; bonobo_ui_component_freeze (icon_view->details->ui, NULL); @@ -1472,6 +1574,8 @@ set_sort_criterion (icon_view, get_sort_criterion_by_sort_type (get_default_sort_order ())); set_sort_reversed (icon_view, get_default_sort_in_reverse_order ()); + nautilus_icon_container_set_keep_aligned + (icon_container, get_default_directory_keep_aligned ()); nautilus_icon_container_set_tighter_layout (icon_container, get_default_directory_tighter_layout ()); @@ -2520,6 +2624,7 @@ klass->clean_up = fm_icon_view_real_clean_up; klass->supports_auto_layout = real_supports_auto_layout; + klass->supports_keep_aligned = real_supports_keep_aligned; klass->get_directory_auto_layout = fm_icon_view_real_get_directory_auto_layout; klass->get_directory_sort_by = fm_icon_view_real_get_directory_sort_by; klass->get_directory_sort_reversed = fm_icon_view_real_get_directory_sort_reversed; Index: src/file-manager/fm-icon-view.h =================================================================== RCS file: /cvs/gnome/nautilus/src/file-manager/fm-icon-view.h,v retrieving revision 1.12 diff -u -r1.12 fm-icon-view.h --- src/file-manager/fm-icon-view.h 4 Feb 2003 10:36:21 -0000 1.12 +++ src/file-manager/fm-icon-view.h 3 May 2003 23:21:43 -0000 @@ -84,6 +84,12 @@ */ gboolean (* supports_auto_layout) (FMIconView *view); + /* supports_auto_layout is a function pointer that subclasses may + * override to control whether snap-to-grid mode + * should be enabled. The default implementation returns FALSE. + */ + gboolean (* supports_keep_aligned) (FMIconView *view); + }; /* GObject support */ Index: src/file-manager/nautilus-icon-view-ui.xml =================================================================== RCS file: /cvs/gnome/nautilus/src/file-manager/nautilus-icon-view-ui.xml,v retrieving revision 1.23 diff -u -r1.23 nautilus-icon-view-ui.xml --- src/file-manager/nautilus-icon-view-ui.xml 5 Jul 2002 20:37:24 -0000 1.23 +++ src/file-manager/nautilus-icon-view-ui.xml 3 May 2003 23:21:44 -0000 @@ -24,6 +24,9 @@ + @@ -78,6 +81,9 @@ + @@ -108,6 +114,7 @@ id="Sort by Emblems"/> + @@ -115,7 +122,11 @@ id="Reversed Order" type="toggle"/> + +