wmi

git clone git://oldgit.suckless.org/wmi/
Log | Files | Refs | LICENSE

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 }