/*! * \brief Move the list in the given direction. * * @param objects The objects to move * @param dia The diagram owning the objects * @param dir The direction to move to * @param step The step-width to move */ void object_list_nudge(GList *objects, Diagram *dia, Direction dir, real step) { Point *orig_pos; Point *dest_pos; guint nobjs, i; real inc_x, inc_y; GList *list; DiaObject *obj; if (!objects) return; g_return_if_fail (step > 0); nobjs = g_list_length (objects); g_return_if_fail (nobjs > 0); dest_pos = g_new(Point, nobjs); orig_pos = g_new(Point, nobjs); inc_x = DIR_RIGHT == dir ? step : (DIR_LEFT == dir ? -step : 0); inc_y = DIR_DOWN == dir ? step : (DIR_UP == dir ? -step : 0); /* remeber original positions and calculate destination */ i = 0; list = objects; while (list != NULL) { obj = (DiaObject *) list->data; orig_pos[i] = obj->position; dest_pos[i].x = orig_pos[i].x + inc_x; dest_pos[i].y = orig_pos[i].y + inc_y; obj->ops->move(obj, &dest_pos[i]); ++i; list = g_list_next(list); } /* if anything is connected not anymore */ diagram_unconnect_selected(dia); undo_move_objects(dia, orig_pos, dest_pos, g_list_copy(objects)); }
static void modify_button_release(ModifyTool *tool, GdkEventButton *event, DDisplay *ddisp) { Point *dest_pos, to; GList *list; int i; ObjectChange *objchange; tool->break_connections = FALSE; ddisplay_set_all_cursor(default_cursor); /* remove position from status bar */ { GtkStatusbar *statusbar = GTK_STATUSBAR (ddisp->modified_status); guint context_id = gtk_statusbar_get_context_id (statusbar, "ObjectPos"); gtk_statusbar_pop (statusbar, context_id); } switch (tool->state) { case STATE_MOVE_OBJECT: /* Return to normal state */ gdk_pointer_ungrab (event->time); ddisplay_untransform_coords(ddisp, event->x, event->y, &to.x, &to.y); if (!modify_move_already(tool, ddisp, &to)) { tool->orig_pos = NULL; tool->state = STATE_NONE; return; } diagram_update_connections_selection(ddisp->diagram); if (tool->orig_pos != NULL) { /* consider the non-selected children affected */ list = parent_list_affected(ddisp->diagram->data->selected); dest_pos = g_new(Point, g_list_length(list)); i=0; while (list != NULL) { DiaObject *obj = (DiaObject *) list->data; dest_pos[i] = obj->position; list = g_list_next(list); i++; } undo_move_objects(ddisp->diagram, tool->orig_pos, dest_pos, parent_list_affected(ddisp->diagram->data->selected)); } ddisplay_connect_selected(ddisp); /* pushes UNDO info */ diagram_update_extents(ddisp->diagram); diagram_modified(ddisp->diagram); diagram_flush(ddisp->diagram); undo_set_transactionpoint(ddisp->diagram->undo); tool->orig_pos = NULL; tool->state = STATE_NONE; break; case STATE_MOVE_HANDLE: gdk_pointer_ungrab (event->time); tool->state = STATE_NONE; if (tool->orig_pos != NULL) { undo_move_handle(ddisp->diagram, tool->handle, tool->object, *tool->orig_pos, tool->last_to, gdk_event_to_dia_ModifierKeys(event->state)); } /* Final move: */ object_add_updates(tool->object, ddisp->diagram); objchange = tool->object->ops->move_handle(tool->object, tool->handle, &tool->last_to, NULL, HANDLE_MOVE_USER_FINAL,gdk_event_to_dia_ModifierKeys(event->state)); if (objchange != NULL) { undo_object_change(ddisp->diagram, tool->object, objchange); } object_add_updates(tool->object, ddisp->diagram); /* Connect if possible: */ if (tool->handle->connect_type != HANDLE_NONCONNECTABLE) { object_connect_display(ddisp, tool->object, tool->handle, TRUE); /* pushes UNDO info */ diagram_update_connections_selection(ddisp->diagram); } highlight_reset_all(ddisp->diagram); diagram_flush(ddisp->diagram); diagram_modified(ddisp->diagram); diagram_update_extents(ddisp->diagram); undo_set_transactionpoint(ddisp->diagram->undo); if (tool->orig_pos != NULL) { g_free(tool->orig_pos); tool->orig_pos = NULL; } break; case STATE_BOX_SELECT: gdk_pointer_ungrab (event->time); /* Remove last box: */ dia_interactive_renderer_set_selection (ddisp->renderer, FALSE, 0, 0, 0, 0); { GList *list, *list_to_free; list = list_to_free = find_selected_objects(ddisp, tool); if (selection_style == SELECT_REPLACE && !(event->state & GDK_SHIFT_MASK)) { /* Not Multi-select => Remove all selected */ diagram_remove_all_selected(ddisp->diagram, TRUE); } if (selection_style == SELECT_INTERSECTION) { GList *intersection = NULL; while (list != NULL) { DiaObject *obj = (DiaObject *)list->data; if (diagram_is_selected(ddisp->diagram, obj)) { intersection = g_list_append(intersection, obj); } list = g_list_next(list); } list = intersection; diagram_remove_all_selected(ddisp->diagram, TRUE); while (list != NULL) { DiaObject *obj = (DiaObject *)list->data; diagram_select(ddisp->diagram, obj); list = g_list_next(list); } g_list_free(intersection); } else { while (list != NULL) { DiaObject *obj = (DiaObject *)list->data; if (selection_style == SELECT_REMOVE) { if (diagram_is_selected(ddisp->diagram, obj)) diagram_unselect_object(ddisp->diagram, obj); } else if (selection_style == SELECT_INVERT) { if (diagram_is_selected(ddisp->diagram, obj)) diagram_unselect_object(ddisp->diagram, obj); else diagram_select(ddisp->diagram, obj); } else { if (!diagram_is_selected(ddisp->diagram, obj)) diagram_select(ddisp->diagram, obj); } list = g_list_next(list); } } g_list_free(list_to_free); } ddisplay_do_update_menu_sensitivity(ddisp); ddisplay_flush(ddisp); tool->state = STATE_NONE; break; case STATE_NONE: break; default: message_error("Internal error: Strange state in modify_tool\n"); } }
/*! * \brief Align objects by moving then horizontally * * For each node in objects align them horizontally. The connections (edges) will follow. * * @param objects selection of objects to be considered * @param dia the diagram owning the objects (and holding undo information) * @param align the alignment algorithm */ void object_list_align_h(GList *objects, Diagram *dia, int align) { GList *list; Point *orig_pos; Point *dest_pos; real x_pos = 0; DiaObject *obj; Point pos; real left, right, freespc = 0; int nobjs; int i; GList *unconnected = NULL; filter_connected (objects, 1, NULL, &unconnected); objects = unconnected; if (objects==NULL) return; obj = (DiaObject *) objects->data; /* First object */ left = obj->bounding_box.left; right = obj->bounding_box.right; freespc = right - left; nobjs = 1; list = objects->next; while (list != NULL) { obj = (DiaObject *) list->data; if (obj->bounding_box.left < left) left = obj->bounding_box.left; if (obj->bounding_box.right > right) right = obj->bounding_box.right; freespc += obj->bounding_box.right - obj->bounding_box.left; nobjs++; list = g_list_next(list); } /* * These alignments can alter the order of elements, so we need * to sort them out by position. */ if (align == DIA_ALIGN_EQUAL || align == DIA_ALIGN_ADJACENT) { DiaObject **object_array = (DiaObject **)g_malloc(sizeof(DiaObject*)*nobjs); int i = 0; list = objects; while (list != NULL) { obj = (DiaObject *) list->data; object_array[i] = obj; i++; list = g_list_next(list); } qsort(object_array, nobjs, sizeof(DiaObject*), object_list_sort_horizontal); list = NULL; for (i = 0; i < nobjs; i++) { list = g_list_append(list, object_array[i]); } g_assert (objects == unconnected); g_list_free (unconnected); objects = unconnected = list; g_free(object_array); } switch (align) { case DIA_ALIGN_LEFT: x_pos = left; break; case DIA_ALIGN_CENTER: x_pos = (left + right)/2.0; break; case DIA_ALIGN_RIGHT: x_pos = right; break; case DIA_ALIGN_POSITION: x_pos = (left + right)/2.0; break; case DIA_ALIGN_EQUAL: freespc = (right - left - freespc)/(double)(nobjs - 1); x_pos = left; break; case DIA_ALIGN_ADJACENT: x_pos = left; break; default: message_warning("Wrong argument to object_list_align_h()\n"); } dest_pos = g_new(Point, nobjs); orig_pos = g_new(Point, nobjs); i = 0; list = objects; while (list != NULL) { obj = (DiaObject *) list->data; switch (align) { case DIA_ALIGN_LEFT: pos.x = x_pos + obj->position.x - obj->bounding_box.left; break; case DIA_ALIGN_CENTER: pos.x = x_pos + obj->position.x - (obj->bounding_box.left + obj->bounding_box.right)/2.0; break; case DIA_ALIGN_RIGHT: pos.x = x_pos - (obj->bounding_box.right - obj->position.x); break; case DIA_ALIGN_POSITION: pos.x = x_pos; break; case DIA_ALIGN_EQUAL: pos.x = x_pos + obj->position.x - obj->bounding_box.left; x_pos += obj->bounding_box.right - obj->bounding_box.left + freespc; break; case DIA_ALIGN_ADJACENT: pos.x = x_pos + obj->position.x - obj->bounding_box.left; x_pos += obj->bounding_box.right - obj->bounding_box.left; break; } pos.y = obj->position.y; orig_pos[i] = obj->position; dest_pos[i] = pos; obj->ops->move(obj, &pos); i++; list = g_list_next(list); } undo_move_objects(dia, orig_pos, dest_pos, g_list_copy(objects)); g_list_free (unconnected); }
/*! * \brief Align objects at their connected points * * The result of the algorithm depends on the order of objects * in the list. The list is typically coming from the selection. * That order depends on the kind of selection performed. * - selection by rubberband gives objects in reverse order of * the the original layer order as of this writing * - Select/Transitive lets the order follow the connection order, * but still reversed due data_select() prepending * - Step-wise manual selection would also be in reverse order * So it appears to be a good idea to reverse the processing order * In this function to minimize surpise. * * The result also currently depends on the direction of the connections. * When aligning two objects the one connected with HANDLE_MOVE_ENDPOINT * will be moved. This might be contradictory to the selection order. * * @param objects selection of objects to be considered * @param dia the diagram owning the objects (and holding undo information) * @param align unused */ void object_list_align_connected (GList *objects, Diagram *dia, int align) { GList *list; Point *orig_pos; Point *dest_pos; DiaObject *obj, *o2; int i, nobjs; GList *connected = NULL; GList *to_be_moved = NULL; GList *movelist = NULL; /* find all elements to be moved directly */ filter_connected (objects, 2, &connected, &to_be_moved); dia_log_message ("Moves %d - Connections %d\n", g_list_length (to_be_moved), g_list_length (connected)); /* for every connection check: * - "matching" directions of both object connection points (this also gives * the direction of the move of the second object) * - * - move every object only once */ nobjs = g_list_length (to_be_moved); orig_pos = g_new (Point, nobjs); dest_pos = g_new (Point, nobjs); list = g_list_reverse (connected); while (list != NULL) { DiaObject *con = list->data; Handle *h1 = NULL, *h2 = NULL; g_assert (con->num_handles >= 2); for (i = 0; i < con->num_handles; ++i) { if (con->handles[i]->id == HANDLE_MOVE_STARTPOINT) h1 = con->handles[i]; else if (con->handles[i]->id == HANDLE_MOVE_ENDPOINT) h2 = con->handles[i]; } /* should this be an assert? */ if (h1 && h2 && h1->connected_to && h2->connected_to) { ConnectionPoint *cps = h1->connected_to; ConnectionPoint *cpe = h2->connected_to; obj = cps->object; o2 = cpe->object; /* swap alignment direction if we are working backwards by the selection order */ if (g_list_index (to_be_moved, obj) < g_list_index (to_be_moved, o2)) { DiaObject *otmp = o2; ConnectionPoint *cptmp = cpe; o2 = obj; obj = otmp; cpe = cps; cps = cptmp; } if ( !g_list_find (movelist, o2) && g_list_find(to_be_moved, o2) && g_list_find(to_be_moved, obj)) { Point delta = {0, 0}; /* If we haven't moved it yet, check if we want to */ int hweight = 0; int vweight = 0; if (cps->directions == DIR_NORTH || cps->directions == DIR_SOUTH) ++vweight; else if (cps->directions == DIR_EAST || cps->directions == DIR_WEST) ++hweight; if (cpe->directions == DIR_NORTH || cpe->directions == DIR_SOUTH) ++vweight; else if (cpe->directions == DIR_EAST || cpe->directions == DIR_WEST) ++hweight; /* One clear directions is required to move at all */ if (vweight > hweight) { /* horizontal move */ delta.x = cps->pos.x - cpe->pos.x; } else if (hweight > vweight) { /* vertical move */ delta.y = cps->pos.y - cpe->pos.y; } else { /* would need more context */ char dirs[] = "NESW"; int j; for (j = 0; j < 4; ++j) if (cps->directions & (1<<j)) g_print ("%c", dirs[j]); g_print ("(%s) -> ", obj->type->name); for (j = 0; j < 4; ++j) if (cpe->directions & (1<<j)) g_print ("%c", dirs[j]); g_print ("(%s)\n", o2->type->name); } if (delta.x != 0.0 || delta.y != 0) { Point pos = o2->position; point_add (&pos, &delta); i = g_list_length (movelist); orig_pos[i] = o2->position; dest_pos[i] = pos; dia_log_message ("Move '%s' by %g,%g\n", o2->type->name, delta.x, delta.y); #if 0 o2->ops->move (o2, &pos); #else { GList *move_list = g_list_append (NULL, o2); object_list_move_delta (move_list, &delta); g_list_free (move_list); } #endif diagram_update_connections_object (dia, o2, TRUE); movelist = g_list_append (movelist, o2); } } } list = g_list_next (list); } /* eating all the passed in parameters */ undo_move_objects (dia, orig_pos, dest_pos, movelist); g_list_free (to_be_moved); g_list_free (connected); }
/*! * \brief Align objects by moving them vertically * * For each node in objects align them vertically. The connections (edges) will follow. * * @param objects selection of objects to be considered * @param dia the diagram owning the objects (and holding undo information) * @param align the alignment algorithm */ void object_list_align_v(GList *objects, Diagram *dia, int align) { GList *list; Point *orig_pos; Point *dest_pos; real y_pos = 0; DiaObject *obj; Point pos; real top, bottom, freespc; int nobjs; int i; GList *unconnected = NULL; filter_connected (objects, 1, NULL, &unconnected); objects = unconnected; if (objects==NULL) return; obj = (DiaObject *) objects->data; /* First object */ top = obj->bounding_box.top; bottom = obj->bounding_box.bottom; freespc = bottom - top; nobjs = 1; list = objects->next; while (list != NULL) { obj = (DiaObject *) list->data; if (obj->bounding_box.top < top) top = obj->bounding_box.top; if (obj->bounding_box.bottom > bottom) bottom = obj->bounding_box.bottom; freespc += obj->bounding_box.bottom - obj->bounding_box.top; nobjs++; list = g_list_next(list); } /* * These alignments can alter the order of elements, so we need * to sort them out by position. */ if (align == DIA_ALIGN_EQUAL || align == DIA_ALIGN_ADJACENT) { DiaObject **object_array = (DiaObject **)g_malloc(sizeof(DiaObject*)*nobjs); int i = 0; list = objects; while (list != NULL) { obj = (DiaObject *) list->data; object_array[i] = obj; i++; list = g_list_next(list); } qsort(object_array, nobjs, sizeof(DiaObject*), object_list_sort_vertical); list = NULL; for (i = 0; i < nobjs; i++) { list = g_list_append(list, object_array[i]); } g_assert (objects == unconnected); g_list_free (unconnected); objects = unconnected = list; g_free(object_array); } switch (align) { case DIA_ALIGN_TOP: /* TOP */ y_pos = top; break; case DIA_ALIGN_CENTER: /* CENTER */ y_pos = (top + bottom)/2.0; break; case DIA_ALIGN_BOTTOM: /* BOTTOM */ y_pos = bottom; break; case DIA_ALIGN_POSITION: /* OBJECT POSITION */ y_pos = (top + bottom)/2.0; break; case DIA_ALIGN_EQUAL: /* EQUAL DISTANCE */ freespc = (bottom - top - freespc)/(double)(nobjs - 1); y_pos = top; break; case DIA_ALIGN_ADJACENT: /* ADJACENT */ y_pos = top; break; default: message_warning("Wrong argument to object_list_align_v()\n"); } dest_pos = g_new(Point, nobjs); orig_pos = g_new(Point, nobjs); i = 0; list = objects; while (list != NULL) { obj = (DiaObject *) list->data; pos.x = obj->position.x; switch (align) { case DIA_ALIGN_TOP: /* TOP */ pos.y = y_pos + obj->position.y - obj->bounding_box.top; break; case DIA_ALIGN_CENTER: /* CENTER */ pos.y = y_pos + obj->position.y - (obj->bounding_box.top + obj->bounding_box.bottom)/2.0; break; case DIA_ALIGN_BOTTOM: /* BOTTOM */ pos.y = y_pos - (obj->bounding_box.bottom - obj->position.y); break; case DIA_ALIGN_POSITION: /* OBJECT POSITION */ pos.y = y_pos; break; case DIA_ALIGN_EQUAL: /* EQUAL DISTANCE */ pos.y = y_pos + obj->position.y - obj->bounding_box.top; y_pos += obj->bounding_box.bottom - obj->bounding_box.top + freespc; break; case DIA_ALIGN_ADJACENT: /* ADJACENT */ pos.y = y_pos + obj->position.y - obj->bounding_box.top; y_pos += obj->bounding_box.bottom - obj->bounding_box.top; break; } pos.x = obj->position.x; orig_pos[i] = obj->position; dest_pos[i] = pos; obj->ops->move(obj, &pos); i++; list = g_list_next(list); } undo_move_objects(dia, orig_pos, dest_pos, g_list_copy(objects)); g_list_free (unconnected); }