]> Dogcows Code - chaz/openbox/blob - src/frame.cc
window decorations use "unmanaged" widgets now.
[chaz/openbox] / src / frame.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 extern "C" {
8 #ifdef SHAPE
9 #include <X11/extensions/shape.h>
10 #endif // SHAPE
11 }
12
13 #include "openbox.hh"
14 #include "frame.hh"
15 #include "client.hh"
16 #include "otk/display.hh"
17
18 #include <string>
19 #include <iostream> // TEMP
20
21 namespace ob {
22
23 OBFrame::OBFrame(OBClient *client, otk::Style *style)
24 : otk::OtkWidget(Openbox::instance, style),
25 _client(client),
26 _screen(otk::OBDisplay::screenInfo(client->screen())),
27 _titlebar(this),
28 _button_close(&_titlebar),
29 _button_iconify(&_titlebar),
30 _button_max(&_titlebar),
31 _button_stick(&_titlebar),
32 _label(&_titlebar),
33 _handle(this),
34 _grip_left(&_handle),
35 _grip_right(&_handle),
36 _decorations(client->decorations())
37 {
38 assert(client);
39 assert(style);
40
41 unmanaged();
42 _titlebar.unmanaged();
43 _button_close.unmanaged();
44 _button_iconify.unmanaged();
45 _button_max.unmanaged();
46 _button_stick.unmanaged();
47 _label.unmanaged();
48 _handle.unmanaged();
49 _grip_left.unmanaged();
50 _grip_right.unmanaged();
51
52 _button_close.setText("X");
53 _button_iconify.setText("I");
54 _button_max.setText("M");
55 _button_stick.setText("C");
56 _label.setText(_client->title());
57
58 _style = 0;
59 setStyle(style);
60
61 grabClient();
62 }
63
64
65 OBFrame::~OBFrame()
66 {
67 releaseClient(false);
68 }
69
70
71 void OBFrame::setStyle(otk::Style *style)
72 {
73 assert(style);
74
75 otk::OtkWidget::setStyle(style);
76
77 // if a style was previously set, then 'replace' is true, cause we're
78 // replacing a style
79 bool replace = (_style);
80
81 if (replace) {
82 // XXX: do shit here whatever
83 // XXX: save the position based on gravity
84 }
85
86 _style = style;
87
88 XSetWindowBorder(otk::OBDisplay::display, getWindow(),
89 _style->getBorderColor()->pixel());
90 XSetWindowBorder(otk::OBDisplay::display, _titlebar.getWindow(),
91 _style->getBorderColor()->pixel());
92 XSetWindowBorder(otk::OBDisplay::display, _grip_left.getWindow(),
93 _style->getBorderColor()->pixel());
94 XSetWindowBorder(otk::OBDisplay::display, _grip_right.getWindow(),
95 _style->getBorderColor()->pixel());
96 XSetWindowBorder(otk::OBDisplay::display, _handle.getWindow(),
97 _style->getBorderColor()->pixel());
98
99 // XXX: if (focused)
100 XSetWindowBackground(otk::OBDisplay::display, getWindow(),
101 _style->getFrameFocus()->color().pixel());
102 // XXX: else
103 // XXX: XSetWindowBackground(otk::OBDisplay::display, _window,
104 // XXX: _style->getFrameUnfocus().color().pixel());
105
106 // if !replace, then adjust() will get called after the client is grabbed!
107 if (replace)
108 adjust(); // size/position everything
109 }
110
111
112 void OBFrame::adjust()
113 {
114 // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
115 _decorations = _client->decorations();
116 _decorations = 0xffffffff;
117
118 int width; // the width of the client window and the border around it
119 int bwidth; // width to make borders
120
121 if (_decorations & OBClient::Decor_Border) {
122 bwidth = _style->getBorderWidth();
123 _size.left = _size.top = _size.bottom = _size.right =
124 _style->getFrameWidth();
125 width = _client->area().width() + _style->getFrameWidth() * 2;
126 } else {
127 bwidth = 0;
128 _size.left = _size.top = _size.bottom = _size.right = 0;
129 width = _client->area().width();
130 }
131 XSetWindowBorderWidth(otk::OBDisplay::display, getWindow(), bwidth);
132 XSetWindowBorderWidth(otk::OBDisplay::display, _titlebar.getWindow(),
133 bwidth);
134 XSetWindowBorderWidth(otk::OBDisplay::display, _grip_left.getWindow(),
135 bwidth);
136 XSetWindowBorderWidth(otk::OBDisplay::display, _grip_right.getWindow(),
137 bwidth);
138 XSetWindowBorderWidth(otk::OBDisplay::display, _handle.getWindow(), bwidth);
139
140 if (_decorations & OBClient::Decor_Titlebar) {
141 // set the titlebar size
142 _titlebar.setGeometry(-bwidth,
143 -bwidth,
144 width,
145 (_style->getFont().height() +
146 _style->getBevelWidth() * 2));
147 _size.top += _titlebar.height() + bwidth;
148
149 // set the label size
150 _label.setGeometry(0, _style->getBevelWidth(),
151 width, _style->getFont().height());
152 // set the buttons sizes
153 if (_decorations & OBClient::Decor_Iconify)
154 _button_iconify.setGeometry(0, _style->getBevelWidth() + 1,
155 _label.height() - 2,
156 _label.height() - 2);
157 if (_decorations & OBClient::Decor_Maximize)
158 _button_max.setGeometry(0, _style->getBevelWidth() + 1,
159 _label.height() - 2,
160 _label.height() - 2);
161 if (_decorations & OBClient::Decor_Sticky)
162 _button_stick.setGeometry(0, _style->getBevelWidth() + 1,
163 _label.height() - 2,
164 _label.height() - 2);
165 if (_decorations & OBClient::Decor_Close)
166 _button_close.setGeometry(0, _style->getBevelWidth() + 1,
167 _label.height() - 2,
168 _label.height() - 2);
169
170 // separation between titlebar elements
171 const int sep = _style->getBevelWidth() + 1;
172
173 std::string layout = "SLIMC"; // XXX: get this from somewhere
174 // XXX: it is REQUIRED that by this point, the string only has one of each
175 // possible letter, all of the letters are valid, and L exists somewhere in
176 // the string!
177
178 // the size of the label. this ASSUMES the layout has only buttons other
179 // that the ONE LABEL!!
180 // adds an extra sep so that there's a space on either side of the
181 // titlebar.. note: x = sep, below.
182 _label.setWidth(_label.width() -
183 ((_button_iconify.width() + sep) *
184 (layout.size() - 1) + sep * 2));
185
186 int x = sep;
187 for (int i = 0, len = layout.size(); i < len; ++i) {
188 switch (layout[i]) {
189 case 'I':
190 _button_iconify.move(x, _button_iconify.getRect().y());
191 x += _button_iconify.width();
192 break;
193 case 'L':
194 _label.move(x, _label.getRect().y());
195 x += _label.width();
196 break;
197 case 'M':
198 _button_max.move(x, _button_max.getRect().y());
199 x += _button_max.width();
200 break;
201 case 'S':
202 _button_stick.move(x, _button_stick.getRect().y());
203 x += _button_stick.width();
204 break;
205 case 'C':
206 _button_close.move(x, _button_close.getRect().y());
207 x += _button_close.width();
208 break;
209 default:
210 assert(false); // the layout string is invalid!
211 }
212 x += sep;
213 }
214 }
215
216 if (_decorations & OBClient::Decor_Handle) {
217 _handle.setGeometry(-bwidth,
218 _size.top + _client->area().height() +
219 _style->getFrameWidth(),
220 width, _style->getHandleWidth());
221 _grip_left.setGeometry(-bwidth,
222 -bwidth,
223 // XXX: get a Point class in otk and use that for
224 // the 'buttons size' since theyre all the same
225 _button_iconify.width() * 2,
226 _handle.height());
227 _grip_right.setGeometry(((_handle.getRect().right() + 1) -
228 _button_iconify.width() * 2),
229 -bwidth,
230 // XXX: get a Point class in otk and use that for
231 // the 'buttons size' since theyre all the same
232 _button_iconify.width() * 2,
233 _handle.height());
234 _size.bottom += _handle.height() + bwidth;
235 }
236
237
238 // position/size all the windows
239
240 resize(_size.left + _size.right + _client->area().width(),
241 _size.top + _size.bottom + _client->area().height());
242
243 XMoveWindow(otk::OBDisplay::display, _client->window(),
244 _size.left, _size.top);
245
246 // map/unmap all the windows
247 if (_decorations & OBClient::Decor_Titlebar) {
248 _label.show();
249 if (_decorations & OBClient::Decor_Iconify)
250 _button_iconify.show();
251 else
252 _button_iconify.hide();
253 if (_decorations & OBClient::Decor_Maximize)
254 _button_max.show();
255 else
256 _button_max.hide();
257 if (_decorations & OBClient::Decor_Sticky)
258 _button_stick.show();
259 else
260 _button_stick.hide();
261 if (_decorations & OBClient::Decor_Close)
262 _button_close.show();
263 else
264 _button_close.hide();
265 _titlebar.show();
266 } else {
267 _titlebar.hide(true);
268 }
269
270 if (_decorations & OBClient::Decor_Handle)
271 _handle.show(true);
272 else
273 _handle.hide(true);
274
275 // XXX: more is gunna have to happen here
276
277 adjustShape();
278
279 update();
280 }
281
282
283 void OBFrame::adjustShape()
284 {
285 #ifdef SHAPE
286 if (!_client->shaped()) {
287 // clear the shape on the frame window
288 XShapeCombineMask(otk::OBDisplay::display, getWindow(), ShapeBounding,
289 _size.left,
290 _size.top,
291 None, ShapeSet);
292 } else {
293 // make the frame's shape match the clients
294 XShapeCombineShape(otk::OBDisplay::display, getWindow(), ShapeBounding,
295 _size.left,
296 _size.top,
297 _client->window(), ShapeBounding, ShapeSet);
298
299 int num = 0;
300 XRectangle xrect[2];
301
302 /*
303 if (decorations & Decor_Titlebar) {
304 xrect[0].x = xrect[0].y = -frame.border_w;
305 xrect[0].width = frame.rect.width();
306 xrect[0].height = frame.title_h + (frame.border_w * 2);
307 ++num;
308 }
309
310 if (decorations & Decor_Handle) {
311 xrect[1].x = -frame.border_w;
312 xrect[1].y = frame.rect.height() - frame.margin.bottom +
313 frame.mwm_border_w - frame.border_w;
314 xrect[1].width = frame.rect.width();
315 xrect[1].height = frame.handle_h + (frame.border_w * 2);
316 ++num;
317 }*/
318
319 XShapeCombineRectangles(otk::OBDisplay::display, getWindow(),
320 ShapeBounding, 0, 0, xrect, num,
321 ShapeUnion, Unsorted);
322 }
323 #endif // SHAPE
324 }
325
326
327 void OBFrame::grabClient()
328 {
329
330 // select the event mask on the frame
331 //XSelectInput(otk::OBDisplay::display, _window, SubstructureRedirectMask);
332
333 // reparent the client to the frame
334 XReparentWindow(otk::OBDisplay::display, _client->window(), getWindow(), 0, 0);
335 _client->ignore_unmaps++;
336
337 // raise the client above the frame
338 //XRaiseWindow(otk::OBDisplay::display, _client->window());
339 // map the client so it maps when the frame does
340 XMapWindow(otk::OBDisplay::display, _client->window());
341
342 adjust();
343 }
344
345
346 void OBFrame::releaseClient(bool remap)
347 {
348 // check if the app has already reparented its window to the root window
349 XEvent ev;
350 if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
351 ReparentNotify, &ev)) {
352 remap = true; // XXX: why do we remap the window if they already
353 // reparented to root?
354 } else {
355 // according to the ICCCM - if the client doesn't reparent to
356 // root, then we have to do it for them
357 XReparentWindow(otk::OBDisplay::display, _client->window(),
358 _screen->getRootWindow(),
359 _client->area().x(), _client->area().y());
360 }
361
362 // if we want to remap the window, do so now
363 if (remap)
364 XMapWindow(otk::OBDisplay::display, _client->window());
365 }
366
367
368 Window OBFrame::createChild(Window parent, Cursor cursor)
369 {
370 XSetWindowAttributes attrib_create;
371 unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
372
373 attrib_create.background_pixmap = None;
374 attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
375 ButtonMotionMask | ExposureMask;
376
377 if (cursor) {
378 create_mask |= CWCursor;
379 attrib_create.cursor = cursor;
380 }
381
382 Window w = XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0,
383 _screen->getDepth(), InputOutput,
384 _screen->getVisual(), create_mask, &attrib_create);
385 return w;
386 }
387
388
389 Window OBFrame::createFrame()
390 {
391 XSetWindowAttributes attrib_create;
392 unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
393 CWOverrideRedirect | CWEventMask;
394
395 attrib_create.background_pixmap = None;
396 attrib_create.colormap = _screen->getColormap();
397 attrib_create.override_redirect = True;
398 attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | ButtonPress;
399 /*
400 We catch button presses because other wise they get passed down to the
401 root window, which will then cause root menus to show when you click the
402 window's frame.
403 */
404
405 return XCreateWindow(otk::OBDisplay::display, _screen->getRootWindow(),
406 0, 0, 1, 1, 0,
407 _screen->getDepth(), InputOutput, _screen->getVisual(),
408 create_mask, &attrib_create);
409 }
410
411 }
This page took 0.054006 seconds and 4 git commands to generate.