* lib/text.h: Added text_get_line() declaration
[dia.git] / lib / parent.c
blob434a4e09072a55899595072d57f1fefba0daa9a4
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 2003 Vadim Berezniker
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.
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.
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
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #include <config.h>
21 #include "geometry.h"
22 #include "object.h"
23 #include "parent.h"
24 #include <glib.h>
28 Takes a list of objects and appends all possible children (at any depth) to the list
30 Returns TRUE if the list didn't change
32 gboolean parent_list_expand(GList *obj_list)
34 gboolean nothing_affected = FALSE;
35 GList *list = obj_list;
36 while (list)
38 DiaObject *obj = (DiaObject *) list->data;
40 if (object_flags_set(obj, DIA_OBJECT_CAN_PARENT) && obj->children)
42 obj_list = g_list_concat(obj_list, g_list_copy(obj->children));
43 nothing_affected = FALSE;
46 list = g_list_next(list);
49 return nothing_affected;
52 /**
53 * Returns the original list minus any items that appear as children
54 * (at any depth) of the objects in the original list. This is very
55 * different from the parent_list_affected function, which returns a
56 * list of ALL objects affected.
58 * The caller must call g_list_free() on the returned list
59 * when the list is no longer needed.
62 GList *parent_list_affected_hierarchy(GList *obj_list)
64 GHashTable *object_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
65 GList *all_list = g_list_copy(obj_list);
66 GList *new_list = NULL, *list = all_list;
67 int orig_length = g_list_length(obj_list);
69 if (parent_list_expand(all_list)) /* fast way out */
70 return g_list_copy(obj_list);
72 /* enter all of the objects (except initial) in a hash */
73 list = g_list_nth(all_list, orig_length);
74 while (list)
76 g_hash_table_insert(object_hash, list->data, (void*) 1);
77 list = g_list_next(list);
80 list = obj_list;
81 while (list)
83 if (!g_hash_table_lookup(object_hash, list->data))
84 new_list = g_list_append(new_list, list->data);
86 list = g_list_next(list);
89 g_list_free(all_list);
90 g_hash_table_destroy(object_hash);
92 return new_list;
96 Finds all children of affected objects that are not in the list that should be
97 affected by an operation
99 The caller must call g_list_free() on the returned list
100 when the list is no longer needed
102 Any found affected children will be appended to the end of the list,
103 thus the front of the list is exactly the same as the passed in list.
106 GList *parent_list_affected(GList *obj_list)
108 GHashTable *object_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
109 GList *all_list = g_list_copy(obj_list);
110 GList *new_list = NULL, *list = all_list;
112 if (parent_list_expand(all_list)) /* fast way out */
113 return g_list_copy(obj_list);
115 /* eliminate duplicates */
116 list = all_list;
117 while (list)
119 DiaObject *obj = (DiaObject *) list->data;
120 if (!g_hash_table_lookup(object_hash, obj))
122 new_list = g_list_append(new_list, obj);
123 g_hash_table_insert(object_hash, obj, (void *) 1);
126 list = g_list_next(list);
129 g_list_free(all_list);
131 return new_list;
134 /* if the object is resizing up and hits its parents' border, prevent the resizing from
135 going any further
137 returns TRUE if resizing was obstructed*/
138 gboolean
139 parent_handle_move_out_check(DiaObject *object, Point *to)
141 Rectangle p_ext, c_ext;
142 Point new_delta;
144 if (!object->parent)
145 return FALSE;
147 parent_handle_extents(object->parent, &p_ext);
148 parent_point_extents(to, &c_ext);
150 new_delta = parent_move_child_delta(&p_ext, &c_ext, NULL);
151 point_add(to, &new_delta);
153 if (new_delta.x || new_delta.y)
154 return TRUE;
156 return FALSE;
159 /* if the object resizing down intersects a child, the resizing is prevented from going
160 any further
162 returns TRUE if resizing was obstructed
164 gboolean parent_handle_move_in_check(DiaObject *object, Point *to, Point *start_at)
166 GList *list = object->children;
167 gboolean once = TRUE;
168 Rectangle common_ext;
169 gboolean restricted = FALSE;
171 if (!object_flags_set(object, DIA_OBJECT_CAN_PARENT) || !object->children)
172 return FALSE;
174 while (list)
176 if (once) {
177 parent_handle_extents(list->data, &common_ext);
178 once = FALSE;
179 } else {
180 Rectangle c_ext;
181 parent_handle_extents (list->data, &c_ext);
182 rectangle_union(&common_ext, &c_ext);
184 list = g_list_next(list);
186 /* The start point gives the decision were to clip to:
187 * if it is below the child rect we need to clip to it's bottom.
188 * The check has nothing to do with 'to' being in the rectangle,
189 * but instead having the right barrier
191 if (start_at->y >= common_ext.bottom) {
192 if (to->y < common_ext.bottom)
193 to->y = common_ext.bottom, restricted = TRUE;
194 } else if (start_at->y <= common_ext.top) {
195 if (to->y > common_ext.top)
196 to->y = common_ext.top, restricted = TRUE;
199 if (start_at->x >= common_ext.right) {
200 if (to->x < common_ext.right)
201 to->x = common_ext.right, restricted = TRUE;
202 } else if (start_at->x <= common_ext.left) {
203 if (to->x > common_ext.left)
204 to->x = common_ext.left, restricted = TRUE;
207 return restricted;
210 /* p_ext are the "extents" of the parent and c_ext are the "extents" of the child
211 The extent is a rectangle that specifies how far an object goes on the grid (based on its handles)
212 If delta is not present, these are the extents *before* any moves
213 If delta is present, delta is considered into the extents's position
215 Point parent_move_child_delta(Rectangle *p_ext, Rectangle *c_ext, Point *delta)
217 Point new_delta = {0, 0};
218 gboolean free_delta = FALSE;
219 if (delta == NULL)
221 delta = g_new0(Point, 1);
222 free_delta = TRUE;
224 /* we check if the child extent would be inside the parent extent after the move
225 if not, we calculate how far we have to move the extent back to place it back
226 inside the parent extent */
227 if (c_ext->left + delta->x < p_ext->left )
228 new_delta.x = p_ext->left - (c_ext->left + delta->x);
229 else if (c_ext->left + delta->x + (c_ext->right - c_ext->left) > p_ext->right)
230 new_delta.x = p_ext->right - (c_ext->left + delta->x + (c_ext->right - c_ext->left));
232 if (c_ext->top + delta->y < p_ext->top)
233 new_delta.y = p_ext->top - (c_ext->top + delta->y);
234 else if (c_ext->top + delta->y + (c_ext->bottom - c_ext->top) > p_ext->bottom)
235 new_delta.y = p_ext->bottom - (c_ext->top + delta->y + (c_ext->bottom - c_ext->top));
237 if (free_delta)
238 g_free(delta);
240 return new_delta;
243 /* the caller must free the returned rectangle */
244 void
245 parent_point_extents(Point *point, Rectangle *extents)
247 extents->left = point->x;
248 extents->right = point->x;
249 extents->top = point->y;
250 extents->bottom = point->y;
254 * the caller must provide the 'returned' rectangle,
255 * which is initialized to the biggest rectangle containing
256 * all the objects handles
258 gboolean
259 parent_handle_extents(DiaObject *obj, Rectangle *extents)
261 int idx;
262 coord *left_most = NULL, *top_most = NULL, *bottom_most = NULL, *right_most = NULL;
264 if (obj->num_handles == 0)
265 return FALSE;
267 for (idx = 0; idx < obj->num_handles; idx++)
269 Handle *handle = obj->handles[idx];
271 if (!left_most || *left_most > handle->pos.x)
272 left_most = &handle->pos.x;
273 if (!right_most || *right_most < handle->pos.x)
274 right_most = &handle->pos.x;
275 if (!top_most || *top_most > handle->pos.y)
276 top_most = &handle->pos.y;
277 if (!bottom_most || *bottom_most < handle->pos.y)
278 bottom_most = &handle->pos.y;
281 extents->left = *left_most;
282 extents->right = *right_most;
283 extents->top = *top_most;
284 extents->bottom = *bottom_most;
286 return TRUE;
289 /** Apply a function to all children of the given object (recursively,
290 * depth-first).
291 * @param obj A parent object.
292 * @param function A function that takes a single DiaObject as an argument.
294 void
295 parent_apply_to_children(DiaObject *obj, DiaObjectFunc function)
297 GList *children;
298 for (children = obj->children; children != NULL; children = g_list_next(children)) {
299 DiaObject *child = children->data;
300 (*function)(child);
301 parent_apply_to_children(child, function);