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