Implement reserve-north/south/west/east
[kwingrid.git] / kwingrid.cc
1 #include <iostream>
2
3 #include <QtGui/QDesktopWidget>
4 #include <QtGui/QX11Info>
5
6 #include <kapplication.h>
7 #include <kwindowsystem.h>
8 #include <netwm_def.h>
9
10 #include "kwingrid.h"
11 #include "kwingrid.moc"
12
13 KWinGrid::KWinGrid(int hgap__, int vgap__, int hsplit__, int vsplit__, int split__,
14                    int ignorestruts__, int reserveNorth__, int reserveSouth__,
15                    int reserveWest__, int reserveEast__)
16   : split_(split__), ignorestruts_(ignorestruts__), reserveNorth_(reserveNorth__),
17     reserveSouth_(reserveSouth__), reserveEast_(reserveEast__), reserveWest_(reserveWest__),
18     hgap_(hgap__), vgap_(vgap__), hsplit_(hsplit__), vsplit_(vsplit__)
19 {
20     connect(KWindowSystem::self(),SIGNAL(activeWindowChanged(WId)),
21             this,SLOT(activeWindowChanged(WId)));
22 }
23
24 void KWinGrid::move(int __xslot, int __yslot)
25 {
26     moveResize(__xslot, __yslot, -1, -1);
27 }
28
29 void KWinGrid::resize(int __xsize, int __ysize)
30 {
31     moveResize(-1,-1,__xsize,__ysize);
32 }
33
34 void KWinGrid::toDesk(int __desk)
35 {
36     int w = activeWindow();
37     if (w)
38         KWindowSystem::setOnDesktop(w,__desk);
39 }
40
41 void KWinGrid::quit()
42 {
43     KApplication::kApplication()->quit();
44 }
45
46 int KWinGrid::activeWindow()
47 {
48     int av = KWindowSystem::activeWindow();
49     KWindowInfo info = KWindowSystem::windowInfo(av,NET::WMWindowType);
50     if (info.windowType(NET::AllTypesMask) == NET::Desktop) return 0;
51     return av;
52 }
53
54 void KWinGrid::updateTimestamp(void)
55 {
56     timestamp_ = QDateTime::currentDateTime();
57 }
58
59 void KWinGrid::activeWindowChanged(WId id)
60 {
61     if (!activeWindow_ || timestamp_.isNull())
62         return;
63
64     QDateTime tm = QDateTime::currentDateTime();
65
66     int deltaDays = timestamp_.date().daysTo(tm.date());
67     int deltaMSecs = timestamp_.time().msecsTo(tm.time());
68
69     if (deltaDays>2 || deltaDays<0) {
70         activeWindow_ = 0;
71         return;
72     }
73
74     deltaMSecs += deltaDays * 1000*(60*60*24);
75
76     if (deltaMSecs <= 1000 && deltaMSecs > 0)
77         KWindowSystem::forceActiveWindow(activeWindow_);
78     else
79         activeWindow_ = 0;
80 }
81
82 void KWinGrid::moveResize(int __xslot, int __yslot,
83                           int __xsize, int __ysize)
84 {
85     initGeometry();
86     if (activeWindow_) {
87         QRect newGeometry = doMoveResize(__xslot,__yslot,__xsize,__ysize);
88         updateGeometry(newGeometry);
89         applyGeometry();
90     }
91 }
92
93 void KWinGrid::moveRelative(int __xdiff, int __ydiff)
94 {
95     initGeometry();
96     if (activeWindow_) {
97         int xSlot = (outer_.left()-region_.left()+hsize_/2)/hsize_;
98         int ySlot = (outer_.top()-region_.top()+vsize_/2)/vsize_;
99         xSlot += __xdiff;
100         ySlot += __ydiff;
101         if (xSlot<0) {
102             QPoint p (outer_.topLeft());
103             if (numScreens_ > 1 && p.x() > hsize_) {
104                 p.rx() -= hsize_;
105                 initGeometry( QApplication::desktop()->screenNumber(p) );
106             } else
107                 xSlot = 0;
108         } else if (xSlot >= hsplit_) {
109             QPoint p (outer_.topLeft());
110             QRect wa = KWindowSystem::workArea();
111             if (numScreens_ > 1 && p.x() + 2* hsize_ < wa.right()) {
112                 p.rx() += 2*hsize_;
113                 initGeometry( QApplication::desktop()->screenNumber(p) );
114                 xSlot = 0;
115                 ySlot = (outer_.top()-region_.top()+vsize_/2)/vsize_ + __ydiff;
116             } else
117                 xSlot = hsplit_-1;
118         }
119         if (ySlot<0)
120             ySlot = 0;
121         else if (ySlot >= vsplit_)
122             ySlot = vsplit_-1;
123         QRect newGeometry = doMoveResize(xSlot,ySlot,-1,-1);
124         updateGeometry(newGeometry);
125         applyGeometry();
126     }
127 }
128
129 void KWinGrid::resizeRelative(int __xdiff, int __ydiff)
130 {
131     initGeometry();
132     if (activeWindow_) {
133         int xSize = (outer_.width()+hsize_/2)/hsize_;
134         int ySize = (outer_.height()+vsize_/2)/vsize_;
135         xSize += __xdiff;
136         ySize += __ydiff;
137         if (xSize<1)
138             xSize = 1;
139         if (xSize>hsplit_)
140             xSize = hsplit_;
141         if (ySize<1)
142             ySize = 1;
143         if (ySize>vsplit_)
144             ySize = vsplit_;
145         QRect newGeometry = doMoveResize(-1,-1,xSize,ySize);
146         updateGeometry(newGeometry);
147         applyGeometry();
148     }
149 }
150
151 QRect KWinGrid::doMoveResize(int __xslot, int __yslot,
152                              int __xsize, int __ysize)
153 {
154     QRect newGeometry(outer_);
155
156     if (__xsize == -1) {
157         __xsize = (outer_.width()+hsize_/2)/hsize_;
158         if (__xsize<1) __xsize = 1;
159         if (__xsize>hsplit_) __xsize = hsplit_;
160     }
161     if (__ysize == -1) {
162         __ysize = (outer_.height()+vsize_/2)/vsize_;
163         if (__ysize<1) __ysize = 1;
164         if (__ysize>vsplit_) __ysize = vsplit_;
165     }
166
167     newGeometry.setWidth(__xsize*hsize_-hgap_);
168     newGeometry.setHeight(__ysize*vsize_-vgap_);
169
170     if (__xslot == -1) {
171         __xslot = (outer_.left()-region_.left()+hsize_/2)/hsize_;
172         if (__xslot<0) __xslot = 0;
173         if (__xslot>=hsplit_) __xslot = hsplit_-1;
174     }
175     if (__yslot == -1) {
176         __yslot = (outer_.top()-region_.top()+vsize_/2)/vsize_;
177         if (__yslot<0) __yslot = 0;
178         if (__yslot>=vsplit_) __yslot = vsplit_-1;
179     }
180     newGeometry.moveTopLeft(QPoint(region_.left() + __xslot*hsize_ + hgap_/2,
181                                    region_.top() + __yslot*vsize_ + vgap_/2));
182     return newGeometry;
183 }
184
185 std::ostream& operator<<(std::ostream& os, QRect rect)
186 {
187     os << '(' << rect.width() << 'x' << rect.height() << '+' << rect.x() << '+' << rect.y() << ')';
188     return os;
189 }
190
191 std::ostream& operator<<(std::ostream& os, QPoint p)
192 {
193     os << '(' << p.x() << ',' << p.y() << ')';
194     return os;
195 }
196
197 std::ostream& operator<<(std::ostream& os, QSize s)
198 {
199     os << '(' << s.width() << 'x' << s.height() << ')';
200     return os;
201 }
202
203 void KWinGrid::initGeometry(int __forceScreen)
204 {
205     activeWindowChanged(0);
206     if (activeWindow_ == 0)
207         activeWindow_ = activeWindow();
208     if (activeWindow_) {
209         KWindowInfo info(KWindowSystem::windowInfo(activeWindow_,NET::WMGeometry|NET::WMFrameExtents));
210         inner_ = info.geometry();
211         outer_ = info.frameGeometry();
212         orig_ = outer_;
213         if (split_) {
214             if (__forceScreen == -1)
215                 screen_ = outer_.left()>=split_ ? 1 : 0;
216             else
217                 screen_ = __forceScreen;
218             region_ = QApplication::desktop()->screenGeometry();
219             if (screen_ == 1)
220                 region_.setLeft(split_);
221             else
222                 region_.setRight(split_-1);
223             numScreens_ = 2;
224         } else {
225             if (__forceScreen == -1)
226                 screen_ = QApplication::desktop()->screenNumber(outer_.topLeft());
227             else
228                 screen_ = __forceScreen;
229             region_ = QApplication::desktop()->screenGeometry(screen_);
230             numScreens_ = QApplication::desktop()->numScreens();
231         }
232         if (screen_ != ignorestruts_) {
233             QRect wa = KWindowSystem::workArea();
234             region_ = region_ & wa;
235         }
236
237         hsize_ = (region_.width()-hgap_)/hsplit_;
238         vsize_ = (region_.height()-vgap_)/vsplit_;
239
240         int hdelta = region_.width()-hsize_*hsplit_;
241         int vdelta = region_.height()-vsize_*vsplit_;
242         QPoint topLeft(region_.topLeft());
243         topLeft+=QPoint(hdelta/2,vdelta/2);
244         region_.moveTopLeft(topLeft);
245         region_.setSize(QSize(hsize_*hsplit_,vsize_*vsplit_));
246
247         long supplied;
248         if (XGetWMNormalHints(QX11Info::display(), activeWindow_, &hints_, &supplied))
249             hints_.flags &= supplied;
250         else
251             hints_.flags = 0;
252     }
253 }
254
255 void KWinGrid::updateGeometry(QRect& __new)
256 {
257     QRect newInner(inner_);
258     newInner.moveTopLeft(QPoint(__new.left()+(inner_.left()-outer_.left()),
259                                 __new.top()+(inner_.top()-outer_.top())));
260     newInner.setSize(QSize(__new.width()-(outer_.width()-inner_.width()),
261                            __new.height()-(outer_.height()-inner_.height())));
262     inner_ = newInner;
263     outer_ = __new;
264
265     if (hints_.flags & PResizeInc && hints_.width_inc != 0 && hints_.height_inc != 0) {
266         QSize base(0,0);
267         if (hints_.flags & PBaseSize) {
268             base.setWidth(hints_.base_width);
269             base.setHeight(hints_.base_height);
270         } else if (hints_.flags & PMinSize) {
271             base.setWidth(hints_.min_width);
272             base.setHeight(hints_.min_height);
273         }
274         QSize newSize(((inner_.width()-base.width())/hints_.width_inc)*hints_.width_inc
275                       + base.width(),
276                       ((inner_.height()-base.height())/hints_.height_inc)*hints_.height_inc
277                       + base.height());
278         QSize delta(inner_.size() - newSize);
279         QPoint offset(delta.width()/2,delta.height()/2);
280         inner_.setSize(newSize);
281         outer_.setSize(outer_.size() - delta);
282         inner_.moveTopLeft(inner_.topLeft() + offset);
283         outer_.moveTopLeft(outer_.topLeft() + offset);
284     }
285 }
286
287 void KWinGrid::applyGeometry()
288 {
289     updateTimestamp();
290     if (orig_.topLeft() == outer_.topLeft())
291         // If the position of the window did not change,
292         // XMoveResizeWindow sometimes still moves the window a little
293         // bit. Seems to have something todo with window gravity
294         // ... we just leave the position allone in that case.
295         XResizeWindow(QX11Info::display(),activeWindow_, inner_.width(),inner_.height());
296     else {
297         // I don't really know, whats all this stuff concerning window
298         // gravity. I only know, this works for my openoffice windows,
299         // which have StaticGravity. I have not found any window with
300         // a window_gravity of neither StaticGravity nor
301         // NorthWestGravity on my desktop so did not check other
302         // window gravities.
303         QPoint pos = outer_.topLeft();
304         if (hints_.flags & PWinGravity && hints_.win_gravity == StaticGravity)
305             pos = inner_.topLeft();
306         XMoveResizeWindow(QX11Info::display(),activeWindow_,
307                           pos.x(),pos.y(), inner_.width(),inner_.height());
308     }
309 }
310
311 // slots
312
313 void KWinGrid::move_TL()
314 {
315     move(reserveWest_,reserveNorth_);
316 }
317
318 void KWinGrid::move_TR()
319 {
320     move((hsplit_-reserveWest_-reserveEast_)/2+reserveWest_,reserveNorth_);
321 }
322
323 void KWinGrid::move_BL()
324 {
325     move(reserveWest_,(vsplit_-reserveNorth_-reserveSouth_)/2+reserveNorth_);
326 }
327
328 void KWinGrid::move_BR()
329 {
330     move((hsplit_-reserveWest_-reserveEast_)/2+reserveWest_,
331          (vsplit_-reserveNorth_-reserveSouth_)/2+reserveNorth_);
332 }
333
334 void KWinGrid::resize_Q()
335 {
336     resize((hsplit_-reserveWest_-reserveEast_)/2,(vsplit_-reserveNorth_-reserveSouth_)/2);
337 }
338
339 void KWinGrid::resize_H()
340 {
341     resize((hsplit_-reserveWest_-reserveEast_),(vsplit_-reserveNorth_-reserveSouth_)/2);
342 }
343
344 void KWinGrid::resize_V()
345 {
346     resize((hsplit_-reserveWest_-reserveEast_)/2,vsplit_-reserveNorth_-reserveSouth_);
347 }
348
349 void KWinGrid::resize_F()
350 {
351     resize(hsplit_-reserveWest_-reserveEast_,vsplit_-reserveNorth_-reserveSouth_);
352 }
353
354 void KWinGrid::move_L()
355 {
356     moveRelative(-1,0);
357 }
358
359 void KWinGrid::move_R()
360 {
361     moveRelative(1,0);
362 }
363
364 void KWinGrid::move_U()
365 {
366     moveRelative(0,-1);
367 }
368
369 void KWinGrid::move_D()
370 {
371     moveRelative(0,1);
372 }
373
374 void KWinGrid::resize_IH()
375 {
376     resizeRelative(1,0);
377 }
378
379 void KWinGrid::resize_DH()
380 {
381     resizeRelative(-1,0);
382 }
383
384 void KWinGrid::resize_IV()
385 {
386     resizeRelative(0,1);
387 }
388
389 void KWinGrid::resize_DV()
390 {
391     resizeRelative(0,-1);
392 }