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