]> Dogcows Code - chaz/openbox/blob - openbox/popup.c
Make the focus cycle indicator follow target fallback in the popup
[chaz/openbox] / openbox / popup.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 popup.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "popup.h"
21
22 #include "openbox.h"
23 #include "frame.h"
24 #include "client.h"
25 #include "stacking.h"
26 #include "event.h"
27 #include "screen.h"
28 #include "mainloop.h"
29 #include "render/render.h"
30 #include "render/theme.h"
31
32 ObPopup *popup_new(void)
33 {
34 XSetWindowAttributes attrib;
35 ObPopup *self = g_new0(ObPopup, 1);
36
37 self->obwin.type = Window_Internal;
38 self->gravity = NorthWestGravity;
39 self->x = self->y = self->textw = self->h = 0;
40 self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
41 self->a_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
42 self->iconwm = self->iconhm = 1;
43
44 attrib.override_redirect = True;
45 self->bg = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen),
46 0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
47 InputOutput, RrVisual(ob_rr_inst),
48 CWOverrideRedirect, &attrib);
49
50 self->text = XCreateWindow(ob_display, self->bg,
51 0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
52 InputOutput, RrVisual(ob_rr_inst), 0, NULL);
53
54 XSetWindowBorderWidth(ob_display, self->bg, ob_rr_theme->obwidth);
55 XSetWindowBorder(ob_display, self->bg,
56 RrColorPixel(ob_rr_theme->osd_border_color));
57
58 XMapWindow(ob_display, self->text);
59
60 stacking_add(INTERNAL_AS_WINDOW(self));
61 g_hash_table_insert(window_map, &self->bg, self);
62 return self;
63 }
64
65 void popup_free(ObPopup *self)
66 {
67 if (self) {
68 popup_hide(self); /* make sure it's not showing or is being delayed and
69 will be shown */
70 XDestroyWindow(ob_display, self->bg);
71 XDestroyWindow(ob_display, self->text);
72 RrAppearanceFree(self->a_bg);
73 RrAppearanceFree(self->a_text);
74 g_hash_table_remove(window_map, &self->bg);
75 stacking_remove(self);
76 g_free(self);
77 }
78 }
79
80 void popup_position(ObPopup *self, gint gravity, gint x, gint y)
81 {
82 self->gravity = gravity;
83 self->x = x;
84 self->y = y;
85 }
86
87 void popup_text_width(ObPopup *self, gint w)
88 {
89 self->textw = w;
90 }
91
92 void popup_min_width(ObPopup *self, gint minw)
93 {
94 self->minw = minw;
95 }
96
97 void popup_max_width(ObPopup *self, gint maxw)
98 {
99 self->maxw = maxw;
100 }
101
102 void popup_height(ObPopup *self, gint h)
103 {
104 gint texth;
105
106 /* don't let the height be smaller than the text */
107 texth = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
108 self->h = MAX(h, texth);
109 }
110
111 void popup_text_width_to_string(ObPopup *self, gchar *text)
112 {
113 if (text[0] != '\0') {
114 self->a_text->texture[0].data.text.string = text;
115 self->textw = RrMinWidth(self->a_text);
116 } else
117 self->textw = 0;
118 }
119
120 void popup_height_to_string(ObPopup *self, gchar *text)
121 {
122 self->h = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
123 }
124
125 void popup_text_width_to_strings(ObPopup *self, gchar **strings, gint num)
126 {
127 gint i, maxw;
128
129 maxw = 0;
130 for (i = 0; i < num; ++i) {
131 popup_text_width_to_string(self, strings[i]);
132 maxw = MAX(maxw, self->textw);
133 }
134 self->textw = maxw;
135 }
136
137 void popup_set_text_align(ObPopup *self, RrJustify align)
138 {
139 self->a_text->texture[0].data.text.justify = align;
140 }
141
142 static gboolean popup_show_timeout(gpointer data)
143 {
144 ObPopup *self = data;
145
146 XMapWindow(ob_display, self->bg);
147 stacking_raise(INTERNAL_AS_WINDOW(self));
148 self->mapped = TRUE;
149 self->delay_mapped = FALSE;
150
151 return FALSE; /* don't repeat */
152 }
153
154 void popup_delay_show(ObPopup *self, gulong usec, gchar *text)
155 {
156 gint l, t, r, b;
157 gint x, y, w, h;
158 guint m;
159 gint emptyx, emptyy; /* empty space between elements */
160 gint textx, texty, textw, texth;
161 gint iconx, icony, iconw, iconh;
162 Rect *area, mon;
163
164 /* when there is no icon and the text is not parent relative, then
165 fill the whole dialog with the text appearance, don't use the bg at all
166 */
167 if (self->hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
168 RrMargins(self->a_bg, &l, &t, &r, &b);
169 else
170 l = t = r = b = 0;
171
172 /* set up the textures */
173 self->a_text->texture[0].data.text.string = text;
174
175 /* measure the text out */
176 if (text[0] != '\0') {
177 RrMinSize(self->a_text, &textw, &texth);
178 } else {
179 textw = 0;
180 texth = RrMinHeight(self->a_text);
181 }
182
183 /* get the height, which is also used for the icon width */
184 emptyy = t + b + ob_rr_theme->paddingy * 2;
185 if (self->h)
186 texth = self->h - emptyy;
187 h = texth * self->iconhm + emptyy;
188
189 if (self->textw)
190 textw = self->textw;
191
192 iconx = textx = l + ob_rr_theme->paddingx;
193
194 emptyx = l + r + ob_rr_theme->paddingx * 2;
195 if (self->hasicon) {
196 iconw = texth * self->iconwm;
197 iconh = texth * self->iconhm;
198 textx += iconw + ob_rr_theme->paddingx;
199 if (textw)
200 emptyx += ob_rr_theme->paddingx; /* between the icon and text */
201 } else
202 iconw = 0;
203
204 texty = (h - texth - emptyy) / 2 + t + ob_rr_theme->paddingy;
205 icony = (h - iconh - emptyy) / 2 + t + ob_rr_theme->paddingy;
206
207 /* when there is no icon, then fill the whole dialog with the text
208 appearance
209 */
210 if (!self->hasicon)
211 {
212 textx = texty = 0;
213 texth += emptyy;
214 textw += emptyx;
215 emptyx = emptyy = 0;
216 }
217
218 w = textw + emptyx + iconw;
219 /* cap it at maxw/minw */
220 if (self->maxw) w = MIN(w, self->maxw);
221 if (self->minw) w = MAX(w, self->minw);
222 textw = w - emptyx - iconw;
223
224 /* sanity checks to avoid crashes! */
225 if (w < 1) w = 1;
226 if (h < 1) h = 1;
227 if (texth < 1) texth = 1;
228
229 /* set up the x coord */
230 x = self->x;
231 switch (self->gravity) {
232 case NorthGravity: case CenterGravity: case SouthGravity:
233 x -= w / 2;
234 break;
235 case NorthEastGravity: case EastGravity: case SouthEastGravity:
236 x -= w;
237 break;
238 }
239
240 /* set up the y coord */
241 y = self->y;
242 switch (self->gravity) {
243 case WestGravity: case CenterGravity: case EastGravity:
244 y -= h / 2;
245 break;
246 case SouthWestGravity: case SouthGravity: case SouthEastGravity:
247 y -= h;
248 break;
249 }
250
251 /* Find the monitor which contains the biggest part of the popup.
252 * If the popup is completely off screen, limit it to the intersection
253 * of all monitors and then try again. If it's still off screen, put it
254 * on monitor 0. */
255 RECT_SET(mon, x, y, w, h);
256 m = screen_find_monitor(&mon);
257 area = screen_physical_area_monitor(m);
258
259 x=MAX(MIN(x, area->x+area->width-w),area->x);
260 y=MAX(MIN(y, area->y+area->height-h),area->y);
261
262 g_free(area);
263
264 if (m == screen_num_monitors) {
265 RECT_SET(mon, x, y, w, h);
266 m = screen_find_monitor(&mon);
267 if (m == screen_num_monitors)
268 m = 0;
269 area = screen_physical_area_monitor(m);
270
271 x=MAX(MIN(x, area->x+area->width-w),area->x);
272 y=MAX(MIN(y, area->y+area->height-h),area->y);
273
274 g_free(area);
275 }
276
277 /* set the windows/appearances up */
278 XMoveResizeWindow(ob_display, self->bg, x, y, w, h);
279 /* when there is no icon and the text is not parent relative, then
280 fill the whole dialog with the text appearance, don't use the bg at all
281 */
282 if (self->hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
283 RrPaint(self->a_bg, self->bg, w, h);
284
285 if (textw) {
286 self->a_text->surface.parent = self->a_bg;
287 self->a_text->surface.parentx = textx;
288 self->a_text->surface.parenty = texty;
289 XMoveResizeWindow(ob_display, self->text, textx, texty, textw, texth);
290 RrPaint(self->a_text, self->text, textw, texth);
291 }
292
293 if (self->hasicon)
294 self->draw_icon(iconx, icony, iconw, iconh, self->draw_icon_data);
295
296 /* do the actual showing */
297 if (!self->mapped) {
298 if (usec) {
299 /* don't kill previous show timers */
300 if (!self->delay_mapped) {
301 ob_main_loop_timeout_add(ob_main_loop, usec,
302 popup_show_timeout, self,
303 g_direct_equal, NULL);
304 self->delay_mapped = TRUE;
305 }
306 } else {
307 popup_show_timeout(self);
308 }
309 }
310 }
311
312 void popup_hide(ObPopup *self)
313 {
314 if (self->mapped) {
315 gulong ignore_start;
316
317 /* kill enter events cause by this unmapping */
318 ignore_start = event_start_ignore_all_enters();
319
320 XUnmapWindow(ob_display, self->bg);
321 self->mapped = FALSE;
322
323 event_end_ignore_all_enters(ignore_start);
324 } else if (self->delay_mapped) {
325 ob_main_loop_timeout_remove_data(ob_main_loop, popup_show_timeout, self, FALSE);
326 self->delay_mapped = FALSE;
327 }
328 }
329
330 static void icon_popup_draw_icon(gint x, gint y, gint w, gint h, gpointer data)
331 {
332 ObIconPopup *self = data;
333
334 self->a_icon->surface.parent = self->popup->a_bg;
335 self->a_icon->surface.parentx = x;
336 self->a_icon->surface.parenty = y;
337 XMoveResizeWindow(ob_display, self->icon, x, y, w, h);
338 RrPaint(self->a_icon, self->icon, w, h);
339 }
340
341 ObIconPopup *icon_popup_new(void)
342 {
343 ObIconPopup *self;
344
345 self = g_new0(ObIconPopup, 1);
346 self->popup = popup_new();
347 self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
348 self->icon = XCreateWindow(ob_display, self->popup->bg,
349 0, 0, 1, 1, 0,
350 RrDepth(ob_rr_inst), InputOutput,
351 RrVisual(ob_rr_inst), 0, NULL);
352 XMapWindow(ob_display, self->icon);
353
354 self->popup->hasicon = TRUE;
355 self->popup->draw_icon = icon_popup_draw_icon;
356 self->popup->draw_icon_data = self;
357
358 return self;
359 }
360
361 void icon_popup_free(ObIconPopup *self)
362 {
363 if (self) {
364 XDestroyWindow(ob_display, self->icon);
365 RrAppearanceFree(self->a_icon);
366 popup_free(self->popup);
367 g_free(self);
368 }
369 }
370
371 void icon_popup_delay_show(ObIconPopup *self, gulong usec,
372 gchar *text, RrImage *icon)
373 {
374 if (icon) {
375 RrAppearanceClearTextures(self->a_icon);
376 self->a_icon->texture[0].type = RR_TEXTURE_IMAGE;
377 self->a_icon->texture[0].data.image.alpha = 0xff;
378 self->a_icon->texture[0].data.image.image = icon;
379 } else {
380 RrAppearanceClearTextures(self->a_icon);
381 self->a_icon->texture[0].type = RR_TEXTURE_NONE;
382 }
383
384 popup_delay_show(self->popup, usec, text);
385 }
386
387 void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm)
388 {
389 /* cap them at 1 */
390 self->popup->iconwm = MAX(1, wm);
391 self->popup->iconhm = MAX(1, hm);
392 }
393
394 static void pager_popup_draw_icon(gint px, gint py, gint w, gint h,
395 gpointer data)
396 {
397 ObPagerPopup *self = data;
398 gint x, y;
399 guint rown, n;
400 guint horz_inc;
401 guint vert_inc;
402 guint r, c;
403 gint eachw, eachh;
404 const guint cols = screen_desktop_layout.columns;
405 const guint rows = screen_desktop_layout.rows;
406 const gint linewidth = ob_rr_theme->obwidth;
407
408 eachw = (w - ((cols + 1) * linewidth)) / cols;
409 eachh = (h - ((rows + 1) * linewidth)) / rows;
410 /* make them squares */
411 eachw = eachh = MIN(eachw, eachh);
412
413 /* center */
414 px += (w - (cols * (eachw + linewidth) + linewidth)) / 2;
415 py += (h - (rows * (eachh + linewidth) + linewidth)) / 2;
416
417 if (eachw <= 0 || eachh <= 0)
418 return;
419
420 switch (screen_desktop_layout.orientation) {
421 case OB_ORIENTATION_HORZ:
422 switch (screen_desktop_layout.start_corner) {
423 case OB_CORNER_TOPLEFT:
424 n = 0;
425 horz_inc = 1;
426 vert_inc = cols;
427 break;
428 case OB_CORNER_TOPRIGHT:
429 n = cols - 1;
430 horz_inc = -1;
431 vert_inc = cols;
432 break;
433 case OB_CORNER_BOTTOMRIGHT:
434 n = rows * cols - 1;
435 horz_inc = -1;
436 vert_inc = -screen_desktop_layout.columns;
437 break;
438 case OB_CORNER_BOTTOMLEFT:
439 n = (rows - 1) * cols;
440 horz_inc = 1;
441 vert_inc = -cols;
442 break;
443 default:
444 g_assert_not_reached();
445 }
446 break;
447 case OB_ORIENTATION_VERT:
448 switch (screen_desktop_layout.start_corner) {
449 case OB_CORNER_TOPLEFT:
450 n = 0;
451 horz_inc = rows;
452 vert_inc = 1;
453 break;
454 case OB_CORNER_TOPRIGHT:
455 n = rows * (cols - 1);
456 horz_inc = -rows;
457 vert_inc = 1;
458 break;
459 case OB_CORNER_BOTTOMRIGHT:
460 n = rows * cols - 1;
461 horz_inc = -rows;
462 vert_inc = -1;
463 break;
464 case OB_CORNER_BOTTOMLEFT:
465 n = rows - 1;
466 horz_inc = rows;
467 vert_inc = -1;
468 break;
469 default:
470 g_assert_not_reached();
471 }
472 break;
473 default:
474 g_assert_not_reached();
475 }
476
477 rown = n;
478 for (r = 0, y = 0; r < rows; ++r, y += eachh + linewidth)
479 {
480 for (c = 0, x = 0; c < cols; ++c, x += eachw + linewidth)
481 {
482 RrAppearance *a;
483
484 if (n < self->desks) {
485 a = (n == self->curdesk ? self->hilight : self->unhilight);
486
487 a->surface.parent = self->popup->a_bg;
488 a->surface.parentx = x + px;
489 a->surface.parenty = y + py;
490 XMoveResizeWindow(ob_display, self->wins[n],
491 x + px, y + py, eachw, eachh);
492 RrPaint(a, self->wins[n], eachw, eachh);
493 }
494 n += horz_inc;
495 }
496 n = rown += vert_inc;
497 }
498 }
499
500 ObPagerPopup *pager_popup_new(void)
501 {
502 ObPagerPopup *self;
503
504 self = g_new(ObPagerPopup, 1);
505 self->popup = popup_new();
506
507 self->desks = 0;
508 self->wins = g_new(Window, self->desks);
509 self->hilight = RrAppearanceCopy(ob_rr_theme->osd_hilite_fg);
510 self->unhilight = RrAppearanceCopy(ob_rr_theme->osd_unhilite_fg);
511
512 self->popup->hasicon = TRUE;
513 self->popup->draw_icon = pager_popup_draw_icon;
514 self->popup->draw_icon_data = self;
515
516 return self;
517 }
518
519 void pager_popup_free(ObPagerPopup *self)
520 {
521 if (self) {
522 guint i;
523
524 for (i = 0; i < self->desks; ++i)
525 XDestroyWindow(ob_display, self->wins[i]);
526 g_free(self->wins);
527 RrAppearanceFree(self->hilight);
528 RrAppearanceFree(self->unhilight);
529 popup_free(self->popup);
530 g_free(self);
531 }
532 }
533
534 void pager_popup_delay_show(ObPagerPopup *self, gulong usec,
535 gchar *text, guint desk)
536 {
537 guint i;
538
539 if (screen_num_desktops < self->desks)
540 for (i = screen_num_desktops; i < self->desks; ++i)
541 XDestroyWindow(ob_display, self->wins[i]);
542
543 if (screen_num_desktops != self->desks)
544 self->wins = g_renew(Window, self->wins, screen_num_desktops);
545
546 if (screen_num_desktops > self->desks)
547 for (i = self->desks; i < screen_num_desktops; ++i) {
548 XSetWindowAttributes attr;
549
550 attr.border_pixel =
551 RrColorPixel(ob_rr_theme->osd_border_color);
552 self->wins[i] = XCreateWindow(ob_display, self->popup->bg,
553 0, 0, 1, 1, ob_rr_theme->obwidth,
554 RrDepth(ob_rr_inst), InputOutput,
555 RrVisual(ob_rr_inst), CWBorderPixel,
556 &attr);
557 XMapWindow(ob_display, self->wins[i]);
558 }
559
560 self->desks = screen_num_desktops;
561 self->curdesk = desk;
562
563 popup_delay_show(self->popup, usec, text);
564 }
565
566 void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm)
567 {
568 /* cap them at 1 */
569 self->popup->iconwm = MAX(1, wm);
570 self->popup->iconhm = MAX(1, hm);
571 }
This page took 0.060261 seconds and 4 git commands to generate.