split.cpp (9861B)
1 // Copyright (c) 2003 - 2009 Anselm R Garbe <anselm@garbe.us> 2 // See LICENSE for license details. 3 4 extern "C" { 5 #include <assert.h> 6 } 7 8 #include <cmath> 9 10 #include "split.h" 11 12 #include "frame.h" 13 #include "logger.h" 14 #include "rectangle.h" 15 #include "tree.h" 16 #include "util.h" 17 18 Tree *Split::topMostRoot(Tree *first, Tree *last) { 19 20 assert(first); 21 assert(last); 22 23 Tree *tmp = first; 24 while (tmp->parent()) { 25 tmp = tmp->parent(); 26 tmp->setVisit(tmp->visits() + 1); 27 } 28 29 tmp = last; 30 while (tmp->parent()) { 31 tmp = tmp->parent(); 32 tmp->setVisit(tmp->visits() + 1); 33 } 34 35 // now all tree nodes have been marked, let's 36 // search for the first tree node with two visits, 37 // this is the topmost root of first an last 38 39 Tree *result = first; 40 while (result->visits() < 2) { 41 result = result->parent(); 42 } 43 44 // we got the result, now cleanup on both paths 45 tmp = first; 46 while (tmp->parent()) { 47 tmp = tmp->parent(); 48 tmp->setVisit(0); 49 } 50 51 tmp = last; 52 while (tmp->parent()) { 53 tmp = tmp->parent(); 54 tmp->setVisit(0); 55 } 56 57 return result; 58 } 59 60 // applied patch by Dr. J. Pfefferl 61 void Split::resize(Tree *root, Tree *origin, 62 Direction dir, int stepW, int stepH) 63 { 64 int newW, newH; 65 66 if (!origin->frame()) { 67 return; 68 } 69 70 Tree *neighbor = Split::neighbor(root, origin, dir); 71 if (!neighbor|| !neighbor->parent()) { 72 return; 73 } 74 75 // Normalize stepH/W to positive numbers 76 // This is necessary to guarantee that the boundary checks 77 // for newW/newH later on work under all circumstances! 78 if(0 > stepH || 0 > stepW) { 79 stepH *= -1; 80 stepW *= -1; 81 // Reverse the grow/shrink directions accordingly 82 switch(dir) { 83 case LEFT: dir = RIGHT; break; 84 case RIGHT: dir = LEFT; break; 85 case UP: dir = DOWN; break; 86 case DOWN: dir = UP; break; 87 default: break; // Should not happen 88 } 89 } 90 91 // neighbor is the nearest neighbor, but this can be 92 // a subtree of the top-level neighbor, so we've to 93 // determine the top-level neighbor 94 Tree *parent = topMostRoot(origin, neighbor); 95 96 // determine the correct root for resizing and if we 97 // should grow or shrink the origin 98 Tree *growTree, *shrinkTree; 99 if ((dir == LEFT) || (dir == UP)) { 100 // shrink 101 growTree = parent->last(); 102 shrinkTree = parent->first(); 103 } 104 else { 105 // grow 106 growTree = parent->first(); 107 shrinkTree = parent->last(); 108 } 109 110 switch (dir) { 111 case LEFT: 112 newW = shrinkTree->width() - stepW; 113 if (0 >= newW) { 114 return; 115 } 116 growTree->setX(growTree->x() - stepW); 117 growTree->setWidth(growTree->width() + stepW); 118 shrinkTree->setWidth(newW); 119 break; 120 case RIGHT: 121 newW = shrinkTree->width() - stepW; 122 if (0 >= newW) { 123 return; 124 } 125 growTree->setWidth(growTree->width() + stepW); 126 shrinkTree->setX(shrinkTree->x() + stepW); 127 shrinkTree->setWidth(newW); 128 break; 129 case UP: 130 newH = shrinkTree->height() - stepH; 131 if (0 >= newH) { 132 return; 133 } 134 growTree->setY(growTree->y() - stepH); 135 growTree->setHeight(growTree->height() + stepH); 136 shrinkTree->setHeight(newH); 137 break; 138 case DOWN: 139 newH = shrinkTree->height() - stepH; 140 if(0 >= newH) { 141 return; 142 } 143 growTree->setHeight(growTree->height() + stepH); 144 shrinkTree->setY(shrinkTree->y() + stepH); 145 shrinkTree->setHeight(newH); 146 break; 147 default: 148 break; 149 } 150 adjustSize(growTree, ((dir == LEFT) || (dir == RIGHT))); 151 adjustSize(shrinkTree, ((dir == LEFT) || (dir == RIGHT))); 152 } 153 154 int Split::distance(Tree *origin, Tree *target) { 155 156 int ox = origin->x() + origin->width() / 2; 157 int oy = origin->y() + origin->height() / 2; 158 int tx = target->x() + target->width() / 2; 159 int ty = target->y() + target->height() / 2; 160 161 return (int)sqrt((double)(((ox - tx) * (ox - tx)) + 162 ((oy - ty) * (oy - ty)))); 163 } 164 165 bool Split::isNeighbor(Tree *origin, Tree *target, Direction dir) { 166 167 switch (dir) { 168 case LEFT: 169 return origin->x() >= (int)(target->x() + target->width()); 170 break; 171 case RIGHT: 172 return (int)(origin->x() + origin->width()) <= target->x(); 173 break; 174 case UP: 175 return origin->y() >= (int)(target->y() + target->height()); 176 break; 177 case DOWN: 178 return (int)(origin->y() + origin->height()) <= target->y(); 179 break; 180 default: 181 return false; 182 break; 183 } 184 } 185 186 Tree *Split::neighbor(Tree *root, Tree *origin, Direction dir) { 187 188 if (root == 0) { 189 return 0; 190 } 191 192 Tree *result1 = neighbor(root->first(), origin, dir); 193 if (!result1 && root->first()) { 194 result1 = root->first(); 195 } 196 197 Tree *result2 = neighbor(root->last(), origin, dir); 198 if (!result2 && root->last()) { 199 result2 = root->last(); 200 } 201 202 bool res1 = result1 && (result1 != origin) 203 && isNeighbor(origin, result1, dir); 204 bool res2 = result2 && (result2 != origin) 205 && isNeighbor(origin, result2, dir); 206 207 if (res1 && res2) { 208 209 if (distance(origin, result1) 210 < distance(origin, result2)) 211 { 212 return result1; 213 } 214 else { 215 return result2; 216 } 217 } 218 else if (res1) { 219 return result1; 220 } 221 else if (res2) { 222 return result2; 223 } 224 225 return 0; 226 } 227 228 void Split::adjustSize(Tree *tree, bool ignoreVert) { 229 230 Tree *first = tree->first(); 231 Tree *last = tree->last(); 232 if (first) { 233 assert(last); 234 if (tree->isHoriz()) { 235 // horizontal split 236 if (ignoreVert) { 237 first->copy(tree); 238 first->setWidth(tree->width() / 2); 239 last->copy(first); 240 last->setX(first->x() + first->width()); 241 } 242 else { 243 // beware horiz order 244 first->setY(tree->y()); 245 first->setHeight(tree->height()); 246 last->setY(tree->y()); 247 last->setHeight(tree->height()); 248 } 249 } 250 else { 251 // vertical split 252 if (!ignoreVert) { 253 first->copy(tree); 254 first->setHeight(tree->height() / 2); 255 last->copy(first); 256 last->setY(first->y() + first->height()); 257 } 258 else { 259 // beware vert order 260 first->setX(tree->x()); 261 first->setWidth(tree->width()); 262 last->setX(tree->x()); 263 last->setWidth(tree->width()); 264 } 265 } 266 adjustSize(first, ignoreVert); 267 adjustSize(last, ignoreVert); 268 } 269 else { 270 Frame *frame = tree->frame(); 271 if (frame) { 272 frame->copy(tree); 273 frame->resize(); 274 } 275 } 276 } 277 278 void Split::detach(Tree *root, Tree *origin) { 279 280 Tree *parent = origin->parent(); 281 282 if (parent) { 283 Tree *child = (parent->first() != origin) 284 ? parent->first() : parent->last(); 285 delete origin; 286 assert(child); 287 if (child->first()) { 288 assert(child->last()); 289 assert(!child->frame()); 290 parent->setChilds(child->first(), child->last()); 291 parent->setHoriz(child->isHoriz()); 292 adjustSize(parent, parent->isHoriz()); 293 delete child; 294 } 295 else { 296 Frame *frame = child->frame(); 297 assert(frame); 298 parent->setFrame(frame); 299 parent->setChilds(0, 0); 300 frame->resize(); 301 delete child; 302 } 303 } 304 else { 305 assert(origin == root); 306 root->setFrame(0); 307 } 308 } 309 310 Frame *Split::firstLeaf(Tree *tree) { 311 312 if (tree->frame()) { 313 return tree->frame(); 314 } 315 if (tree->first() && tree->first()->frame()) { 316 return tree->first()->frame(); 317 } 318 if (tree->last() && tree->last()->frame()) { 319 return tree->last()->frame(); 320 } 321 if (tree->first()) { 322 return firstLeaf(tree->first()); 323 } 324 else if (tree->last()) { 325 return firstLeaf(tree->last()); 326 } 327 return 0; 328 } 329 330 void Split::attach(Tree *root, Frame *frame, Direction dir) { 331 332 Rectangle rect; 333 assert(root->first() == 0); 334 335 Frame *oldFrame = root->frame(); 336 if (!oldFrame) { 337 root->setFrame(frame); 338 } 339 else { 340 root->setFrame(0); 341 rect.copy(root); 342 343 if ((dir == LEFT) || (dir == RIGHT)) { 344 root->setHoriz(true); 345 rect.setWidth(rect.width() / 2); 346 } 347 else { 348 root->setHoriz(false); 349 rect.setHeight(rect.height() / 2); 350 } 351 352 Tree *first = new Tree(root, &rect); 353 Tree *last; 354 if ((dir == LEFT) || (dir == UP)) { 355 first->setFrame(frame); 356 if (dir == LEFT) { 357 rect.setX(rect.x() + rect.width()); 358 } 359 else { // UP 360 rect.setY(rect.y() + rect.height()); 361 } 362 last = new Tree(root, &rect); 363 last->setFrame(oldFrame); 364 } 365 else { // RIGHT || DOWN 366 first->setFrame(oldFrame); 367 if (dir == RIGHT) { 368 rect.setX(rect.x() + rect.width()); 369 } 370 else { // DOWN 371 rect.setY(rect.y() + rect.height()); 372 } 373 last = new Tree(root, &rect); 374 last->setFrame(frame); 375 } 376 root->setChilds(first, last); 377 oldFrame->resize(); 378 } 379 frame->resize(); 380 }