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