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.
28 #include "obrender/render.h"
29 #include "obrender/theme.h"
31 ObPopup *popup_new(void)
33 XSetWindowAttributes attrib;
35 const Rect r = {0, 0, 1, 1};
38 self = window_new(OB_WINDOW_CLASS_INTERNAL, ObPopup);
39 self->gravity = NorthWestGravity;
40 self->x = self->y = self->textw = self->h = 0;
41 self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg);
42 self->a_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
43 self->iconwm = self->iconhm = 1;
45 attrib.override_redirect = True;
46 self->depth = RrDepth(ob_rr_inst);
47 self->bg = XCreateWindow(obt_display, obt_root(ob_screen),
48 r.x, r.y, r.width, r.height, b, self->depth,
49 InputOutput, RrVisual(ob_rr_inst),
50 CWOverrideRedirect, &attrib);
51 self->layer = OB_STACKING_LAYER_INTERNAL;
53 self->text = XCreateWindow(obt_display, self->bg,
54 0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
55 InputOutput, RrVisual(ob_rr_inst), 0, NULL);
57 XSetWindowBorderWidth(obt_display, self->bg, ob_rr_theme->obwidth);
58 XSetWindowBorder(obt_display, self->bg,
59 RrColorPixel(ob_rr_theme->osd_border_color));
61 XMapWindow(obt_display, self->text);
63 window_set_top_area(INTERNAL_AS_WINDOW(self), &r, b);
64 window_set_abstract(INTERNAL_AS_WINDOW(self),
65 &self->bg, /* top level window */
66 &self->bg, /* composite redir window */
67 &self->layer, /* stacking layer */
68 &self->depth, /* window depth */
71 stacking_add(INTERNAL_AS_WINDOW(self));
72 window_add(&self->bg, INTERNAL_AS_WINDOW(self));
76 void popup_free(ObPopup *self)
79 popup_hide(self); /* make sure it's not showing or is being delayed and
81 window_cleanup(INTERNAL_AS_WINDOW(self));
82 XDestroyWindow(obt_display, self->bg);
83 XDestroyWindow(obt_display, self->text);
84 RrAppearanceFree(self->a_bg);
85 RrAppearanceFree(self->a_text);
86 window_remove(self->bg);
87 stacking_remove(INTERNAL_AS_WINDOW(self));
88 window_free(INTERNAL_AS_WINDOW(self));
92 void popup_position(ObPopup *self, gint gravity, gint x, gint y)
94 self->gravity = gravity;
99 void popup_text_width(ObPopup *self, gint w)
104 void popup_min_width(ObPopup *self, gint minw)
109 void popup_max_width(ObPopup *self, gint maxw)
114 void popup_height(ObPopup *self, gint h)
118 /* don't let the height be smaller than the text */
119 texth = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
120 self->h = MAX(h, texth);
123 void popup_text_width_to_string(ObPopup *self, gchar *text)
125 if (text[0] != '\0') {
126 self->a_text->texture[0].data.text.string = text;
127 self->textw = RrMinWidth(self->a_text);
132 void popup_height_to_string(ObPopup *self, gchar *text)
134 self->h = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
137 void popup_text_width_to_strings(ObPopup *self, gchar **strings, gint num)
142 for (i = 0; i < num; ++i) {
143 popup_text_width_to_string(self, strings[i]);
144 maxw = MAX(maxw, self->textw);
149 void popup_set_text_align(ObPopup *self, RrJustify align)
151 self->a_text->texture[0].data.text.justify = align;
154 static gboolean popup_show_timeout(gpointer data)
156 ObPopup *self = data;
158 XMapWindow(obt_display, self->bg);
159 stacking_raise(INTERNAL_AS_WINDOW(self));
161 self->delay_mapped = FALSE;
162 self->delay_timer = 0;
165 return FALSE; /* don't repeat */
168 void popup_delay_show(ObPopup *self, gulong msec, gchar *text)
173 gint emptyx, emptyy; /* empty space between elements */
174 gint textx, texty, textw, texth;
175 gint iconx, icony, iconw, iconh;
178 gboolean hasicon = self->hasicon;
180 /* when there is no icon and the text is not parent relative, then
181 fill the whole dialog with the text appearance, don't use the bg at all
183 if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
184 RrMargins(self->a_bg, &l, &t, &r, &b);
188 /* set up the textures */
189 self->a_text->texture[0].data.text.string = text;
191 /* measure the text out */
192 if (text[0] != '\0') {
193 RrMinSize(self->a_text, &textw, &texth);
196 texth = RrMinHeight(self->a_text);
199 /* get the height, which is also used for the icon width */
200 emptyy = t + b + ob_rr_theme->paddingy * 2;
202 texth = self->h - emptyy;
203 h = texth * self->iconhm + emptyy;
208 iconx = textx = l + ob_rr_theme->paddingx;
210 emptyx = l + r + ob_rr_theme->paddingx * 2;
212 iconw = texth * self->iconwm;
213 iconh = texth * self->iconhm;
214 textx += iconw + ob_rr_theme->paddingx;
216 emptyx += ob_rr_theme->paddingx; /* between the icon and text */
217 icony = (h - iconh - emptyy) / 2 + t + ob_rr_theme->paddingy;
221 texty = (h - texth - emptyy) / 2 + t + ob_rr_theme->paddingy;
223 /* when there is no icon, then fill the whole dialog with the text
234 w = textw + emptyx + iconw;
235 /* cap it at maxw/minw */
236 if (self->maxw) w = MIN(w, self->maxw);
237 if (self->minw) w = MAX(w, self->minw);
238 textw = w - emptyx - iconw;
240 /* sanity checks to avoid crashes! */
243 if (texth < 1) texth = 1;
245 /* set up the x coord */
247 switch (self->gravity) {
248 case NorthGravity: case CenterGravity: case SouthGravity:
251 case NorthEastGravity: case EastGravity: case SouthEastGravity:
256 /* set up the y coord */
258 switch (self->gravity) {
259 case WestGravity: case CenterGravity: case EastGravity:
262 case SouthWestGravity: case SouthGravity: case SouthEastGravity:
267 /* Find the monitor which contains the biggest part of the popup.
268 * If the popup is completely off screen, limit it to the intersection
269 * of all monitors and then try again. If it's still off screen, put it
271 RECT_SET(mon, x, y, w, h);
272 m = screen_find_monitor(&mon);
273 area = screen_physical_area_monitor(m);
275 x=MAX(MIN(x, area->x+area->width-w),area->x);
276 y=MAX(MIN(y, area->y+area->height-h),area->y);
278 if (m == screen_num_monitors) {
279 RECT_SET(mon, x, y, w, h);
280 m = screen_find_monitor(&mon);
281 if (m == screen_num_monitors)
283 area = screen_physical_area_monitor(m);
285 x=MAX(MIN(x, area->x+area->width-w),area->x);
286 y=MAX(MIN(y, area->y+area->height-h),area->y);
289 /* set the windows/appearances up */
290 XMoveResizeWindow(obt_display, self->bg, x, y, w, h);
291 /* when there is no icon and the text is not parent relative, then
292 fill the whole dialog with the text appearance, don't use the bg at all
294 if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
295 RrPaint(self->a_bg, self->bg, w, h);
298 self->a_text->surface.parent = self->a_bg;
299 self->a_text->surface.parentx = textx;
300 self->a_text->surface.parenty = texty;
301 XMoveResizeWindow(obt_display, self->text, textx, texty, textw, texth);
302 RrPaint(self->a_text, self->text, textw, texth);
306 self->draw_icon(iconx, icony, iconw, iconh, self->draw_icon_data);
308 /* do the actual showing */
311 /* don't kill previous show timers */
312 if (!self->delay_mapped) {
314 g_timeout_add(msec, popup_show_timeout, self);
315 self->delay_mapped = TRUE;
318 popup_show_timeout(self);
323 void popup_hide(ObPopup *self)
328 /* kill enter events cause by this unmapping */
329 ignore_start = event_start_ignore_all_enters();
331 XUnmapWindow(obt_display, self->bg);
332 self->mapped = FALSE;
334 event_end_ignore_all_enters(ignore_start);
335 } else if (self->delay_mapped) {
336 g_source_remove(self->delay_timer);
337 self->delay_timer = 0;
338 self->delay_mapped = FALSE;
342 static void icon_popup_draw_icon(gint x, gint y, gint w, gint h, gpointer data)
344 ObIconPopup *self = data;
346 self->a_icon->surface.parent = self->popup->a_bg;
347 self->a_icon->surface.parentx = x;
348 self->a_icon->surface.parenty = y;
349 XMoveResizeWindow(obt_display, self->icon, x, y, w, h);
350 RrPaint(self->a_icon, self->icon, w, h);
353 ObIconPopup *icon_popup_new(void)
357 self = g_slice_new0(ObIconPopup);
358 self->popup = popup_new();
359 self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
360 self->icon = XCreateWindow(obt_display, self->popup->bg,
362 RrDepth(ob_rr_inst), InputOutput,
363 RrVisual(ob_rr_inst), 0, NULL);
364 XMapWindow(obt_display, self->icon);
366 self->popup->hasicon = TRUE;
367 self->popup->draw_icon = icon_popup_draw_icon;
368 self->popup->draw_icon_data = self;
373 void icon_popup_free(ObIconPopup *self)
376 XDestroyWindow(obt_display, self->icon);
377 RrAppearanceFree(self->a_icon);
378 popup_free(self->popup);
379 g_slice_free(ObIconPopup, self);
383 void icon_popup_delay_show(ObIconPopup *self, gulong msec,
384 gchar *text, RrImage *icon)
387 RrAppearanceClearTextures(self->a_icon);
388 self->a_icon->texture[0].type = RR_TEXTURE_IMAGE;
389 self->a_icon->texture[0].data.image.alpha = 0xff;
390 self->a_icon->texture[0].data.image.image = icon;
392 RrAppearanceClearTextures(self->a_icon);
393 self->a_icon->texture[0].type = RR_TEXTURE_NONE;
396 popup_delay_show(self->popup, msec, text);
399 void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm)
402 self->popup->iconwm = MAX(1, wm);
403 self->popup->iconhm = MAX(1, hm);
406 static void pager_popup_draw_icon(gint px, gint py, gint w, gint h,
409 ObPagerPopup *self = data;
416 const guint cols = screen_desktop_layout.columns;
417 const guint rows = screen_desktop_layout.rows;
418 const gint linewidth = ob_rr_theme->obwidth;
420 eachw = (w - ((cols + 1) * linewidth)) / cols;
421 eachh = (h - ((rows + 1) * linewidth)) / rows;
422 /* make them squares */
423 eachw = eachh = MIN(eachw, eachh);
426 px += (w - (cols * (eachw + linewidth) + linewidth)) / 2;
427 py += (h - (rows * (eachh + linewidth) + linewidth)) / 2;
429 if (eachw <= 0 || eachh <= 0)
432 switch (screen_desktop_layout.orientation) {
433 case OB_ORIENTATION_HORZ:
434 switch (screen_desktop_layout.start_corner) {
435 case OB_CORNER_TOPLEFT:
440 case OB_CORNER_TOPRIGHT:
445 case OB_CORNER_BOTTOMRIGHT:
448 vert_inc = -screen_desktop_layout.columns;
450 case OB_CORNER_BOTTOMLEFT:
451 n = (rows - 1) * cols;
456 g_assert_not_reached();
459 case OB_ORIENTATION_VERT:
460 switch (screen_desktop_layout.start_corner) {
461 case OB_CORNER_TOPLEFT:
466 case OB_CORNER_TOPRIGHT:
467 n = rows * (cols - 1);
471 case OB_CORNER_BOTTOMRIGHT:
476 case OB_CORNER_BOTTOMLEFT:
482 g_assert_not_reached();
486 g_assert_not_reached();
490 for (r = 0, y = 0; r < rows; ++r, y += eachh + linewidth)
492 for (c = 0, x = 0; c < cols; ++c, x += eachw + linewidth)
496 if (n < self->desks) {
497 a = (n == self->curdesk ? self->hilight : self->unhilight);
499 a->surface.parent = self->popup->a_bg;
500 a->surface.parentx = x + px;
501 a->surface.parenty = y + py;
502 XMoveResizeWindow(obt_display, self->wins[n],
503 x + px, y + py, eachw, eachh);
504 RrPaint(a, self->wins[n], eachw, eachh);
508 n = rown += vert_inc;
512 ObPagerPopup *pager_popup_new(void)
516 self = g_slice_new(ObPagerPopup);
517 self->popup = popup_new();
520 self->wins = g_new(Window, self->desks);
521 self->hilight = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
522 self->unhilight = RrAppearanceCopy(ob_rr_theme->osd_unhilite_bg);
524 self->popup->hasicon = TRUE;
525 self->popup->draw_icon = pager_popup_draw_icon;
526 self->popup->draw_icon_data = self;
531 void pager_popup_free(ObPagerPopup *self)
536 for (i = 0; i < self->desks; ++i)
537 XDestroyWindow(obt_display, self->wins[i]);
539 RrAppearanceFree(self->hilight);
540 RrAppearanceFree(self->unhilight);
541 popup_free(self->popup);
542 g_slice_free(ObPagerPopup, self);
546 void pager_popup_delay_show(ObPagerPopup *self, gulong msec,
547 gchar *text, guint desk)
551 if (screen_num_desktops < self->desks)
552 for (i = screen_num_desktops; i < self->desks; ++i)
553 XDestroyWindow(obt_display, self->wins[i]);
555 if (screen_num_desktops != self->desks)
556 self->wins = g_renew(Window, self->wins, screen_num_desktops);
558 if (screen_num_desktops > self->desks)
559 for (i = self->desks; i < screen_num_desktops; ++i) {
560 XSetWindowAttributes attr;
563 RrColorPixel(ob_rr_theme->osd_border_color);
564 self->wins[i] = XCreateWindow(obt_display, self->popup->bg,
565 0, 0, 1, 1, ob_rr_theme->obwidth,
566 RrDepth(ob_rr_inst), InputOutput,
567 RrVisual(ob_rr_inst), CWBorderPixel,
569 XMapWindow(obt_display, self->wins[i]);
572 self->desks = screen_num_desktops;
573 self->curdesk = desk;
575 popup_delay_show(self->popup, msec, text);
578 void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm)
581 self->popup->iconwm = MAX(1, wm);
582 self->popup->iconhm = MAX(1, hm);