/***************************************************************** kwin - the KDE window manager Copyright (C) 1999, 2000 Matthias Ettrich Placement algorithms Copyright (C) 1997 to 2002 Cristian Tibirna ******************************************************************/ #include "workspace.h" #include "client.h" #include "options.h" #include "placement.h" #include using namespace KWinInternal; namespace KWinInternal { class PlacementPrivate { public: //CT needed for cascading+ struct DesktopCascadingInfo { QPoint pos; int col; int row; }; QValueList cci; Workspace* m_WorkspacePtr; }; }; Placement::Placement(Workspace* w) { d = new PlacementPrivate; d->m_WorkspacePtr = w; //CT initialize the cascading info for( int i = 0; i < d->m_WorkspacePtr->numberOfDesktops(); i++) { PlacementPrivate::DesktopCascadingInfo inf; inf.pos = QPoint(0,0); inf.col = 0; inf.row = 0; d->cci.append(inf); } } Placement::~Placement() { delete d; } /*! Places the client \a c according to the workspace's layout policy */ void Placement::place(Client* c) { if (options->placement == Options::Random) placeAtRandom(c); else if (options->placement == Options::Cascade) placeCascaded(c); else if (options->placement == Options::Centered) placeCentered(c); else if (options->placement == Options::ZeroCornered) placeZeroCornered(c); else placeSmart(c); } /*! Place the client \a c according to a simply "random" placement algorithm. */ void Placement::placeAtRandom(Client* c) { const int step = 24; static int px = step; static int py = 2 * step; int tx,ty; const QRect maxRect = d->m_WorkspacePtr->clientArea(Workspace::PlacementArea); if (px < maxRect.x()) px = maxRect.x(); if (py < maxRect.y()) py = maxRect.y(); px += step; py += 2*step; if (px > maxRect.width()/2) px = maxRect.x() + step; if (py > maxRect.height()/2) py = maxRect.y() + step; tx = px; ty = py; if (tx + c->width() > maxRect.right()){ tx = maxRect.right() - c->width(); if (tx < 0) tx = 0; px = maxRect.x(); } if (ty + c->height() > maxRect.bottom()){ ty = maxRect.bottom() - c->height(); if (ty < 0) ty = 0; py = maxRect.y(); } c->move(tx, ty); } /*! Place the client \a c according to a really smart placement algorithm :-) */ void Placement::placeSmart(Client* c) { /* * SmartPlacement by Cristian Tibirna (tibirna@kde.org) * adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with * permission) ideas from fvwm, authored by * Anthony Martin (amartin@engr.csulb.edu). * Xinerama supported added by Balaji Ramani (balaji@yablibli.com) * with ideas from xfce. */ const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types long int overlap, min_overlap = 0; int x_optimal, y_optimal; int possible; int desktop = c->desktop() == 0 || c->isSticky() ? d->m_WorkspacePtr->currentDesktop() : c->desktop(); int cxl, cxr, cyt, cyb; //temp coords int xl, xr, yt, yb; //temp coords int basket; //temp holder // get the maximum allowed windows space const QRect maxRect = d->m_WorkspacePtr->clientArea(Workspace::PlacementArea); int x = maxRect.left(), y = maxRect.top(); x_optimal = x; y_optimal = y; //client gabarit int ch = c->height() - 1; int cw = c->width() - 1; bool first_pass = true; //CT lame flag. Don't like it. What else would do? //loop over possible positions do { //test if enough room in x and y directions if (y + ch > maxRect.bottom() && ch < maxRect.height()) overlap = h_wrong; // this throws the algorithm to an exit else if(x + cw > maxRect.right()) overlap = w_wrong; else { overlap = none; //initialize cxl = x; cxr = x + cw; cyt = y; cyb = y + ch; ClientList::ConstIterator l; for(l = d->m_WorkspacePtr->stackingOrder().begin(); l != d->m_WorkspacePtr->stackingOrder().end() ; ++l) { if((*l)->isOnDesktop(desktop) && !(*l)->isIconified() && (*l) != c) { xl = (*l)->x(); yt = (*l)->y(); xr = xl + (*l)->width(); yb = yt + (*l)->height(); //if windows overlap, calc the overall overlapping if((cxl < xr) && (cxr > xl) && (cyt < yb) && (cyb > yt)) { xl = QMAX(cxl, xl); xr = QMIN(cxr, xr); yt = QMAX(cyt, yt); yb = QMIN(cyb, yb); if((*l)->staysOnTop()) overlap += 16 * (xr - xl) * (yb - yt); else overlap += (xr - xl) * (yb - yt); } } } } //CT first time we get no overlap we stop. if (overlap == none) { x_optimal = x; y_optimal = y; break; } if (first_pass) { first_pass = false; min_overlap = overlap; } //CT save the best position and the minimum overlap up to now else if (overlap >= none && overlap < min_overlap) { min_overlap = overlap; x_optimal = x; y_optimal = y; } // really need to loop? test if there's any overlap if (overlap > none) { possible = maxRect.right(); if (possible - cw > x) possible -= cw; // compare to the position of each client on the same desk ClientList::ConstIterator l; for(l = d->m_WorkspacePtr->stackingOrder().begin(); l != d->m_WorkspacePtr->stackingOrder().end() ; ++l) { if ((*l)->isOnDesktop(desktop) && !(*l)->isIconified() && (*l) != c) { xl = (*l)->x(); yt = (*l)->y(); xr = xl + (*l)->width(); yb = yt + (*l)->height(); // if not enough room above or under the current tested client // determine the first non-overlapped x position if((y < yb) && (yt < ch + y)) { if((xr > x) && (possible > xr)) possible = xr; basket = xl - cw; if((basket > x) && (possible > basket)) possible = basket; } } } x = possible; } // ... else ==> not enough x dimension (overlap was wrong on horizontal) else if (overlap == w_wrong) { x = maxRect.left(); possible = maxRect.bottom(); if (possible - ch > y) possible -= ch; //test the position of each window on the desk ClientList::ConstIterator l; for(l = d->m_WorkspacePtr->stackingOrder().begin(); l != d->m_WorkspacePtr->stackingOrder().end() ; ++l) { if((*l)->isOnDesktop(desktop) && (*l) != c && !c->isIconified()) { xl = (*l)->x(); yt = (*l)->y(); xr = xl + (*l)->width(); yb = yt + (*l)->height(); // if not enough room to the left or right of the current tested client // determine the first non-overlapped y position if((yb > y) && (possible > yb)) possible = yb; basket = yt - ch; if((basket > y) && (possible > basket)) possible = basket; } } y = possible; } } while((overlap != none) && (overlap != h_wrong) && (y < maxRect.bottom())); if(ch>= maxRect.height()) y_optimal=maxRect.top(); // place the window c->move(x_optimal, y_optimal); } /*! Place windows in a cascading order, remembering positions for each desktop */ void Placement::placeCascaded (Client* c, bool re_init) { /* cascadePlacement by Cristian Tibirna (tibirna@kde.org) (30Jan98) */ // work coords int xp, yp; //CT how do I get from the 'Client' class the size that NW squarish "handle" const int delta_x = 24; const int delta_y = 24; const int dn = c->desktop() == 0 || c->isSticky() ? (d->m_WorkspacePtr->currentDesktop() - 1) : (c->desktop() - 1); // get the maximum allowed windows space and desk's origin // (CT 20Nov1999 - is this common to all desktops?) QRect maxRect = d->m_WorkspacePtr->clientArea(Workspace::PlacementArea); // initialize often used vars: width and height of c; we gain speed const int ch = c->height(); const int cw = c->width(); const int X = maxRect.left(); const int Y = maxRect.top(); const int H = maxRect.height(); const int W = maxRect.width(); //initialize if needed if (re_init || d->cci[dn].pos.x() < X || d->cci[dn].pos.y() < Y ) { d->cci[dn].pos = QPoint(X, Y); d->cci[dn].col = d->cci[dn].row = 0; } xp = d->cci[dn].pos.x(); yp = d->cci[dn].pos.y(); //here to touch in case people vote for resize on placement if ((yp + ch) > H) yp = Y; if ((xp + cw) > W) if (!yp) { placeSmart(c); return; } else xp = X; //if this isn't the first window if (d->cci[dn].pos.x() != X && d->cci[dn].pos.y() != Y) { /* The following statements cause an internal compiler error with * egcs-2.91.66 on SuSE Linux 6.3. The equivalent forms compile fine. * 22-Dec-1999 CS * * if (xp != X && yp == Y) xp = delta_x * (++(d->cci[dn].col)); * if (yp != Y && xp == X) yp = delta_y * (++(d->cci[dn].row)); */ if (xp != X && yp == Y) { ++(d->cci[dn].col); xp = delta_x * d->cci[dn].col; } if (yp != Y && xp == X) { ++(d->cci[dn].row); yp = delta_y * d->cci[dn].row; } // last resort: if still doesn't fit, smart place it if (((xp + cw) > W - X) || ((yp + ch) > H - Y)) { placeSmart(c); return; } } // place the window c->move(QPoint(xp, yp)); // new position d->cci[dn].pos = QPoint(xp + delta_x, yp + delta_y); } /*! Place windows centered, on top of all others */ void Placement::placeCentered (Client* c){ // get the maximum allowed windows space and desk's origin // (CT 20Nov1999 - is this common to all desktops?) const QRect maxRect = d->m_WorkspacePtr->clientArea(Workspace::PlacementArea); const int xp = maxRect.left() + (maxRect.width() - c->width()) / 2; const int yp = maxRect.top() + (maxRect.height() - c->height()) / 2; // place the window c->move(QPoint(xp, yp)); } /*! Place windows in the (0,0) corner, on top of all others */ void Placement::placeZeroCornered(Client* c) { // get the maximum allowed windows space and desk's origin // (CT 20Nov1999 - is this common to all desktops?) const QRect maxRect = d->m_WorkspacePtr->clientArea(Workspace::PlacementArea); // place the window c->move(QPoint(maxRect.left(), maxRect.top())); }