Update danish translation
[openbox.git] / openbox / popup.c
blob063fb1f1b324ea7036ac614b8157451d3e374e95
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 if (texth > textw) textw = texth;
221 popup_set_text_align(self, RR_JUSTIFY_CENTER);
223 w = textw + emptyx + iconw;
224 /* cap it at maxw/minw */
225 if (self->maxw) w = MIN(w, self->maxw);
226 if (self->minw) w = MAX(w, self->minw);
227 textw = w - emptyx - iconw;
229 /* sanity checks to avoid crashes! */
230 if (w < 1) w = 1;
231 if (h < 1) h = 1;
232 if (texth < 1) texth = 1;
234 /* set up the x coord */
235 x = self->x;
236 switch (self->gravity) {
237 case NorthGravity: case CenterGravity: case SouthGravity:
238 x -= w / 2;
239 break;
240 case NorthEastGravity: case EastGravity: case SouthEastGravity:
241 x -= w;
242 break;
245 /* set up the y coord */
246 y = self->y;
247 switch (self->gravity) {
248 case WestGravity: case CenterGravity: case EastGravity:
249 y -= h / 2;
250 break;
251 case SouthWestGravity: case SouthGravity: case SouthEastGravity:
252 y -= h;
253 break;
256 /* If the popup belongs to a client (eg, the moveresize popup), get
257 * the monitor for that client, otherwise do other stuff */
258 if (self->client) {
259 m = client_monitor(self->client);
260 } else {
261 /* Find the monitor which contains the biggest part of the popup.
262 * If the popup is completely off screen, limit it to the intersection
263 * of all monitors and then try again. If it's still off screen, put it
264 * on monitor 0. */
265 RECT_SET(mon, x, y, w, h);
266 m = screen_find_monitor(&mon);
268 area = screen_physical_area_monitor(m);
270 x = MAX(MIN(x, area->x+area->width-w), area->x);
271 y = MAX(MIN(y, area->y+area->height-h), area->y);
273 if (m == screen_num_monitors) {
274 RECT_SET(mon, x, y, w, h);
275 m = screen_find_monitor(&mon);
276 if (m == screen_num_monitors)
277 m = 0;
278 area = screen_physical_area_monitor(m);
280 x = MAX(MIN(x, area->x+area->width-w), area->x);
281 y = MAX(MIN(y, area->y+area->height-h), area->y);
284 /* set the windows/appearances up */
285 XMoveResizeWindow(obt_display, self->bg, x, y, w, h);
286 /* when there is no icon and the text is not parent relative, then
287 fill the whole dialog with the text appearance, don't use the bg at all
289 if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
290 RrPaint(self->a_bg, self->bg, w, h);
292 if (textw) {
293 self->a_text->surface.parent = self->a_bg;
294 self->a_text->surface.parentx = textx;
295 self->a_text->surface.parenty = texty;
296 XMoveResizeWindow(obt_display, self->text, textx, texty, textw, texth);
297 RrPaint(self->a_text, self->text, textw, texth);
300 if (hasicon)
301 self->draw_icon(iconx, icony, iconw, iconh, self->draw_icon_data);
303 /* do the actual showing */
304 if (!self->mapped) {
305 if (msec) {
306 /* don't kill previous show timers */
307 if (!self->delay_mapped) {
308 self->delay_timer =
309 g_timeout_add(msec, popup_show_timeout, self);
310 self->delay_mapped = TRUE;
312 } else {
313 popup_show_timeout(self);
318 void popup_hide(ObPopup *self)
320 if (self->mapped) {
321 gulong ignore_start;
323 /* kill enter events cause by this unmapping */
324 ignore_start = event_start_ignore_all_enters();
326 XUnmapWindow(obt_display, self->bg);
327 self->mapped = FALSE;
329 event_end_ignore_all_enters(ignore_start);
330 } else if (self->delay_mapped) {
331 g_source_remove(self->delay_timer);
332 self->delay_timer = 0;
333 self->delay_mapped = FALSE;
337 static void icon_popup_draw_icon(gint x, gint y, gint w, gint h, gpointer data)
339 ObIconPopup *self = data;
341 self->a_icon->surface.parent = self->popup->a_bg;
342 self->a_icon->surface.parentx = x;
343 self->a_icon->surface.parenty = y;
344 XMoveResizeWindow(obt_display, self->icon, x, y, w, h);
345 RrPaint(self->a_icon, self->icon, w, h);
348 ObIconPopup *icon_popup_new(void)
350 ObIconPopup *self;
352 self = g_slice_new0(ObIconPopup);
353 self->popup = popup_new();
354 self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
355 self->icon = XCreateWindow(obt_display, self->popup->bg,
356 0, 0, 1, 1, 0,
357 RrDepth(ob_rr_inst), InputOutput,
358 RrVisual(ob_rr_inst), 0, NULL);
359 XMapWindow(obt_display, self->icon);
361 self->popup->hasicon = TRUE;
362 self->popup->draw_icon = icon_popup_draw_icon;
363 self->popup->draw_icon_data = self;
365 return self;
368 void icon_popup_free(ObIconPopup *self)
370 if (self) {
371 XDestroyWindow(obt_display, self->icon);
372 RrAppearanceFree(self->a_icon);
373 popup_free(self->popup);
374 g_slice_free(ObIconPopup, self);
378 void icon_popup_delay_show(ObIconPopup *self, gulong msec,
379 gchar *text, RrImage *icon)
381 if (icon) {
382 RrAppearanceClearTextures(self->a_icon);
383 self->a_icon->texture[0].type = RR_TEXTURE_IMAGE;
384 self->a_icon->texture[0].data.image.alpha = 0xff;
385 self->a_icon->texture[0].data.image.image = icon;
386 } else {
387 RrAppearanceClearTextures(self->a_icon);
388 self->a_icon->texture[0].type = RR_TEXTURE_NONE;
391 popup_delay_show(self->popup, msec, text);
394 void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm)
396 /* cap them at 1 */
397 self->popup->iconwm = MAX(1, wm);
398 self->popup->iconhm = MAX(1, hm);
401 static void pager_popup_draw_icon(gint px, gint py, gint w, gint h,
402 gpointer data)
404 ObPagerPopup *self = data;
405 gint x, y;
406 guint rown, n;
407 guint horz_inc;
408 guint vert_inc;
409 guint r, c;
410 gint eachw, eachh;
411 const guint cols = screen_desktop_layout.columns;
412 const guint rows = screen_desktop_layout.rows;
413 const gint linewidth = ob_rr_theme->obwidth;
415 eachw = (w - ((cols + 1) * linewidth)) / cols;
416 eachh = (h - ((rows + 1) * linewidth)) / rows;
417 /* make them squares */
418 eachw = eachh = MIN(eachw, eachh);
420 /* center */
421 px += (w - (cols * (eachw + linewidth) + linewidth)) / 2;
422 py += (h - (rows * (eachh + linewidth) + linewidth)) / 2;
424 if (eachw <= 0 || eachh <= 0)
425 return;
427 switch (screen_desktop_layout.orientation) {
428 case OB_ORIENTATION_HORZ:
429 switch (screen_desktop_layout.start_corner) {
430 case OB_CORNER_TOPLEFT:
431 n = 0;
432 horz_inc = 1;
433 vert_inc = cols;
434 break;
435 case OB_CORNER_TOPRIGHT:
436 n = cols - 1;
437 horz_inc = -1;
438 vert_inc = cols;
439 break;
440 case OB_CORNER_BOTTOMRIGHT:
441 n = rows * cols - 1;
442 horz_inc = -1;
443 vert_inc = -screen_desktop_layout.columns;
444 break;
445 case OB_CORNER_BOTTOMLEFT:
446 n = (rows - 1) * cols;
447 horz_inc = 1;
448 vert_inc = -cols;
449 break;
450 default:
451 g_assert_not_reached();
453 break;
454 case OB_ORIENTATION_VERT:
455 switch (screen_desktop_layout.start_corner) {
456 case OB_CORNER_TOPLEFT:
457 n = 0;
458 horz_inc = rows;
459 vert_inc = 1;
460 break;
461 case OB_CORNER_TOPRIGHT:
462 n = rows * (cols - 1);
463 horz_inc = -rows;
464 vert_inc = 1;
465 break;
466 case OB_CORNER_BOTTOMRIGHT:
467 n = rows * cols - 1;
468 horz_inc = -rows;
469 vert_inc = -1;
470 break;
471 case OB_CORNER_BOTTOMLEFT:
472 n = rows - 1;
473 horz_inc = rows;
474 vert_inc = -1;
475 break;
476 default:
477 g_assert_not_reached();
479 break;
480 default:
481 g_assert_not_reached();
484 rown = n;
485 for (r = 0, y = 0; r < rows; ++r, y += eachh + linewidth)
487 for (c = 0, x = 0; c < cols; ++c, x += eachw + linewidth)
489 RrAppearance *a;
491 if (n < self->desks) {
492 a = (n == self->curdesk ? self->hilight : self->unhilight);
494 a->surface.parent = self->popup->a_bg;
495 a->surface.parentx = x + px;
496 a->surface.parenty = y + py;
497 XMoveResizeWindow(obt_display, self->wins[n],
498 x + px, y + py, eachw, eachh);
499 RrPaint(a, self->wins[n], eachw, eachh);
501 n += horz_inc;
503 n = rown += vert_inc;
507 ObPagerPopup *pager_popup_new(void)
509 ObPagerPopup *self;
511 self = g_slice_new(ObPagerPopup);
512 self->popup = popup_new();
514 self->desks = 0;
515 self->wins = g_new(Window, self->desks);
516 self->hilight = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
517 self->unhilight = RrAppearanceCopy(ob_rr_theme->osd_unhilite_bg);
519 self->popup->hasicon = TRUE;
520 self->popup->draw_icon = pager_popup_draw_icon;
521 self->popup->draw_icon_data = self;
523 return self;
526 void pager_popup_free(ObPagerPopup *self)
528 if (self) {
529 guint i;
531 for (i = 0; i < self->desks; ++i)
532 XDestroyWindow(obt_display, self->wins[i]);
533 g_free(self->wins);
534 RrAppearanceFree(self->hilight);
535 RrAppearanceFree(self->unhilight);
536 popup_free(self->popup);
537 g_slice_free(ObPagerPopup, self);
541 void pager_popup_delay_show(ObPagerPopup *self, gulong msec,
542 gchar *text, guint desk)
544 guint i;
546 if (screen_num_desktops < self->desks)
547 for (i = screen_num_desktops; i < self->desks; ++i)
548 XDestroyWindow(obt_display, self->wins[i]);
550 if (screen_num_desktops != self->desks)
551 self->wins = g_renew(Window, self->wins, screen_num_desktops);
553 if (screen_num_desktops > self->desks)
554 for (i = self->desks; i < screen_num_desktops; ++i) {
555 XSetWindowAttributes attr;
557 attr.border_pixel =
558 RrColorPixel(ob_rr_theme->osd_border_color);
559 self->wins[i] = XCreateWindow(obt_display, self->popup->bg,
560 0, 0, 1, 1, ob_rr_theme->obwidth,
561 RrDepth(ob_rr_inst), InputOutput,
562 RrVisual(ob_rr_inst), CWBorderPixel,
563 &attr);
564 XMapWindow(obt_display, self->wins[i]);
567 self->desks = screen_num_desktops;
568 self->curdesk = desk;
570 popup_delay_show(self->popup, msec, text);
573 void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm)
575 /* cap them at 1 */
576 self->popup->iconwm = MAX(1, wm);
577 self->popup->iconhm = MAX(1, hm);