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 "render/render.h"
29 #include "render/theme.h"
31 ObPopup
*popup_new(void)
33 XSetWindowAttributes attrib
;
34 ObPopup
*self
= g_new0(ObPopup
, 1);
36 self
->obwin
.type
= Window_Internal
;
37 self
->gravity
= NorthWestGravity
;
38 self
->x
= self
->y
= self
->textw
= self
->h
= 0;
39 self
->a_bg
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_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
, RootWindow(obt_display
, 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 g_hash_table_insert(window_map
, &self
->bg
, self
);
64 void popup_free(ObPopup
*self
)
67 XDestroyWindow(obt_display
, self
->bg
);
68 XDestroyWindow(obt_display
, self
->text
);
69 RrAppearanceFree(self
->a_bg
);
70 RrAppearanceFree(self
->a_text
);
71 g_hash_table_remove(window_map
, &self
->bg
);
72 stacking_remove(self
);
77 void popup_position(ObPopup
*self
, gint gravity
, gint x
, gint y
)
79 self
->gravity
= gravity
;
84 void popup_text_width(ObPopup
*self
, gint w
)
89 void popup_min_width(ObPopup
*self
, gint minw
)
94 void popup_max_width(ObPopup
*self
, gint maxw
)
99 void popup_height(ObPopup
*self
, gint h
)
103 /* don't let the height be smaller than the text */
104 texth
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
105 self
->h
= MAX(h
, texth
);
108 void popup_text_width_to_string(ObPopup
*self
, gchar
*text
)
110 if (text
[0] != '\0') {
111 self
->a_text
->texture
[0].data
.text
.string
= text
;
112 self
->textw
= RrMinWidth(self
->a_text
);
117 void popup_height_to_string(ObPopup
*self
, gchar
*text
)
119 self
->h
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
122 void popup_text_width_to_strings(ObPopup
*self
, gchar
**strings
, gint num
)
127 for (i
= 0; i
< num
; ++i
) {
128 popup_text_width_to_string(self
, strings
[i
]);
129 maxw
= MAX(maxw
, self
->textw
);
134 void popup_set_text_align(ObPopup
*self
, RrJustify align
)
136 self
->a_text
->texture
[0].data
.text
.justify
= align
;
139 static gboolean
popup_show_timeout(gpointer data
)
141 ObPopup
*self
= data
;
143 XMapWindow(obt_display
, self
->bg
);
144 stacking_raise(INTERNAL_AS_WINDOW(self
));
146 self
->delay_mapped
= FALSE
;
148 return FALSE
; /* don't repeat */
151 void popup_delay_show(ObPopup
*self
, gulong usec
, gchar
*text
)
156 gint emptyx
, emptyy
; /* empty space between elements */
157 gint textx
, texty
, textw
, texth
;
158 gint iconx
, icony
, iconw
, iconh
;
161 /* when there is no icon and the text is not parent relative, then
162 fill the whole dialog with the text appearance, don't use the bg at all
164 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
165 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
169 /* set up the textures */
170 self
->a_text
->texture
[0].data
.text
.string
= text
;
172 /* measure the text out */
173 if (text
[0] != '\0') {
174 RrMinSize(self
->a_text
, &textw
, &texth
);
177 texth
= RrMinHeight(self
->a_text
);
180 /* get the height, which is also used for the icon width */
181 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
183 texth
= self
->h
- emptyy
;
184 h
= texth
* self
->iconhm
+ emptyy
;
189 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
191 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
193 iconw
= texth
* self
->iconwm
;
194 iconh
= texth
* self
->iconhm
;
195 textx
+= iconw
+ ob_rr_theme
->paddingx
;
197 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
201 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
202 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
204 /* when there is no icon, then fill the whole dialog with the text
215 w
= textw
+ emptyx
+ iconw
;
216 /* cap it at maxw/minw */
217 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
218 if (self
->minw
) w
= MAX(w
, self
->minw
);
219 textw
= w
- emptyx
- iconw
;
221 /* sanity checks to avoid crashes! */
224 if (texth
< 1) texth
= 1;
226 /* set up the x coord */
228 switch (self
->gravity
) {
229 case NorthGravity
: case CenterGravity
: case SouthGravity
:
232 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
237 /* set up the y coord */
239 switch (self
->gravity
) {
240 case WestGravity
: case CenterGravity
: case EastGravity
:
243 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
248 /* Find the monitor which contains the biggest part of the popup.
249 * If the popup is completely off screen, limit it to the intersection
250 * of all monitors and then try again. If it's still off screen, put it
252 RECT_SET(mon
, x
, y
, w
, h
);
253 m
= screen_find_monitor(&mon
);
254 area
= screen_physical_area_monitor(m
);
256 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
257 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
259 if (m
== screen_num_monitors
) {
260 RECT_SET(mon
, x
, y
, w
, h
);
261 m
= screen_find_monitor(&mon
);
262 if (m
== screen_num_monitors
)
264 area
= screen_physical_area_monitor(m
);
266 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
267 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
270 /* set the windows/appearances up */
271 XMoveResizeWindow(obt_display
, self
->bg
, x
, y
, w
, h
);
272 /* when there is no icon and the text is not parent relative, then
273 fill the whole dialog with the text appearance, don't use the bg at all
275 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
276 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
279 self
->a_text
->surface
.parent
= self
->a_bg
;
280 self
->a_text
->surface
.parentx
= textx
;
281 self
->a_text
->surface
.parenty
= texty
;
282 XMoveResizeWindow(obt_display
, self
->text
, textx
, texty
, textw
, texth
);
283 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
287 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
289 /* do the actual showing */
292 /* don't kill previous show timers */
293 if (!self
->delay_mapped
) {
294 obt_main_loop_timeout_add(ob_main_loop
, usec
,
295 popup_show_timeout
, self
,
296 g_direct_equal
, NULL
);
297 self
->delay_mapped
= TRUE
;
300 popup_show_timeout(self
);
307 void popup_hide(ObPopup
*self
)
312 /* kill enter events cause by this unmapping */
313 ignore_start
= event_start_ignore_all_enters();
315 XUnmapWindow(obt_display
, self
->bg
);
316 self
->mapped
= FALSE
;
318 event_end_ignore_all_enters(ignore_start
);
319 } else if (self
->delay_mapped
) {
320 obt_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
321 self
->delay_mapped
= FALSE
;
325 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
327 ObIconPopup
*self
= data
;
329 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
330 self
->a_icon
->surface
.parentx
= x
;
331 self
->a_icon
->surface
.parenty
= y
;
332 XMoveResizeWindow(obt_display
, self
->icon
, x
, y
, w
, h
);
333 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
336 ObIconPopup
*icon_popup_new(void)
340 self
= g_new0(ObIconPopup
, 1);
341 self
->popup
= popup_new();
342 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
343 self
->icon
= XCreateWindow(obt_display
, self
->popup
->bg
,
345 RrDepth(ob_rr_inst
), InputOutput
,
346 RrVisual(ob_rr_inst
), 0, NULL
);
347 XMapWindow(obt_display
, self
->icon
);
349 self
->popup
->hasicon
= TRUE
;
350 self
->popup
->draw_icon
= icon_popup_draw_icon
;
351 self
->popup
->draw_icon_data
= self
;
356 void icon_popup_free(ObIconPopup
*self
)
359 XDestroyWindow(obt_display
, self
->icon
);
360 RrAppearanceFree(self
->a_icon
);
361 popup_free(self
->popup
);
366 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
367 gchar
*text
, const ObClientIcon
*icon
)
370 self
->a_icon
->texture
[0].type
= RR_TEXTURE_RGBA
;
371 self
->a_icon
->texture
[0].data
.rgba
.width
= icon
->width
;
372 self
->a_icon
->texture
[0].data
.rgba
.height
= icon
->height
;
373 self
->a_icon
->texture
[0].data
.rgba
.alpha
= 0xff;
374 self
->a_icon
->texture
[0].data
.rgba
.data
= icon
->data
;
376 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
378 popup_delay_show(self
->popup
, usec
, text
);
381 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
384 self
->popup
->iconwm
= MAX(1, wm
);
385 self
->popup
->iconhm
= MAX(1, hm
);
388 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
391 ObPagerPopup
*self
= data
;
398 const guint cols
= screen_desktop_layout
.columns
;
399 const guint rows
= screen_desktop_layout
.rows
;
400 const gint linewidth
= ob_rr_theme
->obwidth
;
402 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
403 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
404 /* make them squares */
405 eachw
= eachh
= MIN(eachw
, eachh
);
408 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
409 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
411 if (eachw
<= 0 || eachh
<= 0)
414 switch (screen_desktop_layout
.orientation
) {
415 case OB_ORIENTATION_HORZ
:
416 switch (screen_desktop_layout
.start_corner
) {
417 case OB_CORNER_TOPLEFT
:
422 case OB_CORNER_TOPRIGHT
:
427 case OB_CORNER_BOTTOMRIGHT
:
430 vert_inc
= -screen_desktop_layout
.columns
;
432 case OB_CORNER_BOTTOMLEFT
:
433 n
= (rows
- 1) * cols
;
438 g_assert_not_reached();
441 case OB_ORIENTATION_VERT
:
442 switch (screen_desktop_layout
.start_corner
) {
443 case OB_CORNER_TOPLEFT
:
448 case OB_CORNER_TOPRIGHT
:
449 n
= rows
* (cols
- 1);
453 case OB_CORNER_BOTTOMRIGHT
:
458 case OB_CORNER_BOTTOMLEFT
:
464 g_assert_not_reached();
468 g_assert_not_reached();
472 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
474 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
478 if (n
< self
->desks
) {
479 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
481 a
->surface
.parent
= self
->popup
->a_bg
;
482 a
->surface
.parentx
= x
+ px
;
483 a
->surface
.parenty
= y
+ py
;
484 XMoveResizeWindow(obt_display
, self
->wins
[n
],
485 x
+ px
, y
+ py
, eachw
, eachh
);
486 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
490 n
= rown
+= vert_inc
;
494 ObPagerPopup
*pager_popup_new(void)
498 self
= g_new(ObPagerPopup
, 1);
499 self
->popup
= popup_new();
502 self
->wins
= g_new(Window
, self
->desks
);
503 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
504 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
506 self
->popup
->hasicon
= TRUE
;
507 self
->popup
->draw_icon
= pager_popup_draw_icon
;
508 self
->popup
->draw_icon_data
= self
;
513 void pager_popup_free(ObPagerPopup
*self
)
518 for (i
= 0; i
< self
->desks
; ++i
)
519 XDestroyWindow(obt_display
, self
->wins
[i
]);
521 RrAppearanceFree(self
->hilight
);
522 RrAppearanceFree(self
->unhilight
);
523 popup_free(self
->popup
);
528 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
529 gchar
*text
, guint desk
)
533 if (screen_num_desktops
< self
->desks
)
534 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
535 XDestroyWindow(obt_display
, self
->wins
[i
]);
537 if (screen_num_desktops
!= self
->desks
)
538 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
540 if (screen_num_desktops
> self
->desks
)
541 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
542 XSetWindowAttributes attr
;
545 RrColorPixel(ob_rr_theme
->osd_border_color
);
546 self
->wins
[i
] = XCreateWindow(obt_display
, self
->popup
->bg
,
547 0, 0, 1, 1, ob_rr_theme
->obwidth
,
548 RrDepth(ob_rr_inst
), InputOutput
,
549 RrVisual(ob_rr_inst
), CWBorderPixel
,
551 XMapWindow(obt_display
, self
->wins
[i
]);
554 self
->desks
= screen_num_desktops
;
555 self
->curdesk
= desk
;
557 popup_delay_show(self
->popup
, usec
, text
);
560 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
563 self
->popup
->iconwm
= MAX(1, wm
);
564 self
->popup
->iconhm
= MAX(1, hm
);