Don't make desktop switching interactive when no mods used. (Fix bug #5203)
[openbox.git] / openbox / popup.c
blob2a83d7b8ed652832026526334cda9bc0455e36a4
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 popup.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
20 #include "popup.h"
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 "obrender/render.h"
29 #include "obrender/theme.h"
31 ObPopup *popup_new(void)
33 XSetWindowAttributes attrib;
34 ObPopup *self = g_slice_new0(ObPopup);
36 self->obwin.type = OB_WINDOW_CLASS_INTERNAL;
37 self->gravity = NorthWestGravity;
38 self->x = self->y = self->textw = self->h = 0;
39 self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg);
40 self->a_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
41 self->iconwm = self->iconhm = 1;
43 attrib.override_redirect = True;
44 self->bg = XCreateWindow(obt_display, obt_root(ob_screen),
45 0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
46 InputOutput, RrVisual(ob_rr_inst),
47 CWOverrideRedirect, &attrib);
49 self->text = XCreateWindow(obt_display, self->bg,
50 0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
51 InputOutput, RrVisual(ob_rr_inst), 0, NULL);
53 XSetWindowBorderWidth(obt_display, self->bg, ob_rr_theme->obwidth);
54 XSetWindowBorder(obt_display, self->bg,
55 RrColorPixel(ob_rr_theme->osd_border_color));
57 XMapWindow(obt_display, self->text);
59 stacking_add(INTERNAL_AS_WINDOW(self));
60 window_add(&self->bg, INTERNAL_AS_WINDOW(self));
61 return self;
64 void popup_free(ObPopup *self)
66 if (self) {
67 popup_hide(self); /* make sure it's not showing or is being delayed and
68 will be shown */
69 XDestroyWindow(obt_display, self->bg);
70 XDestroyWindow(obt_display, self->text);
71 RrAppearanceFree(self->a_bg);
72 RrAppearanceFree(self->a_text);
73 window_remove(self->bg);
74 stacking_remove(self);
75 g_slice_free(ObPopup, self);
79 void popup_position(ObPopup *self, gint gravity, gint x, gint y)
81 self->gravity = gravity;
82 self->x = x;
83 self->y = y;
86 void popup_text_width(ObPopup *self, gint w)
88 self->textw = w;
91 void popup_min_width(ObPopup *self, gint minw)
93 self->minw = minw;
96 void popup_max_width(ObPopup *self, gint maxw)
98 self->maxw = maxw;
101 void popup_height(ObPopup *self, gint h)
103 gint texth;
105 /* don't let the height be smaller than the text */
106 texth = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
107 self->h = MAX(h, texth);
110 void popup_text_width_to_string(ObPopup *self, gchar *text)
112 if (text[0] != '\0') {
113 self->a_text->texture[0].data.text.string = text;
114 self->textw = RrMinWidth(self->a_text);
115 } else
116 self->textw = 0;
119 void popup_height_to_string(ObPopup *self, gchar *text)
121 self->h = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
124 void popup_text_width_to_strings(ObPopup *self, gchar **strings, gint num)
126 gint i, maxw;
128 maxw = 0;
129 for (i = 0; i < num; ++i) {
130 popup_text_width_to_string(self, strings[i]);
131 maxw = MAX(maxw, self->textw);
133 self->textw = maxw;
136 void popup_set_text_align(ObPopup *self, RrJustify align)
138 self->a_text->texture[0].data.text.justify = align;
141 static gboolean popup_show_timeout(gpointer data)
143 ObPopup *self = data;
145 XMapWindow(obt_display, self->bg);
146 stacking_raise(INTERNAL_AS_WINDOW(self));
147 self->mapped = TRUE;
148 self->delay_mapped = FALSE;
149 self->delay_timer = 0;
151 return FALSE; /* don't repeat */
154 void popup_delay_show(ObPopup *self, gulong msec, gchar *text)
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 const Rect *area;
163 Rect mon;
164 gboolean hasicon = self->hasicon;
166 /* when there is no icon and the text is not parent relative, then
167 fill the whole dialog with the text appearance, don't use the bg at all
169 if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
170 RrMargins(self->a_bg, &l, &t, &r, &b);
171 else
172 l = t = r = b = 0;
174 /* set up the textures */
175 self->a_text->texture[0].data.text.string = text;
177 /* measure the text out */
178 if (text[0] != '\0') {
179 RrMinSize(self->a_text, &textw, &texth);
180 } else {
181 textw = 0;
182 texth = RrMinHeight(self->a_text);
185 /* get the height, which is also used for the icon width */
186 emptyy = t + b + ob_rr_theme->paddingy * 2;
187 if (self->h)
188 texth = self->h - emptyy;
189 h = texth * self->iconhm + emptyy;
191 if (self->textw)
192 textw = self->textw;
194 iconx = textx = l + ob_rr_theme->paddingx;
196 emptyx = l + r + ob_rr_theme->paddingx * 2;
197 if (hasicon) {
198 iconw = texth * self->iconwm;
199 iconh = texth * self->iconhm;
200 textx += iconw + ob_rr_theme->paddingx;
201 if (textw)
202 emptyx += ob_rr_theme->paddingx; /* between the icon and text */
203 icony = (h - iconh - emptyy) / 2 + t + ob_rr_theme->paddingy;
204 } else
205 iconw = 0;
207 texty = (h - texth - emptyy) / 2 + t + ob_rr_theme->paddingy;
209 /* when there is no icon, then fill the whole dialog with the text
210 appearance
212 if (!hasicon)
214 textx = texty = 0;
215 texth += emptyy;
216 textw += emptyx;
217 emptyx = emptyy = 0;
220 w = textw + emptyx + iconw;
221 /* cap it at maxw/minw */
222 if (self->maxw) w = MIN(w, self->maxw);
223 if (self->minw) w = MAX(w, self->minw);
224 textw = w - emptyx - iconw;
226 /* sanity checks to avoid crashes! */
227 if (w < 1) w = 1;
228 if (h < 1) h = 1;
229 if (texth < 1) texth = 1;
231 /* set up the x coord */
232 x = self->x;
233 switch (self->gravity) {
234 case NorthGravity: case CenterGravity: case SouthGravity:
235 x -= w / 2;
236 break;
237 case NorthEastGravity: case EastGravity: case SouthEastGravity:
238 x -= w;
239 break;
242 /* set up the y coord */
243 y = self->y;
244 switch (self->gravity) {
245 case WestGravity: case CenterGravity: case EastGravity:
246 y -= h / 2;
247 break;
248 case SouthWestGravity: case SouthGravity: case SouthEastGravity:
249 y -= h;
250 break;
253 /* Find the monitor which contains the biggest part of the popup.
254 * If the popup is completely off screen, limit it to the intersection
255 * of all monitors and then try again. If it's still off screen, put it
256 * on monitor 0. */
257 RECT_SET(mon, x, y, w, h);
258 m = screen_find_monitor(&mon);
259 area = screen_physical_area_monitor(m);
261 x=MAX(MIN(x, area->x+area->width-w),area->x);
262 y=MAX(MIN(y, area->y+area->height-h),area->y);
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);
271 x=MAX(MIN(x, area->x+area->width-w),area->x);
272 y=MAX(MIN(y, area->y+area->height-h),area->y);
275 /* set the windows/appearances up */
276 XMoveResizeWindow(obt_display, self->bg, x, y, w, h);
277 /* when there is no icon and the text is not parent relative, then
278 fill the whole dialog with the text appearance, don't use the bg at all
280 if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
281 RrPaint(self->a_bg, self->bg, w, h);
283 if (textw) {
284 self->a_text->surface.parent = self->a_bg;
285 self->a_text->surface.parentx = textx;
286 self->a_text->surface.parenty = texty;
287 XMoveResizeWindow(obt_display, self->text, textx, texty, textw, texth);
288 RrPaint(self->a_text, self->text, textw, texth);
291 if (hasicon)
292 self->draw_icon(iconx, icony, iconw, iconh, self->draw_icon_data);
294 /* do the actual showing */
295 if (!self->mapped) {
296 if (msec) {
297 /* don't kill previous show timers */
298 if (!self->delay_mapped) {
299 self->delay_timer =
300 g_timeout_add(msec, popup_show_timeout, self);
301 self->delay_mapped = TRUE;
303 } else {
304 popup_show_timeout(self);
309 void popup_hide(ObPopup *self)
311 if (self->mapped) {
312 gulong ignore_start;
314 /* kill enter events cause by this unmapping */
315 ignore_start = event_start_ignore_all_enters();
317 XUnmapWindow(obt_display, self->bg);
318 self->mapped = FALSE;
320 event_end_ignore_all_enters(ignore_start);
321 } else if (self->delay_mapped) {
322 g_source_remove(self->delay_timer);
323 self->delay_timer = 0;
324 self->delay_mapped = FALSE;
328 static void icon_popup_draw_icon(gint x, gint y, gint w, gint h, gpointer data)
330 ObIconPopup *self = data;
332 self->a_icon->surface.parent = self->popup->a_bg;
333 self->a_icon->surface.parentx = x;
334 self->a_icon->surface.parenty = y;
335 XMoveResizeWindow(obt_display, self->icon, x, y, w, h);
336 RrPaint(self->a_icon, self->icon, w, h);
339 ObIconPopup *icon_popup_new(void)
341 ObIconPopup *self;
343 self = g_slice_new0(ObIconPopup);
344 self->popup = popup_new();
345 self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
346 self->icon = XCreateWindow(obt_display, self->popup->bg,
347 0, 0, 1, 1, 0,
348 RrDepth(ob_rr_inst), InputOutput,
349 RrVisual(ob_rr_inst), 0, NULL);
350 XMapWindow(obt_display, self->icon);
352 self->popup->hasicon = TRUE;
353 self->popup->draw_icon = icon_popup_draw_icon;
354 self->popup->draw_icon_data = self;
356 return self;
359 void icon_popup_free(ObIconPopup *self)
361 if (self) {
362 XDestroyWindow(obt_display, self->icon);
363 RrAppearanceFree(self->a_icon);
364 popup_free(self->popup);
365 g_slice_free(ObIconPopup, self);
369 void icon_popup_delay_show(ObIconPopup *self, gulong msec,
370 gchar *text, RrImage *icon)
372 if (icon) {
373 RrAppearanceClearTextures(self->a_icon);
374 self->a_icon->texture[0].type = RR_TEXTURE_IMAGE;
375 self->a_icon->texture[0].data.image.alpha = 0xff;
376 self->a_icon->texture[0].data.image.image = icon;
377 } else {
378 RrAppearanceClearTextures(self->a_icon);
379 self->a_icon->texture[0].type = RR_TEXTURE_NONE;
382 popup_delay_show(self->popup, msec, text);
385 void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm)
387 /* cap them at 1 */
388 self->popup->iconwm = MAX(1, wm);
389 self->popup->iconhm = MAX(1, hm);
392 static void pager_popup_draw_icon(gint px, gint py, gint w, gint h,
393 gpointer data)
395 ObPagerPopup *self = data;
396 gint x, y;
397 guint rown, n;
398 guint horz_inc;
399 guint vert_inc;
400 guint r, c;
401 gint eachw, eachh;
402 const guint cols = screen_desktop_layout.columns;
403 const guint rows = screen_desktop_layout.rows;
404 const gint linewidth = ob_rr_theme->obwidth;
406 eachw = (w - ((cols + 1) * linewidth)) / cols;
407 eachh = (h - ((rows + 1) * linewidth)) / rows;
408 /* make them squares */
409 eachw = eachh = MIN(eachw, eachh);
411 /* center */
412 px += (w - (cols * (eachw + linewidth) + linewidth)) / 2;
413 py += (h - (rows * (eachh + linewidth) + linewidth)) / 2;
415 if (eachw <= 0 || eachh <= 0)
416 return;
418 switch (screen_desktop_layout.orientation) {
419 case OB_ORIENTATION_HORZ:
420 switch (screen_desktop_layout.start_corner) {
421 case OB_CORNER_TOPLEFT:
422 n = 0;
423 horz_inc = 1;
424 vert_inc = cols;
425 break;
426 case OB_CORNER_TOPRIGHT:
427 n = cols - 1;
428 horz_inc = -1;
429 vert_inc = cols;
430 break;
431 case OB_CORNER_BOTTOMRIGHT:
432 n = rows * cols - 1;
433 horz_inc = -1;
434 vert_inc = -screen_desktop_layout.columns;
435 break;
436 case OB_CORNER_BOTTOMLEFT:
437 n = (rows - 1) * cols;
438 horz_inc = 1;
439 vert_inc = -cols;
440 break;
441 default:
442 g_assert_not_reached();
444 break;
445 case OB_ORIENTATION_VERT:
446 switch (screen_desktop_layout.start_corner) {
447 case OB_CORNER_TOPLEFT:
448 n = 0;
449 horz_inc = rows;
450 vert_inc = 1;
451 break;
452 case OB_CORNER_TOPRIGHT:
453 n = rows * (cols - 1);
454 horz_inc = -rows;
455 vert_inc = 1;
456 break;
457 case OB_CORNER_BOTTOMRIGHT:
458 n = rows * cols - 1;
459 horz_inc = -rows;
460 vert_inc = -1;
461 break;
462 case OB_CORNER_BOTTOMLEFT:
463 n = rows - 1;
464 horz_inc = rows;
465 vert_inc = -1;
466 break;
467 default:
468 g_assert_not_reached();
470 break;
471 default:
472 g_assert_not_reached();
475 rown = n;
476 for (r = 0, y = 0; r < rows; ++r, y += eachh + linewidth)
478 for (c = 0, x = 0; c < cols; ++c, x += eachw + linewidth)
480 RrAppearance *a;
482 if (n < self->desks) {
483 a = (n == self->curdesk ? self->hilight : self->unhilight);
485 a->surface.parent = self->popup->a_bg;
486 a->surface.parentx = x + px;
487 a->surface.parenty = y + py;
488 XMoveResizeWindow(obt_display, self->wins[n],
489 x + px, y + py, eachw, eachh);
490 RrPaint(a, self->wins[n], eachw, eachh);
492 n += horz_inc;
494 n = rown += vert_inc;
498 ObPagerPopup *pager_popup_new(void)
500 ObPagerPopup *self;
502 self = g_slice_new(ObPagerPopup);
503 self->popup = popup_new();
505 self->desks = 0;
506 self->wins = g_new(Window, self->desks);
507 self->hilight = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
508 self->unhilight = RrAppearanceCopy(ob_rr_theme->osd_unhilite_bg);
510 self->popup->hasicon = TRUE;
511 self->popup->draw_icon = pager_popup_draw_icon;
512 self->popup->draw_icon_data = self;
514 return self;
517 void pager_popup_free(ObPagerPopup *self)
519 if (self) {
520 guint i;
522 for (i = 0; i < self->desks; ++i)
523 XDestroyWindow(obt_display, self->wins[i]);
524 g_free(self->wins);
525 RrAppearanceFree(self->hilight);
526 RrAppearanceFree(self->unhilight);
527 popup_free(self->popup);
528 g_slice_free(ObPagerPopup, self);
532 void pager_popup_delay_show(ObPagerPopup *self, gulong msec,
533 gchar *text, guint desk)
535 guint i;
537 if (screen_num_desktops < self->desks)
538 for (i = screen_num_desktops; i < self->desks; ++i)
539 XDestroyWindow(obt_display, self->wins[i]);
541 if (screen_num_desktops != self->desks)
542 self->wins = g_renew(Window, self->wins, screen_num_desktops);
544 if (screen_num_desktops > self->desks)
545 for (i = self->desks; i < screen_num_desktops; ++i) {
546 XSetWindowAttributes attr;
548 attr.border_pixel =
549 RrColorPixel(ob_rr_theme->osd_border_color);
550 self->wins[i] = XCreateWindow(obt_display, self->popup->bg,
551 0, 0, 1, 1, ob_rr_theme->obwidth,
552 RrDepth(ob_rr_inst), InputOutput,
553 RrVisual(ob_rr_inst), CWBorderPixel,
554 &attr);
555 XMapWindow(obt_display, self->wins[i]);
558 self->desks = screen_num_desktops;
559 self->curdesk = desk;
561 popup_delay_show(self->popup, msec, text);
564 void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm)
566 /* cap them at 1 */
567 self->popup->iconwm = MAX(1, wm);
568 self->popup->iconhm = MAX(1, hm);