1 // FbWinFrame.cc for Fluxbox Window Manager
2 // Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 
22 #include "FbWinFrame.hh"
23 
24 #include "Keys.hh"
25 #include "FbWinFrameTheme.hh"
26 #include "Screen.hh"
27 #include "FocusableTheme.hh"
28 #include "IconButton.hh"
29 #include "RectangleUtil.hh"
30 
31 #include "FbTk/ImageControl.hh"
32 #include "FbTk/EventManager.hh"
33 #include "FbTk/App.hh"
34 #include "FbTk/SimpleCommand.hh"
35 #include "FbTk/Compose.hh"
36 #include "FbTk/Transparent.hh"
37 #include "FbTk/CompareEqual.hh"
38 #include "FbTk/TextUtils.hh"
39 #include "FbTk/STLUtil.hh"
40 
41 #include <X11/X.h>
42 
43 #include <algorithm>
44 
45 using std::max;
46 using std::mem_fun;
47 using std::string;
48 
49 using FbTk::STLUtil::forAll;
50 
51 namespace {
52 
53 enum { UNFOCUS = 0, FOCUS, PRESSED };
54 
55 const int s_button_size = 26;
56 const long s_mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask;
57 
58 const struct {
59     FbWinFrame::TabPlacement where;
60     FbTk::Orientation orient;
61     FbTk::Container::Alignment align;
62     bool is_horizontal;
63 } s_place[] = {
64     { /* unused */ },
65     { FbWinFrame::TOPLEFT,    FbTk::ROT0,   FbTk::Container::LEFT,   true },
66     { FbWinFrame::TOP,        FbTk::ROT0,   FbTk::Container::CENTER, true },
67     { FbWinFrame::TOPRIGHT,   FbTk::ROT0,   FbTk::Container::RIGHT,  true },
68     { FbWinFrame::BOTTOMLEFT, FbTk::ROT0,   FbTk::Container::LEFT,   true },
69     { FbWinFrame::BOTTOM,     FbTk::ROT0,   FbTk::Container::CENTER, true },
70     { FbWinFrame::BOTTOMRIGHT,FbTk::ROT0,   FbTk::Container::RIGHT,  true },
71     { FbWinFrame::LEFTTOP,    FbTk::ROT270, FbTk::Container::RIGHT,  false },
72     { FbWinFrame::LEFT,       FbTk::ROT270, FbTk::Container::CENTER, false },
73     { FbWinFrame::LEFTBOTTOM, FbTk::ROT270, FbTk::Container::LEFT,   false },
74     { FbWinFrame::RIGHTTOP,   FbTk::ROT90,  FbTk::Container::LEFT,   false },
75     { FbWinFrame::RIGHT,      FbTk::ROT90,  FbTk::Container::LEFT,   false },
76     { FbWinFrame::RIGHTBOTTOM,FbTk::ROT90,  FbTk::Container::LEFT,   false },
77 };
78 
79 /// renders to pixmap or sets color
render(FbTk::Color & col,Pixmap & pm,unsigned int width,unsigned int height,const FbTk::Texture & tex,FbTk::ImageControl & ictl,FbTk::Orientation orient=FbTk::ROT0)80 void render(FbTk::Color &col, Pixmap &pm, unsigned int width, unsigned int height,
81             const FbTk::Texture &tex,
82             FbTk::ImageControl& ictl,
83             FbTk::Orientation orient = FbTk::ROT0) {
84 
85     Pixmap tmp = pm;
86     if (!tex.usePixmap()) {
87         pm = None;
88         col = tex.color();
89     } else {
90         pm = ictl.renderImage(width, height, tex, orient);
91     }
92 
93     if (tmp)
94         ictl.removeImage(tmp);
95 
96 }
97 
bg_pm_or_color(FbTk::FbWindow & win,const Pixmap & pm,const FbTk::Color & color)98 void bg_pm_or_color(FbTk::FbWindow& win, const Pixmap& pm, const FbTk::Color& color) {
99     if (pm) {
100         win.setBackgroundPixmap(pm);
101     } else {
102         win.setBackgroundColor(color);
103     }
104 }
105 
106 
107 } // end anonymous
108 
FbWinFrame(BScreen & screen,unsigned int client_depth,WindowState & state,FocusableTheme<FbWinFrameTheme> & theme)109 FbWinFrame::FbWinFrame(BScreen &screen, unsigned int client_depth,
110                        WindowState &state,
111                        FocusableTheme<FbWinFrameTheme> &theme):
112     m_screen(screen),
113     m_theme(theme),
114     m_imagectrl(screen.imageControl()),
115     m_state(state),
116     m_window(theme->screenNum(), state.x, state.y, state.width, state.height, s_mask, true, false,
117         client_depth, InputOutput,
118         (client_depth == screen.rootWindow().maxDepth() ? screen.rootWindow().visual() : CopyFromParent),
119         (client_depth == screen.rootWindow().maxDepth() ? screen.rootWindow().colormap() : CopyFromParent)),
120     m_layeritem(window(), *screen.layerManager().getLayer(ResourceLayer::NORMAL)),
121     m_titlebar(m_window, 0, 0, 100, 16, s_mask, false, false,
122         screen.rootWindow().decorationDepth(), InputOutput,
123         screen.rootWindow().decorationVisual(),
124         screen.rootWindow().decorationColormap()),
125     m_tab_container(m_titlebar),
126     m_label(m_titlebar, m_theme->font(), FbTk::BiDiString("")),
127     m_handle(m_window, 0, 0, 100, 5, s_mask, false, false,
128         screen.rootWindow().decorationDepth(), InputOutput,
129         screen.rootWindow().decorationVisual(),
130         screen.rootWindow().decorationColormap()),
131     m_grip_right(m_handle, 0, 0, 10, 4, s_mask, false, false,
132         screen.rootWindow().decorationDepth(), InputOutput,
133         screen.rootWindow().decorationVisual(),
134         screen.rootWindow().decorationColormap()),
135     m_grip_left(m_handle, 0, 0, 10, 4, s_mask, false, false,
136         screen.rootWindow().decorationDepth(), InputOutput,
137         screen.rootWindow().decorationVisual(),
138         screen.rootWindow().decorationColormap()),
139     m_clientarea(m_window, 0, 0, 100, 100, s_mask),
140     m_bevel(1),
141     m_use_titlebar(true),
142     m_use_tabs(true),
143     m_use_handle(true),
144     m_visible(false),
145     m_tabmode(screen.getDefaultInternalTabs()?INTERNAL:EXTERNAL),
146     m_active_orig_client_bw(0),
147     m_need_render(true),
148     m_button_size(1),
149     m_shape(m_window, theme->shapePlace()) {
150 
151     init();
152 }
153 
~FbWinFrame()154 FbWinFrame::~FbWinFrame() {
155     removeEventHandler();
156     removeAllButtons();
157 }
158 
setTabMode(TabMode tabmode)159 bool FbWinFrame::setTabMode(TabMode tabmode) {
160     if (m_tabmode == tabmode)
161         return false;
162 
163     FbTk::Container& tabs = tabcontainer();
164     bool ret = true;
165 
166     // setting tabmode to notset forces it through when
167     // something is likely to change
168     if (tabmode == NOTSET)
169         tabmode = m_tabmode;
170 
171     m_tabmode = tabmode;
172 
173     // reparent tab container
174     if (tabmode == EXTERNAL) {
175         m_label.show();
176         tabs.setBorderWidth(m_window.borderWidth());
177         tabs.setEventMask(s_mask);
178         alignTabs();
179 
180         // TODO: tab position
181         if (m_use_tabs && m_visible)
182             tabs.show();
183         else {
184             ret = false;
185             tabs.hide();
186         }
187 
188     } else {
189         tabs.setUpdateLock(true);
190 
191         tabs.setAlignment(FbTk::Container::RELATIVE);
192         tabs.setOrientation(FbTk::ROT0);
193         if (tabs.parent()->window() == m_screen.rootWindow().window()) {
194             m_layeritem.removeWindow(m_tab_container);
195             tabs.hide();
196             tabs.reparent(m_titlebar, m_label.x(), m_label.y());
197             tabs.invalidateBackground();
198             tabs.resize(m_label.width(), m_label.height());
199             tabs.raise();
200         }
201         tabs.setBorderWidth(0);
202         tabs.setMaxTotalSize(0);
203         tabs.setUpdateLock(false);
204         tabs.setMaxSizePerClient(0);
205 
206         renderTabContainer();
207         applyTabContainer();
208 
209         tabs.clear();
210         tabs.raise();
211         tabs.show();
212 
213         if (!m_use_tabs)
214             ret = false;
215 
216         m_label.hide();
217     }
218 
219     return ret;
220 }
221 
hide()222 void FbWinFrame::hide() {
223     m_window.hide();
224     if (m_tabmode == EXTERNAL && m_use_tabs)
225         m_tab_container.hide();
226 
227     m_visible = false;
228 }
229 
show()230 void FbWinFrame::show() {
231     m_visible = true;
232 
233     if (m_need_render) {
234         renderAll();
235         applyAll();
236         clearAll();
237     }
238 
239     if (m_tabmode == EXTERNAL && m_use_tabs)
240         m_tab_container.show();
241 
242     m_window.showSubwindows();
243     m_window.show();
244 }
245 
move(int x,int y)246 void FbWinFrame::move(int x, int y) {
247     moveResize(x, y, 0, 0, true, false);
248 }
249 
resize(unsigned int width,unsigned int height)250 void FbWinFrame::resize(unsigned int width, unsigned int height) {
251     moveResize(0, 0, width, height, false, true);
252 }
253 
254 // need an atomic moveresize where possible
moveResizeForClient(int x,int y,unsigned int width,unsigned int height,int win_gravity,unsigned int client_bw,bool move,bool resize)255 void FbWinFrame::moveResizeForClient(int x, int y,
256                                      unsigned int width, unsigned int height,
257                                      int win_gravity,
258                                      unsigned int client_bw,
259                                      bool move, bool resize) {
260     // total height for frame
261 
262     if (resize) // these fns check if the elements are "on"
263         height += titlebarHeight() + handleHeight();
264 
265     gravityTranslate(x, y, win_gravity, client_bw, false);
266     setActiveGravity(win_gravity, client_bw);
267     moveResize(x, y, width, height, move, resize);
268 }
269 
resizeForClient(unsigned int width,unsigned int height,int win_gravity,unsigned int client_bw)270 void FbWinFrame::resizeForClient(unsigned int width, unsigned int height,
271                                  int win_gravity, unsigned int client_bw) {
272     moveResizeForClient(0, 0, width, height, win_gravity, client_bw, false, true);
273 }
274 
moveResize(int x,int y,unsigned int width,unsigned int height,bool move,bool resize)275 void FbWinFrame::moveResize(int x, int y, unsigned int width, unsigned int height, bool move, bool resize) {
276     if (move && x == window().x() && y == window().y())
277         move = false;
278 
279     if (resize && width == FbWinFrame::width() &&
280                   height == FbWinFrame::height())
281         resize = false;
282 
283     if (!move && !resize)
284         return;
285 
286     if (move && resize) {
287         m_window.moveResize(x, y, width, height);
288         notifyMoved(false); // will reconfigure
289     } else if (move) {
290         m_window.move(x, y);
291         // this stuff will be caught by reconfigure if resized
292         notifyMoved(true);
293     } else {
294         m_window.resize(width, height);
295     }
296 
297     m_state.saveGeometry(window().x(), window().y(),
298                          window().width(), window().height());
299 
300     if (move || (resize && m_screen.getTabPlacement() != TOPLEFT &&
301                            m_screen.getTabPlacement() != LEFTTOP))
302         alignTabs();
303 
304     if (resize) {
305         if (m_tabmode == EXTERNAL) {
306             unsigned int s = width;
307             if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
308                 s = height;
309             }
310             m_tab_container.setMaxTotalSize(s);
311         }
312         reconfigure();
313     }
314 }
315 
quietMoveResize(int x,int y,unsigned int width,unsigned int height)316 void FbWinFrame::quietMoveResize(int x, int y,
317                                  unsigned int width, unsigned int height) {
318     m_window.moveResize(x, y, width, height);
319     m_state.saveGeometry(window().x(), window().y(),
320                          window().width(), window().height());
321     if (m_tabmode == EXTERNAL) {
322         unsigned int s = width;
323         if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
324             s = height;
325         }
326         m_tab_container.setMaxTotalSize(s);
327         alignTabs();
328     }
329 }
330 
alignTabs()331 void FbWinFrame::alignTabs() {
332     if (m_tabmode != EXTERNAL)
333         return;
334 
335 
336     FbTk::Container& tabs = tabcontainer();
337     FbTk::Orientation orig_orient = tabs.orientation();
338     unsigned int orig_tabwidth = tabs.maxWidthPerClient();
339 
340     if (orig_tabwidth != m_screen.getTabWidth())
341         tabs.setMaxSizePerClient(m_screen.getTabWidth());
342 
343     int bw = window().borderWidth();
344     int size = width();
345     int tab_x = x();
346     int tab_y = y();
347 
348     TabPlacement p = m_screen.getTabPlacement();
349     if (orig_orient != s_place[p].orient) {
350         tabs.hide();
351     }
352     if (!s_place[p].is_horizontal) {
353         size = height();
354     }
355     tabs.setOrientation(s_place[p].orient);
356     tabs.setAlignment(s_place[p].align);
357     tabs.setMaxTotalSize(size);
358 
359     int w = static_cast<int>(width());
360     int h = static_cast<int>(height());
361     int xo = xOffset();
362     int yo = yOffset();
363     int tw = static_cast<int>(tabs.width());
364     int th = static_cast<int>(tabs.height());
365 
366     switch (p) {
367     case TOPLEFT:                          tab_y -= yo;         break;
368     case TOP:         tab_x += (w - tw)/2; tab_y -= yo;         break;
369     case TOPRIGHT:    tab_x +=  w - tw;    tab_y -= yo;         break;
370     case BOTTOMLEFT:                       tab_y +=  h + bw;    break;
371     case BOTTOM:      tab_x += (w - tw)/2; tab_y +=  h + bw;    break;
372     case BOTTOMRIGHT: tab_x +=  w - tw;    tab_y +=  h + bw;    break;
373     case LEFTTOP:     tab_x -=  xo;                             break;
374     case LEFT:        tab_x -=  xo;        tab_y += (h - th)/2; break;
375     case LEFTBOTTOM:  tab_x -=  xo;        tab_y +=  h - th;    break;
376     case RIGHTTOP:    tab_x +=  w + bw;                         break;
377     case RIGHT:       tab_x +=  w + bw;    tab_y += (h - th)/2; break;
378     case RIGHTBOTTOM: tab_x +=  w + bw;    tab_y +=  h - th;    break;
379     }
380 
381     if (tabs.orientation() != orig_orient ||
382         tabs.maxWidthPerClient() != orig_tabwidth) {
383         renderTabContainer();
384         if (m_visible && m_use_tabs) {
385             applyTabContainer();
386             tabs.clear();
387             tabs.show();
388         }
389     }
390 
391     if (tabs.parent()->window() != m_screen.rootWindow().window()) {
392         tabs.reparent(m_screen.rootWindow(), tab_x, tab_y);
393         tabs.clear();
394         m_layeritem.addWindow(tabs);
395     } else {
396         tabs.move(tab_x, tab_y);
397     }
398 }
399 
notifyMoved(bool clear)400 void FbWinFrame::notifyMoved(bool clear) {
401     // not important if no alpha...
402     int alpha = getAlpha(m_state.focused);
403     if (alpha == 255)
404         return;
405 
406     if ((m_tabmode == EXTERNAL && m_use_tabs) || m_use_titlebar) {
407         m_tab_container.parentMoved();
408         m_tab_container.for_each(mem_fun(&FbTk::Button::parentMoved));
409     }
410 
411     if (m_use_titlebar) {
412         if (m_tabmode != INTERNAL)
413             m_label.parentMoved();
414 
415         m_titlebar.parentMoved();
416 
417         forAll(m_buttons_left, mem_fun(&FbTk::Button::parentMoved));
418         forAll(m_buttons_right, mem_fun(&FbTk::Button::parentMoved));
419     }
420 
421     if (m_use_handle) {
422         m_handle.parentMoved();
423         m_grip_left.parentMoved();
424         m_grip_right.parentMoved();
425     }
426 
427     if (clear && (m_use_handle || m_use_titlebar)) {
428         clearAll();
429     } else if (clear && m_tabmode == EXTERNAL && m_use_tabs)
430         m_tab_container.clear();
431 }
432 
clearAll()433 void FbWinFrame::clearAll() {
434 
435     if  (m_use_titlebar) {
436         redrawTitlebar();
437         forAll(m_buttons_left, mem_fun(&FbTk::Button::clear));
438         forAll(m_buttons_right, mem_fun(&FbTk::Button::clear));
439     } else if (m_tabmode == EXTERNAL && m_use_tabs)
440         m_tab_container.clear();
441 
442     if (m_use_handle) {
443         m_handle.clear();
444         m_grip_left.clear();
445         m_grip_right.clear();
446     }
447 }
448 
setFocus(bool newvalue)449 void FbWinFrame::setFocus(bool newvalue) {
450     if (m_state.focused == newvalue)
451         return;
452 
453     m_state.focused = newvalue;
454 
455     if (FbTk::Transparent::haveRender() &&
456         getAlpha(true) != getAlpha(false)) { // different alpha for focused and unfocused
457 
458         int alpha = getAlpha(m_state.focused);
459         int opaque = 255;
460         if (FbTk::Transparent::haveComposite()) {
461             std::swap(alpha, opaque);
462         }
463         m_tab_container.setAlpha(alpha);
464         m_window.setOpaque(opaque);
465     }
466 
467     setBorderWidth();
468 
469     applyAll();
470     clearAll();
471 }
472 
applyState()473 void FbWinFrame::applyState() {
474     applyDecorations(false);
475 
476     const int head = m_screen.getHead(window());
477     int new_x = m_state.x, new_y = m_state.y;
478     unsigned int new_w = m_state.width, new_h = m_state.height;
479 
480     if (m_state.isMaximizedVert()) {
481         new_y = m_screen.maxTop(head);
482         new_h = m_screen.maxBottom(head) - new_y - 2*window().borderWidth();
483         if (!m_screen.getMaxOverTabs()) {
484             new_y += yOffset();
485             new_h -= heightOffset();
486         }
487     }
488     if (m_state.isMaximizedHorz()) {
489         new_x = m_screen.maxLeft(head);
490         new_w = m_screen.maxRight(head) - new_x - 2*window().borderWidth();
491         if (!m_screen.getMaxOverTabs()) {
492             new_x += xOffset();
493             new_w -= widthOffset();
494         }
495     }
496 
497     if (m_state.shaded)
498         new_h = m_titlebar.height();
499 
500     if (m_state.fullscreen) {
501         new_x = m_screen.getHeadX(head);
502         new_y = m_screen.getHeadY(head);
503         new_w = m_screen.getHeadWidth(head);
504         new_h = m_screen.getHeadHeight(head);
505     }
506 
507     moveResize(new_x, new_y, new_w, new_h);
508     frameExtentSig().emit();
509 }
510 
setAlpha(bool focused,int alpha)511 void FbWinFrame::setAlpha(bool focused, int alpha) {
512     m_alpha[focused] = alpha;
513     if (m_state.focused == focused)
514         applyAlpha();
515 }
516 
applyAlpha()517 void FbWinFrame::applyAlpha() {
518     int alpha = getAlpha(m_state.focused);
519     if (FbTk::Transparent::haveComposite())
520         m_window.setOpaque(alpha);
521     else {
522         // don't need to setAlpha, since apply updates them anyway
523         applyAll();
524         clearAll();
525     }
526 }
527 
getAlpha(bool focused) const528 int FbWinFrame::getAlpha(bool focused) const {
529     return m_alpha[focused];
530 }
531 
setDefaultAlpha()532 void FbWinFrame::setDefaultAlpha() {
533     if (getUseDefaultAlpha())
534         return;
535     m_alpha[UNFOCUS] = theme().unfocusedTheme()->alpha();
536     m_alpha[FOCUS] = theme().unfocusedTheme()->alpha();
537     applyAlpha();
538 }
539 
getUseDefaultAlpha() const540 bool FbWinFrame::getUseDefaultAlpha() const {
541     if (m_alpha[UNFOCUS] != theme().unfocusedTheme()->alpha()) {
542         return false;
543     } else if (m_alpha[FOCUS] != theme().focusedTheme()->alpha()) {
544         return false;
545     }
546 
547     return true;
548 }
549 
addLeftButton(FbTk::Button * btn)550 void FbWinFrame::addLeftButton(FbTk::Button *btn) {
551     if (btn == 0) // valid button?
552         return;
553 
554     applyButton(*btn); // setup theme and other stuff
555 
556     m_buttons_left.push_back(btn);
557 }
558 
addRightButton(FbTk::Button * btn)559 void FbWinFrame::addRightButton(FbTk::Button *btn) {
560     if (btn == 0) // valid button?
561         return;
562 
563     applyButton(*btn); // setup theme and other stuff
564 
565     m_buttons_right.push_back(btn);
566 }
567 
removeAllButtons()568 void FbWinFrame::removeAllButtons() {
569 
570     FbTk::STLUtil::destroyAndClear(m_buttons_left);
571     FbTk::STLUtil::destroyAndClear(m_buttons_right);
572 }
573 
createTab(FbTk::Button & button)574 void FbWinFrame::createTab(FbTk::Button &button) {
575     button.show();
576     button.setEventMask(ExposureMask | ButtonPressMask |
577                         ButtonReleaseMask | ButtonMotionMask |
578                         EnterWindowMask);
579     FbTk::EventManager::instance()->add(button, button.window());
580 
581     m_tab_container.insertItem(&button);
582 }
583 
removeTab(IconButton * btn)584 void FbWinFrame::removeTab(IconButton *btn) {
585     if (m_tab_container.removeItem(btn))
586         delete btn;
587 }
588 
589 
moveLabelButtonLeft(FbTk::TextButton & btn)590 void FbWinFrame::moveLabelButtonLeft(FbTk::TextButton &btn) {
591     m_tab_container.moveItem(&btn, -1);
592 }
593 
moveLabelButtonRight(FbTk::TextButton & btn)594 void FbWinFrame::moveLabelButtonRight(FbTk::TextButton &btn) {
595     m_tab_container.moveItem(&btn, +1);
596 }
597 
moveLabelButtonTo(FbTk::TextButton & btn,int x,int y)598 void FbWinFrame::moveLabelButtonTo(FbTk::TextButton &btn, int x, int y) {
599     m_tab_container.moveItemTo(&btn, x, y);
600 }
601 
602 
603 
moveLabelButtonLeftOf(FbTk::TextButton & btn,const FbTk::TextButton & dest)604 void FbWinFrame::moveLabelButtonLeftOf(FbTk::TextButton &btn, const FbTk::TextButton &dest) {
605     int dest_pos = m_tab_container.find(&dest);
606     int cur_pos = m_tab_container.find(&btn);
607     if (dest_pos < 0 || cur_pos < 0)
608         return;
609     int movement=dest_pos - cur_pos;
610     if(movement>0)
611         movement-=1;
612 //    else
613   //      movement-=1;
614 
615     m_tab_container.moveItem(&btn, movement);
616 }
617 
moveLabelButtonRightOf(FbTk::TextButton & btn,const FbTk::TextButton & dest)618 void FbWinFrame::moveLabelButtonRightOf(FbTk::TextButton &btn, const FbTk::TextButton &dest) {
619     int dest_pos = m_tab_container.find(&dest);
620     int cur_pos = m_tab_container.find(&btn);
621     if (dest_pos < 0 || cur_pos < 0 )
622         return;
623     int movement=dest_pos - cur_pos;
624     if(movement<0)
625         movement+=1;
626 
627     m_tab_container.moveItem(&btn, movement);
628 }
629 
setClientWindow(FbTk::FbWindow & win)630 void FbWinFrame::setClientWindow(FbTk::FbWindow &win) {
631 
632     win.setBorderWidth(0);
633 
634     XChangeSaveSet(win.display(), win.window(), SetModeInsert);
635 
636     m_window.setEventMask(NoEventMask);
637 
638     // we need to mask this so we don't get unmap event
639     win.setEventMask(NoEventMask);
640     win.reparent(m_window, clientArea().x(), clientArea().y());
641 
642     m_window.setEventMask(ButtonPressMask | ButtonReleaseMask |
643                           ButtonMotionMask | EnterWindowMask |
644                           LeaveWindowMask | SubstructureRedirectMask);
645 
646     XFlush(win.display());
647 
648     // remask window so we get events
649     XSetWindowAttributes attrib_set;
650     attrib_set.event_mask = PropertyChangeMask | StructureNotifyMask | FocusChangeMask | KeyPressMask;
651     attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
652         ButtonMotionMask;
653 
654     XChangeWindowAttributes(win.display(), win.window(), CWEventMask|CWDontPropagate, &attrib_set);
655 
656     if (isVisible())
657         win.show();
658     win.raise();
659     m_window.showSubwindows();
660 
661 }
662 
hideTabs()663 bool FbWinFrame::hideTabs() {
664     if (m_tabmode == INTERNAL || !m_use_tabs) {
665         m_use_tabs = false;
666         return false;
667     }
668 
669     m_use_tabs = false;
670     m_tab_container.hide();
671     return true;
672 }
673 
showTabs()674 bool FbWinFrame::showTabs() {
675     if (m_tabmode == INTERNAL || m_use_tabs) {
676         m_use_tabs = true;
677         return false; // nothing changed
678     }
679 
680     m_use_tabs = true;
681     if (m_visible)
682         m_tab_container.show();
683     return true;
684 }
685 
hideTitlebar()686 bool FbWinFrame::hideTitlebar() {
687     if (!m_use_titlebar)
688         return false;
689 
690     m_titlebar.hide();
691     m_use_titlebar = false;
692 
693     int h = height();
694     int th = m_titlebar.height();
695     int tbw = m_titlebar.borderWidth();
696 
697     // only take away one borderwidth (as the other border is still the "top"
698     // border)
699     h = std::max(1, h - th - tbw);
700     m_window.resize(m_window.width(), h);
701 
702     return true;
703 }
704 
showTitlebar()705 bool FbWinFrame::showTitlebar() {
706     if (m_use_titlebar)
707         return false;
708 
709     m_titlebar.show();
710     m_use_titlebar = true;
711 
712     // only add one borderwidth (as the other border is still the "top"
713     // border)
714     m_window.resize(m_window.width(), m_window.height() + m_titlebar.height() +
715                     m_titlebar.borderWidth());
716 
717     return true;
718 
719 }
720 
hideHandle()721 bool FbWinFrame::hideHandle() {
722     if (!m_use_handle)
723         return false;
724 
725     m_handle.hide();
726     m_grip_left.hide();
727     m_grip_right.hide();
728     m_use_handle = false;
729 
730     int h = m_window.height();
731     int hh = m_handle.height();
732     int hbw = m_handle.borderWidth();
733 
734     // only take away one borderwidth (as the other border is still the "top"
735     // border)
736     h = std::max(1, h - hh - hbw);
737     m_window.resize(m_window.width(), h);
738 
739     return true;
740 }
741 
showHandle()742 bool FbWinFrame::showHandle() {
743     if (m_use_handle || theme()->handleWidth() == 0)
744         return false;
745 
746     m_use_handle = true;
747 
748     // weren't previously rendered...
749     renderHandles();
750     applyHandles();
751 
752     m_handle.show();
753     m_handle.showSubwindows(); // shows grips
754 
755     m_window.resize(m_window.width(), m_window.height() + m_handle.height() +
756                     m_handle.borderWidth());
757     return true;
758 }
759 
760 /**
761    Set new event handler for the frame's windows
762 */
setEventHandler(FbTk::EventHandler & evh)763 void FbWinFrame::setEventHandler(FbTk::EventHandler &evh) {
764 
765     FbTk::EventManager &evm = *FbTk::EventManager::instance();
766     evm.add(evh, m_tab_container);
767     evm.add(evh, m_label);
768     evm.add(evh, m_titlebar);
769     evm.add(evh, m_handle);
770     evm.add(evh, m_grip_right);
771     evm.add(evh, m_grip_left);
772     evm.add(evh, m_window);
773 }
774 
775 /**
776    remove event handler from windows
777 */
removeEventHandler()778 void FbWinFrame::removeEventHandler() {
779     FbTk::EventManager &evm = *FbTk::EventManager::instance();
780     evm.remove(m_tab_container);
781     evm.remove(m_label);
782     evm.remove(m_titlebar);
783     evm.remove(m_handle);
784     evm.remove(m_grip_right);
785     evm.remove(m_grip_left);
786     evm.remove(m_window);
787 }
788 
exposeEvent(XExposeEvent & event)789 void FbWinFrame::exposeEvent(XExposeEvent &event) {
790     FbTk::FbWindow* win = 0;
791     if (m_titlebar == event.window) {
792         win = &m_titlebar;
793     } else if (m_tab_container == event.window) {
794         win = &m_tab_container;
795     } else if (m_label == event.window) {
796         win = &m_label;
797     } else if (m_handle == event.window) {
798         win = &m_handle;
799     } else if (m_grip_left == event.window) {
800         win = &m_grip_left;
801     } else if (m_grip_right == event.window) {
802         win = &m_grip_right;
803     } else {
804 
805         if (m_tab_container.tryExposeEvent(event))
806             return;
807 
808         // create compare function
809         // that we should use with find_if
810         FbTk::CompareEqual_base<FbTk::FbWindow, Window> compare(&FbTk::FbWindow::window,
811                                                                 event.window);
812 
813         ButtonList::iterator it = find_if(m_buttons_left.begin(),
814                                           m_buttons_left.end(),
815                                           compare);
816         if (it != m_buttons_left.end()) {
817             (*it)->exposeEvent(event);
818             return;
819         }
820 
821         it = find_if(m_buttons_right.begin(),
822                      m_buttons_right.end(),
823                      compare);
824 
825         if (it != m_buttons_right.end())
826             (*it)->exposeEvent(event);
827 
828         return;
829     }
830 
831     win->clearArea(event.x, event.y, event.width, event.height);
832 }
833 
handleEvent(XEvent & event)834 void FbWinFrame::handleEvent(XEvent &event) {
835     if (event.type == ConfigureNotify && event.xconfigure.window == window().window())
836         configureNotifyEvent(event.xconfigure);
837 }
838 
configureNotifyEvent(XConfigureEvent & event)839 void FbWinFrame::configureNotifyEvent(XConfigureEvent &event) {
840     resize(event.width, event.height);
841 }
842 
reconfigure()843 void FbWinFrame::reconfigure() {
844     if (m_tab_container.empty())
845         return;
846 
847     int grav_x=0, grav_y=0;
848     // negate gravity
849     gravityTranslate(grav_x, grav_y, -sizeHints().win_gravity, m_active_orig_client_bw, false);
850 
851     m_bevel = theme()->bevelWidth();
852 
853     unsigned int orig_handle_h = handle().height();
854     if (m_use_handle && orig_handle_h != theme()->handleWidth())
855         m_window.resize(m_window.width(), m_window.height() -
856                         orig_handle_h + theme()->handleWidth());
857 
858     handle().resize(handle().width(), theme()->handleWidth());
859     gripLeft().resize(buttonHeight(), theme()->handleWidth());
860     gripRight().resize(gripLeft().width(), gripLeft().height());
861 
862     // align titlebar and render it
863     if (m_use_titlebar) {
864         reconfigureTitlebar();
865         m_titlebar.raise();
866     } else
867         m_titlebar.lower();
868 
869     if (m_tabmode == EXTERNAL) {
870         unsigned int h = buttonHeight();
871         unsigned int w = m_tab_container.width();
872         if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
873             w = m_tab_container.height();
874             std::swap(w, h);
875         }
876         m_tab_container.resize(w, h);
877         alignTabs();
878     }
879 
880     // leave client+grips alone if we're shaded (it'll get fixed when we unshade)
881     if (!m_state.shaded || m_state.fullscreen) {
882         int client_top = 0;
883         int client_height = m_window.height();
884         if (m_use_titlebar) {
885             // only one borderwidth as titlebar is really at -borderwidth
886             int titlebar_height = m_titlebar.height() + m_titlebar.borderWidth();
887             client_top += titlebar_height;
888             client_height -= titlebar_height;
889         }
890 
891         // align handle and grips
892         const int grip_height = m_handle.height();
893         const int grip_width = 20; //TODO
894         const int handle_bw = static_cast<signed>(m_handle.borderWidth());
895 
896         int ypos = m_window.height();
897 
898         // if the handle isn't on, it's actually below the window
899         if (m_use_handle)
900             ypos -= grip_height + handle_bw;
901 
902         // we do handle settings whether on or not so that if they get toggled
903         // then things are ok...
904         m_handle.invalidateBackground();
905         m_handle.moveResize(-handle_bw, ypos,
906                             m_window.width(), grip_height);
907 
908         m_grip_left.invalidateBackground();
909         m_grip_left.moveResize(-handle_bw, -handle_bw,
910                                grip_width, grip_height);
911 
912         m_grip_right.invalidateBackground();
913         m_grip_right.moveResize(m_handle.width() - grip_width - handle_bw, -handle_bw,
914                                 grip_width, grip_height);
915 
916         if (m_use_handle) {
917             m_handle.raise();
918             client_height -= m_handle.height() + m_handle.borderWidth();
919         } else {
920             m_handle.lower();
921         }
922 
923         m_clientarea.moveResize(0, client_top,
924                                 m_window.width(), client_height);
925     }
926 
927     gravityTranslate(grav_x, grav_y, sizeHints().win_gravity, m_active_orig_client_bw, false);
928     // if the location changes, shift it
929     if (grav_x != 0 || grav_y != 0)
930         move(grav_x + x(), grav_y + y());
931 
932     // render the theme
933     if (isVisible()) {
934         // update transparency settings
935         if (FbTk::Transparent::haveRender()) {
936             int alpha = getAlpha(m_state.focused);
937             int opaque = 255;
938             if (FbTk::Transparent::haveComposite()) {
939                 std::swap(alpha, opaque);
940             }
941             m_tab_container.setAlpha(alpha);
942             m_window.setOpaque(opaque);
943         }
944         renderAll();
945         applyAll();
946         clearAll();
947     } else {
948         m_need_render = true;
949     }
950 
951     m_shape.setPlaces(getShape());
952     m_shape.setShapeOffsets(0, titlebarHeight());
953 
954     // titlebar stuff rendered already by reconftitlebar
955 }
956 
setShapingClient(FbTk::FbWindow * win,bool always_update)957 void FbWinFrame::setShapingClient(FbTk::FbWindow *win, bool always_update) {
958     m_shape.setShapeSource(win, 0, titlebarHeight(), always_update);
959 }
960 
buttonHeight() const961 unsigned int FbWinFrame::buttonHeight() const {
962     return m_titlebar.height() - m_bevel*2;
963 }
964 
965 //--------------------- private area
966 
967 /**
968    aligns and redraws title
969 */
redrawTitlebar()970 void FbWinFrame::redrawTitlebar() {
971     if (!m_use_titlebar || m_tab_container.empty())
972         return;
973 
974     if (isVisible()) {
975         m_tab_container.clear();
976         m_label.clear();
977         m_titlebar.clear();
978     }
979 }
980 
981 /**
982    Align buttons with title text window
983 */
reconfigureTitlebar()984 void FbWinFrame::reconfigureTitlebar() {
985     if (!m_use_titlebar)
986         return;
987 
988     int orig_height = m_titlebar.height();
989     // resize titlebar to window size with font height
990     int title_height = theme()->font().height() == 0 ? 16 :
991         theme()->font().height() + m_bevel*2 + 2;
992     if (theme()->titleHeight() != 0)
993         title_height = theme()->titleHeight();
994 
995     // if the titlebar grows in size, make sure the whole window does too
996     if (orig_height != title_height)
997         m_window.resize(m_window.width(), m_window.height()-orig_height+title_height);
998     m_titlebar.invalidateBackground();
999     m_titlebar.moveResize(-m_titlebar.borderWidth(), -m_titlebar.borderWidth(),
1000                           m_window.width(), title_height);
1001 
1002     // draw left buttons first
1003     unsigned int next_x = m_bevel;
1004     unsigned int button_size = buttonHeight();
1005     m_button_size = button_size;
1006     for (size_t i=0; i < m_buttons_left.size(); i++, next_x += button_size + m_bevel) {
1007         // probably on theme reconfigure, leave bg alone for now
1008         m_buttons_left[i]->invalidateBackground();
1009         m_buttons_left[i]->moveResize(next_x, m_bevel,
1010                                       button_size, button_size);
1011     }
1012 
1013     next_x += m_bevel;
1014 
1015     // space left on titlebar between left and right buttons
1016     int space_left = m_titlebar.width() - next_x;
1017 
1018     if (!m_buttons_right.empty())
1019         space_left -= m_buttons_right.size() * (button_size + m_bevel);
1020 
1021     space_left -= m_bevel;
1022 
1023     if (space_left <= 0)
1024         space_left = 1;
1025 
1026     m_label.invalidateBackground();
1027     m_label.moveResize(next_x, m_bevel, space_left, button_size);
1028 
1029     m_tab_container.invalidateBackground();
1030     if (m_tabmode == INTERNAL)
1031         m_tab_container.moveResize(next_x, m_bevel, space_left, button_size);
1032     else {
1033         if (m_use_tabs) {
1034             if (m_tab_container.orientation() == FbTk::ROT0) {
1035                 m_tab_container.resize(m_tab_container.width(), button_size);
1036             } else {
1037                 m_tab_container.resize(button_size, m_tab_container.height());
1038             }
1039         }
1040     }
1041 
1042     next_x += m_label.width() + m_bevel;
1043 
1044     // finaly set new buttons to the right
1045     for (size_t i=0; i < m_buttons_right.size();
1046          ++i, next_x += button_size + m_bevel) {
1047         m_buttons_right[i]->invalidateBackground();
1048         m_buttons_right[i]->moveResize(next_x, m_bevel,
1049                                        button_size, button_size);
1050     }
1051 
1052     m_titlebar.raise(); // always on top
1053 }
1054 
renderAll()1055 void FbWinFrame::renderAll() {
1056     m_need_render = false;
1057 
1058     renderTitlebar();
1059     renderHandles();
1060     renderTabContainer();
1061 }
1062 
applyAll()1063 void FbWinFrame::applyAll() {
1064     applyTitlebar();
1065     applyHandles();
1066     applyTabContainer();
1067 }
1068 
renderTitlebar()1069 void FbWinFrame::renderTitlebar() {
1070     if (!m_use_titlebar)
1071         return;
1072 
1073     if (!isVisible()) {
1074         m_need_render = true;
1075         return;
1076     }
1077 
1078     typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
1079     TP& ft = theme().focusedTheme();
1080     TP& uft = theme().unfocusedTheme();
1081 
1082     // render pixmaps
1083     render(m_title_face.color[FOCUS], m_title_face.pm[FOCUS], m_titlebar.width(), m_titlebar.height(),
1084            ft->titleTexture(), m_imagectrl);
1085 
1086     render(m_title_face.color[UNFOCUS], m_title_face.pm[UNFOCUS], m_titlebar.width(), m_titlebar.height(),
1087            uft->titleTexture(), m_imagectrl);
1088 
1089     //!! TODO: don't render label if internal tabs
1090 
1091     render(m_label_face.color[FOCUS], m_label_face.pm[FOCUS], m_label.width(), m_label.height(),
1092            ft->iconbarTheme()->texture(), m_imagectrl);
1093 
1094     render(m_label_face.color[UNFOCUS], m_label_face.pm[UNFOCUS], m_label.width(), m_label.height(),
1095            uft->iconbarTheme()->texture(), m_imagectrl);
1096 }
1097 
renderTabContainer()1098 void FbWinFrame::renderTabContainer() {
1099     if (!isVisible()) {
1100         m_need_render = true;
1101         return;
1102     }
1103 
1104     typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
1105     TP& ft = theme().focusedTheme();
1106     TP& uft = theme().unfocusedTheme();
1107     FbTk::Container& tabs = tabcontainer();
1108     const FbTk::Texture *tc_focused = &ft->iconbarTheme()->texture();
1109     const FbTk::Texture *tc_unfocused = &uft->iconbarTheme()->texture();
1110 
1111     if (m_tabmode == EXTERNAL && tc_focused->type() & FbTk::Texture::PARENTRELATIVE)
1112         tc_focused = &ft->titleTexture();
1113     if (m_tabmode == EXTERNAL && tc_unfocused->type() & FbTk::Texture::PARENTRELATIVE)
1114         tc_unfocused = &uft->titleTexture();
1115 
1116     render(m_tabcontainer_face.color[FOCUS], m_tabcontainer_face.pm[FOCUS],
1117            tabs.width(), tabs.height(), *tc_focused, m_imagectrl, tabs.orientation());
1118 
1119     render(m_tabcontainer_face.color[UNFOCUS], m_tabcontainer_face.pm[UNFOCUS],
1120            tabs.width(), tabs.height(), *tc_unfocused, m_imagectrl, tabs.orientation());
1121 
1122     renderButtons();
1123 
1124 }
1125 
applyTitlebar()1126 void FbWinFrame::applyTitlebar() {
1127 
1128     int f = m_state.focused;
1129     int alpha = getAlpha(f);
1130     m_titlebar.setAlpha(alpha);
1131     m_label.setAlpha(alpha);
1132 
1133     if (m_tabmode != INTERNAL) {
1134         m_label.setGC(theme()->iconbarTheme()->text().textGC());
1135         m_label.setJustify(theme()->iconbarTheme()->text().justify());
1136 
1137         bg_pm_or_color(m_label, m_label_face.pm[f], m_label_face.color[f]);
1138     }
1139 
1140     bg_pm_or_color(m_titlebar, m_title_face.pm[f], m_title_face.color[f]);
1141     applyButtons();
1142 }
1143 
1144 
renderHandles()1145 void FbWinFrame::renderHandles() {
1146     if (!m_use_handle)
1147         return;
1148 
1149     if (!isVisible()) {
1150         m_need_render = true;
1151         return;
1152     }
1153 
1154     typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
1155     TP& ft = theme().focusedTheme();
1156     TP& uft = theme().unfocusedTheme();
1157 
1158     render(m_handle_face.color[FOCUS], m_handle_face.pm[FOCUS],
1159            m_handle.width(), m_handle.height(),
1160            ft->handleTexture(), m_imagectrl);
1161 
1162     render(m_handle_face.color[UNFOCUS], m_handle_face.pm[UNFOCUS],
1163            m_handle.width(), m_handle.height(),
1164            uft->handleTexture(), m_imagectrl);
1165 
1166     render(m_grip_face.color[FOCUS], m_grip_face.pm[FOCUS],
1167            m_grip_left.width(), m_grip_left.height(),
1168            ft->gripTexture(), m_imagectrl);
1169 
1170     render(m_grip_face.color[UNFOCUS], m_grip_face.pm[UNFOCUS],
1171            m_grip_left.width(), m_grip_left.height(),
1172            uft->gripTexture(), m_imagectrl);
1173 }
1174 
applyHandles()1175 void FbWinFrame::applyHandles() {
1176 
1177     bool f = m_state.focused;
1178     int alpha = getAlpha(f);
1179 
1180     m_handle.setAlpha(alpha);
1181     bg_pm_or_color(m_handle, m_handle_face.pm[f], m_handle_face.color[f]);
1182 
1183     m_grip_left.setAlpha(alpha);
1184     m_grip_right.setAlpha(alpha);
1185 
1186     bg_pm_or_color(m_grip_left, m_grip_face.pm[f], m_grip_face.color[f]);
1187     bg_pm_or_color(m_grip_right, m_grip_face.pm[f], m_grip_face.color[f]);
1188 }
1189 
renderButtons()1190 void FbWinFrame::renderButtons() {
1191 
1192     if (!isVisible()) {
1193         m_need_render = true;
1194         return;
1195     }
1196 
1197     typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
1198     TP& ft = theme().focusedTheme();
1199     TP& uft = theme().unfocusedTheme();
1200 
1201     render(m_button_face.color[UNFOCUS], m_button_face.pm[UNFOCUS],
1202            m_button_size, m_button_size,
1203            uft->buttonTexture(), m_imagectrl);
1204 
1205     render(m_button_face.color[FOCUS], m_button_face.pm[FOCUS],
1206            m_button_size, m_button_size,
1207            ft->buttonTexture(), m_imagectrl);
1208 
1209     render(m_button_face.color[PRESSED], m_button_face.pm[PRESSED],
1210            m_button_size, m_button_size,
1211            theme()->buttonPressedTexture(), m_imagectrl);
1212 
1213 }
1214 
applyButtons()1215 void FbWinFrame::applyButtons() {
1216     // setup left and right buttons
1217     for (size_t i=0; i < m_buttons_left.size(); ++i)
1218         applyButton(*m_buttons_left[i]);
1219 
1220     for (size_t i=0; i < m_buttons_right.size(); ++i)
1221         applyButton(*m_buttons_right[i]);
1222 }
1223 
init()1224 void FbWinFrame::init() {
1225 
1226     if (theme()->handleWidth() == 0)
1227         m_use_handle = false;
1228 
1229     m_alpha[UNFOCUS] = theme().unfocusedTheme()->alpha();
1230     m_alpha[FOCUS] = theme().focusedTheme()->alpha();
1231 
1232     m_handle.showSubwindows();
1233 
1234     // clear pixmaps
1235     m_title_face.pm[UNFOCUS] = m_title_face.pm[FOCUS] = 0;
1236     m_label_face.pm[UNFOCUS] = m_label_face.pm[FOCUS] = 0;
1237     m_tabcontainer_face.pm[UNFOCUS] = m_tabcontainer_face.pm[FOCUS] = 0;
1238     m_handle_face.pm[UNFOCUS] = m_handle_face.pm[FOCUS] = 0;
1239     m_button_face.pm[UNFOCUS] = m_button_face.pm[FOCUS] = m_button_face.pm[PRESSED] = 0;
1240     m_grip_face.pm[UNFOCUS] = m_grip_face.pm[FOCUS] = 0;
1241 
1242     m_button_size = s_button_size;
1243 
1244     m_label.setBorderWidth(0);
1245 
1246     setTabMode(NOTSET);
1247 
1248     m_label.setEventMask(ExposureMask | ButtonPressMask |
1249                          ButtonReleaseMask | ButtonMotionMask |
1250                          EnterWindowMask);
1251 
1252     showHandle();
1253     showTitlebar();
1254 
1255     // Note: we don't show clientarea yet
1256 
1257     setEventHandler(*this);
1258 
1259     // setup cursors for resize grips
1260     gripLeft().setCursor(theme()->lowerLeftAngleCursor());
1261     gripRight().setCursor(theme()->lowerRightAngleCursor());
1262 }
1263 
1264 /**
1265    Setups upp background, pressed pixmap/color of the button to current theme
1266 */
applyButton(FbTk::Button & btn)1267 void FbWinFrame::applyButton(FbTk::Button &btn) {
1268 
1269     FbWinFrame::BtnFace& face = m_button_face;
1270 
1271     if (m_button_face.pm[PRESSED]) {
1272         btn.setPressedPixmap(face.pm[PRESSED]);
1273     } else {
1274         btn.setPressedColor(face.color[PRESSED]);
1275     }
1276 
1277     bool f = m_state.focused;
1278 
1279     btn.setAlpha(getAlpha(f));
1280     btn.setGC(theme()->buttonPicGC());
1281 
1282     bg_pm_or_color(btn, face.pm[f], face.color[f]);
1283 }
1284 
1285 
applyTabContainer()1286 void FbWinFrame::applyTabContainer() {
1287 
1288     FbTk::Container& tabs = tabcontainer();
1289     FbWinFrame::Face& face = m_tabcontainer_face;
1290 
1291     tabs.setAlpha(getAlpha(m_state.focused));
1292     bg_pm_or_color(tabs, face.pm[m_state.focused], face.color[m_state.focused]);
1293 
1294     // and the labelbuttons in it
1295     FbTk::Container::ItemList::iterator btn_it = m_tab_container.begin();
1296     FbTk::Container::ItemList::iterator btn_it_end = m_tab_container.end();
1297     for (; btn_it != btn_it_end; ++btn_it) {
1298         IconButton *btn = static_cast<IconButton *>(*btn_it);
1299         btn->reconfigTheme();
1300     }
1301 }
1302 
getShape() const1303 int FbWinFrame::getShape() const {
1304     int shape = theme()->shapePlace();
1305     if (!m_state.useTitlebar())
1306         shape &= ~(FbTk::Shape::TOPRIGHT|FbTk::Shape::TOPLEFT);
1307     if (!m_state.useHandle())
1308         shape &= ~(FbTk::Shape::BOTTOMRIGHT|FbTk::Shape::BOTTOMLEFT);
1309     return shape;
1310 }
1311 
applyDecorations(bool do_move)1312 void FbWinFrame::applyDecorations(bool do_move) {
1313     int grav_x=0, grav_y=0;
1314     // negate gravity
1315     gravityTranslate(grav_x, grav_y, -sizeHints().win_gravity, m_active_orig_client_bw,
1316                      false);
1317 
1318     bool client_move = setBorderWidth(false);
1319 
1320     // tab deocration only affects if we're external
1321     // must do before the setTabMode in case it goes
1322     // to external and is meant to be hidden
1323     if (m_state.useTabs())
1324         client_move |= showTabs();
1325     else
1326         client_move |= hideTabs();
1327 
1328     // we rely on frame not doing anything if it is already shown/hidden
1329     if (m_state.useTitlebar()) {
1330         client_move |= showTitlebar();
1331         if (m_screen.getDefaultInternalTabs())
1332             client_move |= setTabMode(INTERNAL);
1333         else
1334             client_move |= setTabMode(EXTERNAL);
1335     } else {
1336         client_move |= hideTitlebar();
1337         if (m_state.useTabs())
1338             client_move |= setTabMode(EXTERNAL);
1339     }
1340 
1341     if (m_state.useHandle())
1342         client_move |= showHandle();
1343     else
1344         client_move |= hideHandle();
1345 
1346     // apply gravity once more
1347     gravityTranslate(grav_x, grav_y, sizeHints().win_gravity, m_active_orig_client_bw,
1348                      false);
1349 
1350     // if the location changes, shift it
1351     if (do_move && (grav_x != 0 || grav_y != 0)) {
1352         move(grav_x + x(), grav_y + y());
1353         client_move = true;
1354     }
1355 
1356     if (do_move) {
1357         reconfigure();
1358         m_state.saveGeometry(x(), y(), width(), height());
1359     }
1360     if (client_move)
1361         frameExtentSig().emit();
1362 }
1363 
setBorderWidth(bool do_move)1364 bool FbWinFrame::setBorderWidth(bool do_move) {
1365     unsigned int border_width = theme()->border().width();
1366     unsigned int win_bw = m_state.useBorder() ? border_width : 0;
1367 
1368     if (border_width &&
1369         theme()->border().color().pixel() != window().borderColor()) {
1370         FbTk::Color c = theme()->border().color();
1371         window().setBorderColor(c);
1372         titlebar().setBorderColor(c);
1373         handle().setBorderColor(c);
1374         gripLeft().setBorderColor(c);
1375         gripRight().setBorderColor(c);
1376         tabcontainer().setBorderColor(c);
1377     }
1378 
1379     if (border_width == handle().borderWidth() &&
1380         win_bw == window().borderWidth())
1381         return false;
1382 
1383     int grav_x=0, grav_y=0;
1384     // negate gravity
1385     if (do_move)
1386         gravityTranslate(grav_x, grav_y, -sizeHints().win_gravity,
1387                          m_active_orig_client_bw, false);
1388 
1389     int bw_changes = 0;
1390     // we need to change the size of the window
1391     // if the border width changes...
1392     if (m_use_titlebar)
1393         bw_changes += static_cast<signed>(border_width - titlebar().borderWidth());
1394     if (m_use_handle)
1395         bw_changes += static_cast<signed>(border_width - handle().borderWidth());
1396 
1397     window().setBorderWidth(win_bw);
1398 
1399     setTabMode(NOTSET);
1400 
1401     titlebar().setBorderWidth(border_width);
1402     handle().setBorderWidth(border_width);
1403     gripLeft().setBorderWidth(border_width);
1404     gripRight().setBorderWidth(border_width);
1405 
1406     if (bw_changes != 0)
1407         resize(width(), height() + bw_changes);
1408 
1409     if (m_tabmode == EXTERNAL)
1410         alignTabs();
1411 
1412     if (do_move) {
1413         frameExtentSig().emit();
1414         gravityTranslate(grav_x, grav_y, sizeHints().win_gravity,
1415                          m_active_orig_client_bw, false);
1416         // if the location changes, shift it
1417         if (grav_x != 0 || grav_y != 0)
1418             move(grav_x + x(), grav_y + y());
1419     }
1420 
1421     return true;
1422 }
1423 
1424 // this function translates its arguments according to win_gravity
1425 // if win_gravity is negative, it does an inverse translation
1426 // This function should be used when a window is mapped/unmapped/pos configured
gravityTranslate(int & x,int & y,int win_gravity,unsigned int client_bw,bool move_frame)1427 void FbWinFrame::gravityTranslate(int &x, int &y,
1428                                   int win_gravity, unsigned int client_bw, bool move_frame) {
1429     bool invert = false;
1430     if (win_gravity < 0) {
1431         invert = true;
1432         win_gravity = -win_gravity; // make +ve
1433     }
1434 
1435     /* Ok, so, gravity says which point of the frame is put where the
1436      * corresponding bit of window would have been
1437      * Thus, x,y always refers to where top left of the WINDOW would be placed
1438      * but given that we're wrapping it in a frame, we actually place
1439      * it so that the given reference point is in the same spot as the
1440      * window's reference point would have been.
1441      * i.e. east gravity says that the centre of the right hand side of the
1442      * frame is placed where the centre of the rhs of the window would
1443      * have been if there was no frame.
1444      * Hope that makes enough sense.
1445      *
1446      * NOTE: the gravity calculations are INDEPENDENT of the client
1447      *       window width/height.
1448      *
1449      * If you get confused with the calculations, draw a picture.
1450      *
1451      */
1452 
1453     // We calculate offsets based on the gravity and frame aspects
1454     // and at the end apply those offsets +ve or -ve depending on 'invert'
1455 
1456     // These will be set to the resulting offsets for adjusting the frame position
1457     int x_offset = 0;
1458     int y_offset = 0;
1459 
1460     // These are the amount that the frame is larger than the client window
1461     // Note that the client window's x,y is offset by it's borderWidth, which
1462     // is removed by fluxbox, so the gravity needs to account for this change
1463 
1464     // these functions already check if the title/handle is used
1465     int bw = static_cast<int>(m_window.borderWidth());
1466     int bw_diff = static_cast<int>(client_bw) - bw;
1467     int height_diff = 2*bw_diff - static_cast<int>(titlebarHeight()) - static_cast<int>(handleHeight());
1468     int width_diff = 2*bw_diff;
1469 
1470     if (win_gravity == SouthWestGravity || win_gravity == SouthGravity ||
1471         win_gravity == SouthEastGravity)
1472         y_offset = height_diff;
1473 
1474     if (win_gravity == WestGravity || win_gravity == CenterGravity ||
1475         win_gravity == EastGravity)
1476         y_offset = height_diff/2;
1477 
1478     if (win_gravity == NorthEastGravity || win_gravity == EastGravity ||
1479         win_gravity == SouthEastGravity)
1480         x_offset = width_diff;
1481 
1482     if (win_gravity == NorthGravity || win_gravity == CenterGravity ||
1483         win_gravity == SouthGravity)
1484         x_offset = width_diff/2;
1485 
1486     if (win_gravity == StaticGravity) {
1487         x_offset = bw_diff;
1488         y_offset = bw_diff - titlebarHeight();
1489     }
1490 
1491     if (invert) {
1492         x_offset = -x_offset;
1493         y_offset = -y_offset;
1494     }
1495 
1496     x += x_offset;
1497     y += y_offset;
1498 
1499     if (move_frame && (x_offset != 0 || y_offset != 0)) {
1500         move(x, y);
1501     }
1502 }
1503 
widthOffset() const1504 int FbWinFrame::widthOffset() const {
1505     if (m_tabmode != EXTERNAL || !m_use_tabs)
1506         return 0;
1507     if (s_place[m_screen.getTabPlacement()].is_horizontal) {
1508         return 0;
1509     }
1510     return m_tab_container.width() + m_window.borderWidth();
1511 }
1512 
heightOffset() const1513 int FbWinFrame::heightOffset() const {
1514     if (m_tabmode != EXTERNAL || !m_use_tabs)
1515         return 0;
1516 
1517     if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
1518         return 0;
1519     }
1520     return m_tab_container.height() + m_window.borderWidth();
1521 }
1522 
xOffset() const1523 int FbWinFrame::xOffset() const {
1524     if (m_tabmode != EXTERNAL || !m_use_tabs)
1525         return 0;
1526     TabPlacement p = m_screen.getTabPlacement();
1527     if (p == LEFTTOP || p == LEFT || p == LEFTBOTTOM) {
1528         return m_tab_container.width() + m_window.borderWidth();
1529     }
1530     return 0;
1531 }
1532 
yOffset() const1533 int FbWinFrame::yOffset() const {
1534     if (m_tabmode != EXTERNAL || !m_use_tabs)
1535         return 0;
1536     TabPlacement p = m_screen.getTabPlacement();
1537     if (p == TOPLEFT || p == TOP || p == TOPRIGHT) {
1538         return m_tab_container.height() + m_window.borderWidth();
1539     }
1540     return 0;
1541 }
1542 
applySizeHints(unsigned int & width,unsigned int & height,bool maximizing) const1543 void FbWinFrame::applySizeHints(unsigned int &width, unsigned int &height,
1544                                 bool maximizing) const {
1545     const int h = height - titlebarHeight() - handleHeight();
1546     height = max(h, static_cast<int>(titlebarHeight() + handleHeight()));
1547     sizeHints().apply(width, height, maximizing);
1548     height += titlebarHeight() + handleHeight();
1549 }
1550 
displaySize(unsigned int width,unsigned int height) const1551 void FbWinFrame::displaySize(unsigned int width, unsigned int height) const {
1552     unsigned int i, j;
1553     sizeHints().displaySize(i, j,
1554                             width, height - titlebarHeight() - handleHeight());
1555     m_screen.showGeometry(i, j);
1556 }
1557 
insideTitlebar(Window win) const1558 bool FbWinFrame::insideTitlebar(Window win) const {
1559     return
1560         gripLeft().window() != win &&
1561         gripRight().window() != win &&
1562         window().window() != win;
1563 }
1564 
getContext(Window win,int x,int y,int last_x,int last_y,bool doBorders)1565 int FbWinFrame::getContext(Window win, int x, int y, int last_x, int last_y, bool doBorders) {
1566     int context = 0;
1567     if (gripLeft().window()  == win) return Keys::ON_LEFTGRIP;
1568     if (gripRight().window() == win) return Keys::ON_RIGHTGRIP;
1569     if (doBorders) {
1570         using RectangleUtil::insideBorder;
1571         int borderw = window().borderWidth();
1572         if ( // if mouse is currently on the window border, ignore it
1573                 (
1574                     ! insideBorder(window(), x, y, borderw)
1575                     && ( externalTabMode()
1576                         || ! insideBorder(tabcontainer(), x, y, borderw) )
1577                 )
1578                 || // or if mouse was on border when it was last clicked
1579                 (
1580                     ! insideBorder(window(), last_x, last_y, borderw)
1581                     && ( externalTabMode()
1582                         || ! insideBorder(tabcontainer(), last_x, last_y, borderw ) )
1583                 )
1584            ) context = Keys::ON_WINDOWBORDER;
1585     }
1586 
1587     if (window().window()    == win) return context | Keys::ON_WINDOW;
1588     // /!\ old code: handle = titlebar in motionNotifyEvent but only there !
1589     // handle() as border ??
1590     if (handle().window()    == win) return Keys::ON_WINDOWBORDER | Keys::ON_WINDOW;
1591     if (titlebar().window()  == win) return context | Keys::ON_TITLEBAR;
1592     if (label().window()     == win) return context | Keys::ON_TITLEBAR;
1593     // internal tabs are on title bar
1594     if (tabcontainer().window() == win)
1595         return context | Keys::ON_TAB | (externalTabMode()?0:Keys::ON_TITLEBAR);
1596 
1597 
1598     FbTk::Container::ItemList::iterator it = tabcontainer().begin();
1599     FbTk::Container::ItemList::iterator it_end = tabcontainer().end();
1600     for (; it != it_end; ++it) {
1601         if ((*it)->window() == win)
1602             break;
1603     }
1604     // internal tabs are on title bar
1605     if (it != it_end)
1606         return context | Keys::ON_TAB | (externalTabMode()?0:Keys::ON_TITLEBAR);
1607 
1608     return context;
1609 }
1610