1 /*
2  * IceWM
3  *
4  * Copyright (C) 1997-2002 Marko Macek
5  */
6 #include "config.h"
7 #include "wmframe.h"
8 #include "wmmgr.h"
9 #include "wmtitle.h"
10 #include "wmapp.h"
11 #include "wmcontainer.h"
12 #include "wmtaskbar.h"
13 #include "workspaces.h"
14 #include "wpixmaps.h"
15 #include "ymenuitem.h"
16 #include "yrect.h"
17 #include "prefs.h"
18 
updateMenu()19 void YFrameWindow::updateMenu() {
20     YMenu *windowMenu = this->windowMenu();
21     // enable all commands
22     windowMenu->setActionListener(this);
23     windowMenu->enableCommand(actionNull);
24 
25     if (!canMaximize()) {
26         windowMenu->disableCommand(actionMaximize);
27         windowMenu->disableCommand(actionMaximizeVert);
28         windowMenu->disableCommand(actionMaximizeHoriz);
29     }
30     if (!canMinimize())
31         windowMenu->disableCommand(actionMinimize);
32     if (!canRestore())
33         windowMenu->disableCommand(actionRestore);
34     if (isMinimized() || isHidden() || !canSize() || !visibleNow())
35         windowMenu->disableCommand(actionSize);
36     if (isMinimized() || isHidden() || !canMove() || !visibleNow())
37         windowMenu->disableCommand(actionMove);
38     if (!canLower())
39         windowMenu->disableCommand(actionLower);
40     if (!canRaise())
41         windowMenu->disableCommand(actionRaise);
42     if (!canHide())
43         windowMenu->disableCommand(actionHide);
44     if (!canRollup())
45         windowMenu->disableCommand(actionRollup);
46     if (!canClose())
47         windowMenu->disableCommand(actionClose);
48 
49     bool full = isFullscreen();
50     bool vert = !full && isMaximizedVert();
51     bool hori = !full && isMaximizedHoriz();
52     windowMenu->checkCommand(actionMinimize, isMinimized());
53     windowMenu->checkCommand(actionMaximize, vert && hori);
54     windowMenu->checkCommand(actionMaximizeVert, vert && !hori);
55     windowMenu->checkCommand(actionMaximizeHoriz, hori && !vert);
56     windowMenu->checkCommand(actionFullscreen, full);
57     windowMenu->checkCommand(actionHide, isHidden());
58     windowMenu->checkCommand(actionRollup, isRollup());
59     windowMenu->checkCommand(actionOccupyAllOrCurrent, isAllWorkspaces());
60 #if DO_NOT_COVER_OLD
61     windowMenu->checkCommand(actionDoNotCover, doNotCover());
62 #endif
63     updateSubmenus();
64 }
65 
updateSubmenus()66 void YFrameWindow::updateSubmenus() {
67     if (moveMenu) {
68         YMenuItem *item = windowMenu()->findSubmenu(moveMenu);
69         if (item) {
70             bool enable = !isAllWorkspaces() && 1 < workspaceCount;
71             item->setEnabled(enable);
72             if (enable) {
73                 moveMenu->updatePopup();
74                 moveMenu->setActionListener(this);
75 
76                 for (int i(0); i < moveMenu->itemCount(); i++) {
77                     item = moveMenu->getItem(i);
78                     if (item && item->getAction() == workspaceActionMoveTo[i]) {
79                         bool const e(i == getWorkspace());
80                         item->setEnabled(!e);
81                         item->setChecked(e);
82                     }
83                 }
84             }
85         }
86     }
87 
88     if (layerMenu) {
89         YMenuItem *item = windowMenu()->findSubmenu(layerMenu);
90         if (item) {
91             layerMenu->updatePopup();
92             layerMenu->setActionListener(this);
93 
94             int layer = WinLayerCount - 1;
95             for (int j(0); j < layerMenu->itemCount(); j++) {
96                 YMenuItem* item = layerMenu->getItem(j);
97                 YAction action = item->getAction();
98                 while (action < layerActionSet[layer] && 0 < layer) {
99                     --layer;
100                 }
101                 if (action == layerActionSet[layer]) {
102                     bool const e(layer == getActiveLayer());
103                     item->setEnabled(!e);
104                     item->setChecked(e);
105                 }
106             }
107         }
108     }
109 
110     if (tileMenu) {
111         YMenuItem *item = windowMenu()->findSubmenu(tileMenu);
112         if (item) {
113             bool enable = canMove();
114             item->setEnabled(enable);
115             if (enable) {
116                 tileMenu->updatePopup();
117                 tileMenu->setActionListener(this);
118             }
119         }
120     }
121 
122     YMenuItem* item = windowMenu()->findAction(actionToggleTray);
123     if (item) {
124         bool checked = (getTrayOption() != WinTrayIgnore);
125         bool enabled = notbit(frameOptions(), foIgnoreTaskBar);
126         item->setChecked(checked);
127         item->setEnabled(enabled || checked);
128     }
129 
130 #if 0
131     if (trayMenu) for (int k(0); k < trayMenu->itemCount(); k++) {
132         item = trayMenu->getItem(k);
133         for (int opt(0); opt < WinTrayOptionCount; opt++)
134             if (item && item->getAction() == trayOptionActionSet[opt]) {
135                 bool const e(opt == getTrayOption());
136                 item->setEnabled(!e);
137                 item->setChecked(e);
138             }
139     }
140 #endif
141 }
142 
setShape()143 void YFrameWindow::setShape() {
144 #ifdef CONFIG_SHAPE
145     if (!shapes.supported)
146         return ;
147 
148     if (client()->shaped()) {
149         MSG(("setting shape w=%d, h=%d", width(), height()));
150         if (isRollup() || isIconic()) {
151             XRectangle full;
152             full.x = 0;
153             full.y = 0;
154             full.width = width();
155             full.height = height();
156             XShapeCombineRectangles(xapp->display(), handle(),
157                                     ShapeBounding,
158                                     0, 0, &full, 1,
159                                     ShapeSet, Unsorted);
160         } else {
161             XRectangle rect[6];
162             int nrect = 0;
163 
164             if ((frameDecors() & (fdResize | fdBorder)) == fdResize + fdBorder) {
165                 rect[0].x = 0;
166                 rect[0].y = 0;
167                 rect[0].width = width();
168                 rect[0].height = borderY();
169 
170                 rect[1] = rect[0];
171                 rect[1].y = height() - borderY();
172 
173                 rect[2].x = 0;
174                 rect[2].y = borderY();
175                 rect[2].width = borderX();
176                 rect[2].height = height() - 2 * borderY();
177 
178                 rect[3] = rect[2];
179                 rect[3].x = width() - borderX();
180 
181                 nrect = 4;
182             }
183 
184             if (titleY() > 0) {
185                 rect[nrect].x = borderX();
186                 rect[nrect].y = borderY();
187                 rect[nrect].width  = width() - 2 * borderX();
188                 rect[nrect].height = titleY();
189                 nrect++;
190             }
191 
192             if (nrect !=  0)
193                 XShapeCombineRectangles(xapp->display(), handle(),
194                                         ShapeBounding,
195                                         0, 0, rect, nrect,
196                                         ShapeSet, Unsorted);
197             XShapeCombineShape(xapp->display(), handle(),
198                                ShapeBounding,
199                                borderX(),
200                                borderY() + titleY(),
201                                client()->handle(),
202                                ShapeBounding, nrect ? ShapeUnion : ShapeSet);
203         }
204     }
205 #endif
206 }
207 
layoutShape()208 void YFrameWindow::layoutShape() {
209 #ifdef CONFIG_SHAPE
210     if (fShapeWidth != width() ||
211         fShapeHeight != height() ||
212         fShapeTitleY != titleY() ||
213         fShapeBorderX != borderX() ||
214         fShapeBorderY != borderY() ||
215         fShapeDecors != frameDecors() ||
216         fShapeTitle != getTitle())
217     {
218         fShapeWidth = width();
219         fShapeHeight = height();
220         fShapeTitleY = titleY();
221         fShapeBorderX = borderX();
222         fShapeBorderY = borderY();
223         fShapeDecors = frameDecors();
224         fShapeTitle = getTitle();
225 
226         if (shapes.supported &&
227             (frameDecors() & fdBorder) &&
228             !isIconic() &&
229             !isFullscreen())
230         {
231             int const a(focused());
232             int const t((frameDecors() & fdResize) ? 0 : 1);
233 
234             Pixmap shape = XCreatePixmap(xapp->display(), desktop->handle(),
235                                          width(), height(), 1);
236             Graphics g(shape, width(), height(), 1);
237 
238             g.setColorPixel(1);
239             g.fillRect(0, 0, width(), height());
240 
241             const int
242                 xTL(frameTL[t][a] != null ? frameTL[t][a]->width() : 0),
243                 xTR(width() - (frameTR[t][a] != null ? frameTR[t][a]->width() : 0)),
244                 xBL(frameBL[t][a] != null ? frameBL[t][a]->width() : 0),
245                 xBR(width() - (frameBR[t][a] != null ? frameBR[t][a]->width() : 0));
246             const int
247                 yTL(frameTL[t][a] != null ? frameTL[t][a]->height() : 0),
248                 yBL(height() - (frameBL[t][a] != null ? frameBL[t][a]->height() : 0)),
249                 yTR(frameTR[t][a] != null ? frameTR[t][a]->height() : 0),
250                 yBR(height() - (frameBR[t][a] != null ? frameBR[t][a]->height() : 0));
251 
252             if (frameTL[t][a] != null) {
253                 g.copyDrawable(frameTL[t][a]->mask(), 0, 0,
254                                frameTL[t][a]->width(), frameTL[t][a]->height(),
255                                0, 0);
256                 if (protectClientWindow)
257                     g.fillRect(borderX(), borderY(),
258                                frameTL[t][a]->width() - borderX(),
259                                frameTL[t][a]->height() - borderY());
260             }
261             if (frameTR[t][a] != null) {
262                 g.copyDrawable(frameTR[t][a]->mask(), 0, 0,
263                                frameTR[t][a]->width(), frameTR[t][a]->height(),
264                                xTR, 0);
265                 if (protectClientWindow)
266                     g.fillRect(xTR, borderY(),
267                                frameTR[t][a]->width() - borderX(),
268                                frameTR[t][a]->height() - borderY());
269             }
270             if (frameBL[t][a] != null) {
271                 g.copyDrawable(frameBL[t][a]->mask(), 0, 0,
272                                frameBL[t][a]->width(), frameBL[t][a]->height(),
273                                0, yBL);
274                 if (protectClientWindow)
275                     g.fillRect(borderX(), yBL,
276                                frameBL[t][a]->width() - borderX(),
277                                frameBL[t][a]->height() - borderY());
278             }
279             if (frameBR[t][a] != null) {
280                 g.copyDrawable(frameBR[t][a]->mask(), 0, 0,
281                                frameBR[t][a]->width(), frameBL[t][a]->height(),
282                                xBR, yBR);
283                 if (protectClientWindow)
284                     g.fillRect(xBR, yBR,
285                                frameBR[t][a]->width() - borderX(),
286                                frameBR[t][a]->width() - borderY());
287             }
288 
289             if (frameT[t][a] != null)
290                 g.repHorz(frameT[t][a]->mask(),
291                           frameT[t][a]->width(), frameT[t][a]->height(),
292                           xTL, 0, xTR - xTL);
293             if (frameB[t][a] != null)
294                 g.repHorz(frameB[t][a]->mask(),
295                           frameB[t][a]->width(), frameB[t][a]->height(),
296                           xBL, height() - frameB[t][a]->height(), xBR - xBL);
297             if (frameL[t][a] != null)
298                 g.repVert(frameL[t][a]->mask(),
299                           frameL[t][a]->width(), frameL[t][a]->height(),
300                           0, yTL, yBL - yTL);
301             if (frameR[t][a] != null)
302                 g.repVert(frameR[t][a]->mask(),
303                           frameR[t][a]->width(), frameR[t][a]->height(),
304                           width() - frameR[t][a]->width(), yTR, yBR - yTR);
305 
306             if (titleY() && titlebar())
307                 titlebar()->renderShape(g);
308             XShapeCombineMask(xapp->display(), handle(),
309                               ShapeBounding, 0, 0, shape, ShapeSet);
310             XFreePixmap(xapp->display(), shape);
311         } else {
312             XShapeCombineMask(xapp->display(), handle(),
313                               ShapeBounding, 0, 0, None, ShapeSet);
314         }
315         setShape();
316     }
317 #endif
318 }
319 
configure(const YRect2 & r)320 void YFrameWindow::configure(const YRect2& r) {
321     MSG(("%s %d %d %d %d", __func__, r.x(), r.y(), r.width(), r.height()));
322 
323     if (r.resized()) {
324         performLayout();
325         if (taskBar)
326             taskBar->workspacesRepaint();
327     }
328     if (affectsWorkArea()) {
329         manager->updateWorkArea();
330     }
331 }
332 
performLayout()333 void YFrameWindow::performLayout()
334 {
335     layoutTitleBar();
336     layoutClient();
337     layoutShape();
338     if (fTitleBar)
339         fTitleBar->activate();
340     layoutResizeIndicators();
341 }
342 
layoutTitleBar()343 void YFrameWindow::layoutTitleBar() {
344     if (titleY()) {
345         if (fTitleBar) {
346             fTitleBar->relayout();
347         } else {
348             fTitleBar = new YFrameTitleBar(this, this);
349         }
350     }
351     else if (fTitleBar) {
352         delete fTitleBar;
353         fTitleBar = nullptr;
354     }
355 }
356 
layoutResizeIndicators()357 void YFrameWindow::layoutResizeIndicators() {
358     if (isUnmapped() || !hasBorders() || !isResizable()) {
359         if (indicatorsCreated) {
360             Window* indicators[] = {
361                 &topSide, &leftSide, &rightSide, &bottomSide,
362                 &topLeft, &topRight, &bottomLeft, &bottomRight
363             };
364             for (Window* window : indicators) {
365                 XDestroyWindow(xapp->display(), *window);
366                 *window = None;
367             }
368             indicatorsCreated = false;
369         }
370         return;
371     }
372     if (indicatorsCreated == false)
373         createPointerWindows();
374 
375     int vo = int(min(height(), topSideVerticalOffset));
376     int ww(max(3, (int) width()));
377     int hh(max(3, (int) height() - vo));
378     int bx(max(1, (int) borderX()));
379     int bt(max(2, (int) borderY() - vo));
380     int bb(max(1, (int) borderY()));
381     int cx(max(1, (int) wsCornerX));
382     int cy(max(1, (int) wsCornerY));
383     int xx(min(cx, ww / 2));
384     int yy(min(cy, hh / 2));
385 
386     XMoveResizeWindow(xapp->display(), topSide,
387                       xx, vo, max(1, ww - 2 * xx), bt);
388     XMoveResizeWindow(xapp->display(), leftSide,
389                       0, yy + vo, bx, max(1, hh - 2 * yy));
390     XMoveResizeWindow(xapp->display(), rightSide,
391                       ww - bx, yy + vo, bx, max(1, hh - 2 * yy));
392     XMoveResizeWindow(xapp->display(), bottomSide,
393                       xx, hh - bb + vo, max(1, ww - 2 * xx), bb);
394 
395     XMoveResizeWindow(xapp->display(), topLeft,
396                       0, vo, xx, yy);
397     XMoveResizeWindow(xapp->display(), topRight,
398                       ww - xx, vo, xx, yy);
399     XMoveResizeWindow(xapp->display(), bottomLeft,
400                       0, hh - yy + vo, xx, yy);
401     XMoveResizeWindow(xapp->display(), bottomRight,
402                       ww - xx, hh - yy + vo, xx, yy);
403 
404     XRaiseWindow(xapp->display(), topSide);
405 }
406 
layoutClient()407 void YFrameWindow::layoutClient() {
408     if (!isRollup()) {
409         int x = borderX();
410         int y = borderY();
411         int title = titleY();
412         int w = max(1, int(width()) - 2 * x);
413         int h = max(1, int(height()) - 2 * y - title);
414         bool moved = (x != container()->x() || !fManaged ||
415                       y != container()->y() - title);
416 
417         container()->setGeometry(YRect(x, y + title, w, h));
418         client()->setGeometry(YRect(0, 0, w, h));
419 
420         if (moved) {
421             sendConfigure();
422             client()->setNetFrameExtents(x, x, y + title, y);
423         }
424     }
425 }
426 
isGroupModalFor(const YFrameWindow * other) const427 bool YFrameWindow::isGroupModalFor(const YFrameWindow* other) const {
428     bool have = false;
429     if (hasState(WinStateModal) && owner() == nullptr) {
430         Window leader = client()->clientLeader();
431         if (leader && leader == other->client()->clientLeader()) {
432             bool self = false, that = false;
433             for (auto& modal : groupModals) {
434                 if (modal == this) {
435                     self = true;
436                 }
437                 if (modal == other) {
438                     that = true;
439                 }
440             }
441             have = self && !that;
442         }
443     }
444     return have;
445 }
446 
isTransientFor(const YFrameWindow * other) const447 bool YFrameWindow::isTransientFor(const YFrameWindow* other) const {
448     YFrameWindow* o = owner();
449     while (o && o != other) {
450         o = o->owner();
451     }
452     return o == other;
453 }
454 
canLower() const455 bool YFrameWindow::canLower() const {
456     for (YFrameWindow* w = next(); w; w = w->next()) {
457         if (isTransientFor(w) == false && isGroupModalFor(w) == false) {
458             return true;
459         }
460     }
461     return false;
462 }
463 
canRaise() const464 bool YFrameWindow::canRaise() const {
465     for (YFrameWindow *w = prev(); w; w = w->prev()) {
466         if (w->visibleNow() || w->visibleOn(getWorkspace())) {
467             if (w->isTransientFor(this) == false &&
468                 w->isGroupModalFor(this) == false)
469                 return true;
470         }
471     }
472     return false;
473 }
474 
overlap(YFrameWindow * f)475 unsigned YFrameWindow::overlap(YFrameWindow* f) {
476     if (false == f->isHidden() &&
477         false == f->isMinimized() &&
478         f->visibleNow())
479     {
480         return geometry().intersect(f->geometry()).pixels();
481     }
482     return 0;
483 }
484 
overlaps(bool isAbove)485 bool YFrameWindow::overlaps(bool isAbove) {
486     YFrameWindow* f = isAbove ? prev() : next();
487     for (; f; f = isAbove ? f->prev() : f->next())
488         if (overlap(f))
489             return true;
490     return false;
491 }
492 
hasBorders() const493 bool YFrameWindow::hasBorders() const {
494     return hasbit(frameDecors(), fdBorder) && !isFullscreen() &&
495          !(hideBordersMaximized && isMaximizedFully()) &&
496          (hasbit(frameDecors(), fdResize) ?
497           (wsBorderX | wsBorderY) != 0 :
498           (wsDlgBorderX | wsDlgBorderY) != 0);
499 }
500 
501 /// TODO #warning "should precalculate these"
borderX() const502 int YFrameWindow::borderX() const {
503     return
504         isFullscreen() ? 0 : borderXN();
505 }
506 
borderXN() const507 int YFrameWindow::borderXN() const {
508     return
509         ((frameDecors() & fdBorder) && !(hideBordersMaximized && isMaximizedFully()))
510         ? ((frameDecors() & fdResize) ? wsBorderX : wsDlgBorderX)
511         : 0;
512 }
513 
borderY() const514 int YFrameWindow::borderY() const {
515     return
516         isFullscreen() ? 0 : borderYN();
517 }
518 
borderYN() const519 int YFrameWindow::borderYN() const {
520     return
521         ((frameDecors() & fdBorder) && !(hideBordersMaximized && isMaximizedFully()))
522         ? ((frameDecors() & fdResize) ? wsBorderY : wsDlgBorderY)
523         : 0;
524 }
525 
titleY() const526 int YFrameWindow::titleY() const {
527     return isFullscreen() ? 0 : titleYN();
528 }
529 
titleYN() const530 int YFrameWindow::titleYN() const {
531     if (hideTitleBarWhenMaximized && isMaximizedVert())
532         return 0;
533     return (frameDecors() & fdTitleBar) ? wsTitleBar : 0;
534 }
535 
536 // vim: set sw=4 ts=4 et:
537