| 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 | |
|---|