1 | // =============================================================== // |
---|
2 | // // |
---|
3 | // File : ED4_container.cxx // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Institute of Microbiology (Technical University Munich) // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // =============================================================== // |
---|
10 | |
---|
11 | #include <arbdb.h> |
---|
12 | #include "ed4_class.hxx" |
---|
13 | #include <aw_msg.hxx> |
---|
14 | #include <aw_question.hxx> |
---|
15 | |
---|
16 | ED4_returncode ED4_container::search_target_species(ED4_extension *location, ED4_properties prop, ED4_base **found_member, ED4_level return_level) { |
---|
17 | // who's extension falls within the given |
---|
18 | // location thereby considering orientation given by |
---|
19 | // prop (only pos[] is relevant) list has to be |
---|
20 | // ordered in either x- or y- direction, ( binary |
---|
21 | // search algorithm later ) returns index of found member, |
---|
22 | // -1 if list is empty or no_of_members if search reached end of list |
---|
23 | |
---|
24 | |
---|
25 | if (!members()) { // there's no list |
---|
26 | return ED4_R_IMPOSSIBLE; |
---|
27 | } |
---|
28 | |
---|
29 | ED4_base *current_member = member(0); |
---|
30 | // case for the device_manager: |
---|
31 | if (current_member->is_area_manager()) { |
---|
32 | current_member->to_area_manager() |
---|
33 | ->get_multi_species_manager() |
---|
34 | ->search_target_species(location, prop, found_member, return_level); // there are always the three areas !!! |
---|
35 | |
---|
36 | if (*found_member) { |
---|
37 | return ED4_R_OK; |
---|
38 | } |
---|
39 | } |
---|
40 | |
---|
41 | ED4_index rel_pos; // relative position, i.e. on screen, to check |
---|
42 | ED4_index rel_size; |
---|
43 | AW_pos abs_pos; // relative size of object, to check |
---|
44 | |
---|
45 | { |
---|
46 | AW_pos abs_pos_x = 0; |
---|
47 | AW_pos abs_pos_y = 0; |
---|
48 | current_member->parent->calc_world_coords(&abs_pos_x, &abs_pos_y); |
---|
49 | |
---|
50 | // set extension-indexes rel_pos, rel_size and abs_pos according to properties: |
---|
51 | if (prop & PROP_HORIZONTAL) { |
---|
52 | rel_pos = Y_POS; |
---|
53 | rel_size = HEIGHT; |
---|
54 | abs_pos = abs_pos_y; |
---|
55 | } |
---|
56 | else { // i.e. prop & PROP_VERTICAL |
---|
57 | rel_pos = X_POS; |
---|
58 | rel_size = WIDTH; |
---|
59 | abs_pos = abs_pos_x; |
---|
60 | } |
---|
61 | } |
---|
62 | |
---|
63 | ED4_index current_index = 0; |
---|
64 | |
---|
65 | while (current_member && |
---|
66 | (location->position[rel_pos] >= (current_member->extension.position[rel_pos] + abs_pos)) && // just as long as possibility exists, to find the object |
---|
67 | (location->position[rel_pos] >= abs_pos && location->position[rel_pos] <= current_member->parent->extension.size[rel_size] + abs_pos)) |
---|
68 | { |
---|
69 | e4_assert(!current_member->is_root_group_manager()); |
---|
70 | if (current_member->is_group_manager() && |
---|
71 | !current_member->flag.hidden && |
---|
72 | !current_member->is_consensus_manager()) { // search_clicked_member for multi_species_manager in groups |
---|
73 | current_member->to_group_manager()->search_target_species(location, prop, found_member, return_level); |
---|
74 | } |
---|
75 | else if (!(current_member->flag.hidden) && |
---|
76 | (location->position[rel_pos] <= (current_member->extension.position[rel_pos] + |
---|
77 | abs_pos + current_member->extension.size[rel_size]))) // found a suitable member |
---|
78 | { |
---|
79 | if (return_level & LEV_MULTI_SPECIES) { // search for drag target |
---|
80 | if (current_member->is_multi_species_manager()) { |
---|
81 | *found_member = current_member; // we have to return the multi_species_manager for insertion |
---|
82 | current_member->to_multi_species_manager()->search_target_species(location, prop, found_member, return_level); |
---|
83 | } |
---|
84 | else if ((current_member->is_spacer_terminal()) && (current_index + 1 == no_of_members)) { // if we have found the last spacer in a group |
---|
85 | // -> we can move the object behind the group |
---|
86 | *found_member = current_member->get_parent(LEV_MULTI_SPECIES); |
---|
87 | if ((*found_member) && !((*found_member)->parent->is_area_manager())) { |
---|
88 | *found_member = (*found_member)->parent->get_parent(LEV_MULTI_SPECIES); |
---|
89 | } |
---|
90 | } |
---|
91 | else if (!(current_member->is_terminal()) || (current_member->is_spacer_terminal())) { |
---|
92 | *found_member = current_member->get_parent(LEV_MULTI_SPECIES); |
---|
93 | } |
---|
94 | |
---|
95 | } |
---|
96 | else { // search for drag target line |
---|
97 | if (current_member->is_multi_species_manager()) { |
---|
98 | current_member->to_multi_species_manager()->search_target_species(location, prop, found_member, return_level); |
---|
99 | } |
---|
100 | else if ((current_member->is_spacer_terminal()) && (current_index + 1 == no_of_members)) { // if we have found the last spacer |
---|
101 | *found_member = current_member->get_parent(LEV_MULTI_SPECIES); // in a group we can move the |
---|
102 | if ((*found_member) && !((*found_member)->parent->is_area_manager())) { |
---|
103 | *found_member = (*found_member)->parent; |
---|
104 | } |
---|
105 | } |
---|
106 | else if ((current_member->is_species_manager()) || |
---|
107 | ((current_member->is_spacer_terminal()) && current_member->parent->is_multi_species_manager())) { // we are a species manager |
---|
108 | *found_member = current_member; |
---|
109 | } |
---|
110 | } |
---|
111 | |
---|
112 | } |
---|
113 | |
---|
114 | current_index++; // no hit => continue search |
---|
115 | current_member = existing_index(current_index) ? member(current_index) : NULp; |
---|
116 | |
---|
117 | if (current_member) { // handle folding groups |
---|
118 | ED4_index old_index = current_index; |
---|
119 | while (current_member && current_member->flag.hidden && current_index!=no_of_members) { |
---|
120 | current_index++; |
---|
121 | current_member = existing_index(current_index) ? member(current_index) : NULp; |
---|
122 | } |
---|
123 | |
---|
124 | if (current_index != old_index) { |
---|
125 | if (current_member && |
---|
126 | !((location->position[rel_pos] >= (current_member->extension.position[rel_pos] + abs_pos)) && |
---|
127 | (location->position[rel_pos] >= abs_pos && location->position[rel_pos] <= current_member->parent->extension.size[rel_size] + abs_pos)) && |
---|
128 | (current_member->is_spacer_terminal()) && (current_index + 1 == no_of_members)) |
---|
129 | { |
---|
130 | if (return_level & LEV_MULTI_SPECIES) |
---|
131 | *found_member = current_member->get_parent(LEV_MULTI_SPECIES)->parent->get_parent(LEV_MULTI_SPECIES); |
---|
132 | else |
---|
133 | *found_member = current_member->get_parent(LEV_MULTI_SPECIES)->parent; |
---|
134 | } |
---|
135 | } |
---|
136 | } |
---|
137 | |
---|
138 | if (current_member) { |
---|
139 | if (current_member->is_area_manager()) { |
---|
140 | current_member->to_area_manager() |
---|
141 | ->get_multi_species_manager() |
---|
142 | ->search_target_species(location, prop, found_member, return_level); // there are always the three areas !!! |
---|
143 | |
---|
144 | if (*found_member) { |
---|
145 | return ED4_R_OK; |
---|
146 | } |
---|
147 | } |
---|
148 | } |
---|
149 | } |
---|
150 | |
---|
151 | return ED4_R_OK; |
---|
152 | } |
---|
153 | |
---|
154 | void ED4_container::correct_insert_position(ED4_index& index) { |
---|
155 | // ensure to insert before group_end_spacer |
---|
156 | |
---|
157 | if (index>1 && // avoid underflow |
---|
158 | existing_index(index-1) && |
---|
159 | member(index-1)->is_spacer_terminal() && |
---|
160 | !owner()->is_device_manager()) // only in group_manager |
---|
161 | { |
---|
162 | index--; |
---|
163 | } |
---|
164 | } |
---|
165 | |
---|
166 | void ED4_container::resize(ED4_index needed_size) { |
---|
167 | if (needed_size>size_of_list) { |
---|
168 | e4_assert(needed_size>0); |
---|
169 | ED4_index new_size = (needed_size*3)/2+2; |
---|
170 | ARB_recalloc(memberList, size_of_list, new_size); |
---|
171 | size_of_list = new_size; |
---|
172 | } |
---|
173 | } |
---|
174 | |
---|
175 | void ED4_container::insert_member(ED4_base *new_member) { |
---|
176 | // inserts a new member into current owners's member array and |
---|
177 | // asks to adjust owner's bounding box |
---|
178 | |
---|
179 | ED4_properties prop = owner()->spec.static_prop; // properties of parent object |
---|
180 | |
---|
181 | ED4_index index; |
---|
182 | if ((index = search_member(&(new_member->extension), prop)) < 0) { // search list for a suitable position |
---|
183 | index = 0; // list was empty |
---|
184 | } |
---|
185 | else if (index != no_of_members) { // we want to insert new member just behind found position |
---|
186 | index++; // if index == no_of_members we reached the end of the list |
---|
187 | } // and index already has the right value |
---|
188 | |
---|
189 | correct_insert_position(index); // insert before end-spacer |
---|
190 | shift_list(index, 1); // shift members if necessary |
---|
191 | |
---|
192 | e4_assert(index<size_of_list); |
---|
193 | memberList[index] = new_member; |
---|
194 | no_of_members ++; |
---|
195 | new_member->index = index; |
---|
196 | |
---|
197 | owner()->request_resize(); // tell owner about resize |
---|
198 | } |
---|
199 | |
---|
200 | void ED4_container::append_member(ED4_base *new_member) { |
---|
201 | ED4_index index = no_of_members; |
---|
202 | |
---|
203 | e4_assert(owner()->spec.allowed_to_contain(new_member->spec.level)); |
---|
204 | |
---|
205 | correct_insert_position(index); // insert before end-spacer |
---|
206 | shift_list(index, 1); // shift members if necessary |
---|
207 | |
---|
208 | e4_assert(index<size_of_list); |
---|
209 | memberList[index] = new_member; |
---|
210 | no_of_members++; |
---|
211 | new_member->index = index; |
---|
212 | |
---|
213 | owner()->spec.announce_added(new_member->spec.level); |
---|
214 | owner()->request_resize(); |
---|
215 | } |
---|
216 | |
---|
217 | ED4_returncode ED4_container::remove_member(ED4_base *member_to_del) { |
---|
218 | if (!member_to_del || (no_of_members <= 0)) { |
---|
219 | return ED4_R_IMPOSSIBLE; |
---|
220 | } |
---|
221 | |
---|
222 | ED4_index index = member_to_del->index; |
---|
223 | e4_assert(member_to_del->parent == this); |
---|
224 | |
---|
225 | shift_list(index+1, -1); // shift member list to left, starting at index+1 |
---|
226 | |
---|
227 | member_to_del->parent = NULp; // avoid referencing wrong parent |
---|
228 | no_of_members--; |
---|
229 | e4_assert(members_ok()); |
---|
230 | |
---|
231 | owner()->request_resize(); |
---|
232 | |
---|
233 | return ED4_R_OK; |
---|
234 | } |
---|
235 | |
---|
236 | void ED4_container::shift_list(ED4_index start_index, int length) { |
---|
237 | // shifts member_list of current object by |length| positions starting with start_index, |
---|
238 | // if length is positive => shift to the right; allocates new memory if necessary |
---|
239 | // if length is negative => shift to the left (down to position 0); does not resize allocated memory |
---|
240 | |
---|
241 | e4_assert(length != 0); |
---|
242 | |
---|
243 | if (length>0) { // shift list to the right |
---|
244 | long needed_size = no_of_members + length; |
---|
245 | |
---|
246 | e4_assert(start_index<needed_size); |
---|
247 | resize(needed_size); |
---|
248 | |
---|
249 | for (ED4_index n = needed_size-1; n > start_index; n--) { // start shifting to the right |
---|
250 | ED4_index o = n-length; |
---|
251 | |
---|
252 | e4_assert(valid_index(n)); |
---|
253 | e4_assert(valid_index(o)); |
---|
254 | |
---|
255 | memberList[n] = memberList[o]; |
---|
256 | if (memberList[n]) { |
---|
257 | memberList[n]->index = n; |
---|
258 | } |
---|
259 | memberList[o] = NULp; |
---|
260 | } |
---|
261 | } |
---|
262 | else if (length<0) { // shift list to the left, thereby not freeing any memory ! |
---|
263 | e4_assert((start_index + length) >= 0); // invalid shift! |
---|
264 | |
---|
265 | for (ED4_index n = start_index + length; n < (no_of_members + length); n++) { // start shifting left |
---|
266 | ED4_index o = n-length; // Note: length is negative! |
---|
267 | |
---|
268 | e4_assert(valid_index(n)); |
---|
269 | e4_assert(valid_index(o)); |
---|
270 | |
---|
271 | memberList[n] = memberList[o]; |
---|
272 | if (memberList[n]) { |
---|
273 | memberList[n]->index = n; |
---|
274 | } |
---|
275 | memberList[o] = NULp; |
---|
276 | } |
---|
277 | } |
---|
278 | } |
---|
279 | |
---|
280 | |
---|
281 | ED4_returncode ED4_container::move_member(ED4_index old_pos, ED4_index new_pos) { |
---|
282 | if (old_pos>=0 && old_pos<no_of_members && new_pos>=0 && new_pos<no_of_members) { |
---|
283 | if (new_pos!=old_pos) { |
---|
284 | ED4_base *moved_member = memberList[old_pos]; |
---|
285 | |
---|
286 | shift_list(old_pos+1, -1); |
---|
287 | shift_list(new_pos, 1); |
---|
288 | |
---|
289 | memberList[new_pos] = moved_member; |
---|
290 | moved_member->index = new_pos; |
---|
291 | |
---|
292 | e4_assert(members_ok()); |
---|
293 | } |
---|
294 | return ED4_R_OK; |
---|
295 | } |
---|
296 | |
---|
297 | return ED4_R_IMPOSSIBLE; |
---|
298 | } |
---|
299 | |
---|
300 | ED4_index ED4_container::search_member(ED4_extension *location, ED4_properties prop) { |
---|
301 | /* searches member_list of current object for a member who's extension falls within the given location |
---|
302 | * thereby considering orientation given by prop (only pos[] is relevant) |
---|
303 | * list has to be ordered in either x- or y- direction, ( binary search algorithm later ) |
---|
304 | * returns index of found member, -1 if list is empty or no_of_members if search reached end of list |
---|
305 | */ |
---|
306 | ED4_index current_index = 0, |
---|
307 | rel_pos, |
---|
308 | rel_size; |
---|
309 | |
---|
310 | ED4_base *current_member; |
---|
311 | |
---|
312 | if (prop & PROP_HORIZONTAL) { // set extension-indexes rel_pos and rel_size according to properties |
---|
313 | rel_pos = Y_POS; rel_size = HEIGHT; |
---|
314 | } |
---|
315 | else { // i.e. prop & PROP_VERTICAL |
---|
316 | rel_pos = X_POS; rel_size = WIDTH; |
---|
317 | } |
---|
318 | |
---|
319 | current_member = memberList[0]; // search list |
---|
320 | if (! current_member) { // there's no list |
---|
321 | return -1; |
---|
322 | } |
---|
323 | |
---|
324 | current_index = 0; |
---|
325 | while (current_member) { // just as long as possibility exists, to find the object |
---|
326 | if (location->position[rel_pos] <= (current_member->extension.position[rel_pos] + current_member->extension.size[rel_size])) { // found a suitable member |
---|
327 | return current_index; |
---|
328 | } |
---|
329 | current_index++; // no hit => search on |
---|
330 | current_member = memberList[current_index]; |
---|
331 | } |
---|
332 | |
---|
333 | return no_of_members; // reached this position => no hit, return no_of_members |
---|
334 | } |
---|
335 | |
---|
336 | #ifdef ASSERTION_USED |
---|
337 | bool ED4_container::members_ok() const { |
---|
338 | bool error = false; |
---|
339 | for (int m=0; m<no_of_members; m++) { |
---|
340 | ED4_base *base = memberList[m]; |
---|
341 | |
---|
342 | if (base->index!=m) { |
---|
343 | printf("Member %i has illegal index %li\n", m, base->index); |
---|
344 | error = true; |
---|
345 | } |
---|
346 | } |
---|
347 | |
---|
348 | return !error; |
---|
349 | } |
---|
350 | #endif // ASSERTION_USED |
---|
351 | |
---|
352 | ED4_container::ED4_container() : |
---|
353 | no_of_members(0), |
---|
354 | size_of_list(1) |
---|
355 | { |
---|
356 | ARB_calloc(memberList, size_of_list); |
---|
357 | } |
---|
358 | |
---|
359 | |
---|
360 | void ED4_container::clear() { |
---|
361 | while (members()) { |
---|
362 | ED4_index last = members()-1; |
---|
363 | ED4_base *child = member(last); |
---|
364 | |
---|
365 | remove_member(child); |
---|
366 | child->parent = NULp; |
---|
367 | delete child; |
---|
368 | } |
---|
369 | } |
---|
370 | |
---|
371 | ED4_container::~ED4_container() { |
---|
372 | e4_assert(empty()); |
---|
373 | free(memberList); |
---|
374 | } |
---|
375 | |
---|