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.
29 #include "render/render.h"
30 #include "render/theme.h"
32 ObPopup
*popup_new(void)
34 XSetWindowAttributes attrib
;
35 ObPopup
*self
= g_new0(ObPopup
, 1);
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;
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
);
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
);
54 XSetWindowBorderWidth(ob_display
, self
->bg
, ob_rr_theme
->obwidth
);
55 XSetWindowBorder(ob_display
, self
->bg
,
56 RrColorPixel(ob_rr_theme
->osd_border_color
));
58 XMapWindow(ob_display
, self
->text
);
60 stacking_add(INTERNAL_AS_WINDOW(self
));
61 g_hash_table_insert(window_map
, &self
->bg
, self
);
65 void popup_free(ObPopup
*self
)
68 XDestroyWindow(ob_display
, self
->bg
);
69 XDestroyWindow(ob_display
, self
->text
);
70 RrAppearanceFree(self
->a_bg
);
71 RrAppearanceFree(self
->a_text
);
72 g_hash_table_remove(window_map
, &self
->bg
);
73 stacking_remove(self
);
78 void popup_position(ObPopup
*self
, gint gravity
, gint x
, gint y
)
80 self
->gravity
= gravity
;
85 void popup_text_width(ObPopup
*self
, gint w
)
90 void popup_min_width(ObPopup
*self
, gint minw
)
95 void popup_max_width(ObPopup
*self
, gint maxw
)
100 void popup_height(ObPopup
*self
, gint h
)
104 /* don't let the height be smaller than the text */
105 texth
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
106 self
->h
= MAX(h
, texth
);
109 void popup_text_width_to_string(ObPopup
*self
, gchar
*text
)
111 if (text
[0] != '\0') {
112 self
->a_text
->texture
[0].data
.text
.string
= text
;
113 self
->textw
= RrMinWidth(self
->a_text
);
118 void popup_height_to_string(ObPopup
*self
, gchar
*text
)
120 self
->h
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
123 void popup_text_width_to_strings(ObPopup
*self
, gchar
**strings
, gint num
)
128 for (i
= 0; i
< num
; ++i
) {
129 popup_text_width_to_string(self
, strings
[i
]);
130 maxw
= MAX(maxw
, self
->textw
);
135 void popup_set_text_align(ObPopup
*self
, RrJustify align
)
137 self
->a_text
->texture
[0].data
.text
.justify
= align
;
140 static gboolean
popup_show_timeout(gpointer data
)
142 ObPopup
*self
= data
;
144 XMapWindow(ob_display
, self
->bg
);
145 stacking_raise(INTERNAL_AS_WINDOW(self
));
147 self
->delay_mapped
= FALSE
;
149 return FALSE
; /* don't repeat */
152 void popup_delay_show(ObPopup
*self
, gulong usec
, gchar
*text
)
157 gint emptyx
, emptyy
; /* empty space between elements */
158 gint textx
, texty
, textw
, texth
;
159 gint iconx
, icony
, iconw
, iconh
;
162 /* when there is no icon and the text is not parent relative, then
163 fill the whole dialog with the text appearance, don't use the bg at all
165 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
166 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
170 /* set up the textures */
171 self
->a_text
->texture
[0].data
.text
.string
= text
;
173 /* measure the text out */
174 if (text
[0] != '\0') {
175 RrMinSize(self
->a_text
, &textw
, &texth
);
178 texth
= RrMinHeight(self
->a_text
);
181 /* get the height, which is also used for the icon width */
182 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
184 texth
= self
->h
- emptyy
;
185 h
= texth
* self
->iconhm
+ emptyy
;
190 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
192 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
194 iconw
= texth
* self
->iconwm
;
195 iconh
= texth
* self
->iconhm
;
196 textx
+= iconw
+ ob_rr_theme
->paddingx
;
198 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
202 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
203 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
205 /* when there is no icon, then fill the whole dialog with the text
216 w
= textw
+ emptyx
+ iconw
;
217 /* cap it at maxw/minw */
218 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
219 if (self
->minw
) w
= MAX(w
, self
->minw
);
220 textw
= w
- emptyx
- iconw
;
222 /* sanity checks to avoid crashes! */
225 if (texth
< 1) texth
= 1;
227 /* set up the x coord */
229 switch (self
->gravity
) {
230 case NorthGravity
: case CenterGravity
: case SouthGravity
:
233 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
238 /* set up the y coord */
240 switch (self
->gravity
) {
241 case WestGravity
: case CenterGravity
: case EastGravity
:
244 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
249 /* Find the monitor which contains the biggest part of the popup.
250 * If the popup is completely off screen, limit it to the intersection
251 * of all monitors and then try again. If it's still off screen, put it
253 RECT_SET(mon
, x
, y
, w
, h
);
254 m
= screen_find_monitor(&mon
);
255 area
= screen_physical_area_monitor(m
);
257 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
258 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
260 if (m
== screen_num_monitors
) {
261 RECT_SET(mon
, x
, y
, w
, h
);
262 m
= screen_find_monitor(&mon
);
263 if (m
== screen_num_monitors
)
265 area
= screen_physical_area_monitor(m
);
267 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
268 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
271 /* set the windows/appearances up */
272 XMoveResizeWindow(ob_display
, self
->bg
, x
, y
, w
, h
);
273 /* when there is no icon and the text is not parent relative, then
274 fill the whole dialog with the text appearance, don't use the bg at all
276 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
277 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
280 self
->a_text
->surface
.parent
= self
->a_bg
;
281 self
->a_text
->surface
.parentx
= textx
;
282 self
->a_text
->surface
.parenty
= texty
;
283 XMoveResizeWindow(ob_display
, self
->text
, textx
, texty
, textw
, texth
);
284 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
288 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
290 /* do the actual showing */
293 /* don't kill previous show timers */
294 if (!self
->delay_mapped
) {
295 ob_main_loop_timeout_add(ob_main_loop
, usec
,
296 popup_show_timeout
, self
,
297 g_direct_equal
, NULL
);
298 self
->delay_mapped
= TRUE
;
301 popup_show_timeout(self
);
308 void popup_hide(ObPopup
*self
)
313 /* kill enter events cause by this unmapping */
314 ignore_start
= event_start_ignore_all_enters();
316 XUnmapWindow(ob_display
, self
->bg
);
317 self
->mapped
= FALSE
;
319 event_end_ignore_all_enters(ignore_start
);
320 } else if (self
->delay_mapped
) {
321 ob_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
322 self
->delay_mapped
= FALSE
;
326 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
328 ObIconPopup
*self
= data
;
330 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
331 self
->a_icon
->surface
.parentx
= x
;
332 self
->a_icon
->surface
.parenty
= y
;
333 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
334 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
337 ObIconPopup
*icon_popup_new(void)
341 self
= g_new0(ObIconPopup
, 1);
342 self
->popup
= popup_new();
343 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
344 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
346 RrDepth(ob_rr_inst
), InputOutput
,
347 RrVisual(ob_rr_inst
), 0, NULL
);
348 XMapWindow(ob_display
, self
->icon
);
350 self
->popup
->hasicon
= TRUE
;
351 self
->popup
->draw_icon
= icon_popup_draw_icon
;
352 self
->popup
->draw_icon_data
= self
;
357 void icon_popup_free(ObIconPopup
*self
)
360 XDestroyWindow(ob_display
, self
->icon
);
361 RrAppearanceFree(self
->a_icon
);
362 popup_free(self
->popup
);
367 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
368 gchar
*text
, const ObClientIcon
*icon
)
371 self
->a_icon
->texture
[0].type
= RR_TEXTURE_RGBA
;
372 self
->a_icon
->texture
[0].data
.rgba
.width
= icon
->width
;
373 self
->a_icon
->texture
[0].data
.rgba
.height
= icon
->height
;
374 self
->a_icon
->texture
[0].data
.rgba
.alpha
= 0xff;
375 self
->a_icon
->texture
[0].data
.rgba
.data
= icon
->data
;
377 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
379 popup_delay_show(self
->popup
, usec
, text
);
382 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
385 self
->popup
->iconwm
= MAX(1, wm
);
386 self
->popup
->iconhm
= MAX(1, hm
);
389 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
392 ObPagerPopup
*self
= data
;
399 const guint cols
= screen_desktop_layout
.columns
;
400 const guint rows
= screen_desktop_layout
.rows
;
401 const gint linewidth
= ob_rr_theme
->obwidth
;
403 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
404 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
405 /* make them squares */
406 eachw
= eachh
= MIN(eachw
, eachh
);
409 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
410 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
412 if (eachw
<= 0 || eachh
<= 0)
415 switch (screen_desktop_layout
.orientation
) {
416 case OB_ORIENTATION_HORZ
:
417 switch (screen_desktop_layout
.start_corner
) {
418 case OB_CORNER_TOPLEFT
:
423 case OB_CORNER_TOPRIGHT
:
428 case OB_CORNER_BOTTOMRIGHT
:
431 vert_inc
= -screen_desktop_layout
.columns
;
433 case OB_CORNER_BOTTOMLEFT
:
434 n
= (rows
- 1) * cols
;
439 g_assert_not_reached();
442 case OB_ORIENTATION_VERT
:
443 switch (screen_desktop_layout
.start_corner
) {
444 case OB_CORNER_TOPLEFT
:
449 case OB_CORNER_TOPRIGHT
:
450 n
= rows
* (cols
- 1);
454 case OB_CORNER_BOTTOMRIGHT
:
459 case OB_CORNER_BOTTOMLEFT
:
465 g_assert_not_reached();
469 g_assert_not_reached();
473 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
475 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
479 if (n
< self
->desks
) {
480 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
482 a
->surface
.parent
= self
->popup
->a_bg
;
483 a
->surface
.parentx
= x
+ px
;
484 a
->surface
.parenty
= y
+ py
;
485 XMoveResizeWindow(ob_display
, self
->wins
[n
],
486 x
+ px
, y
+ py
, eachw
, eachh
);
487 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
491 n
= rown
+= vert_inc
;
495 ObPagerPopup
*pager_popup_new(void)
499 self
= g_new(ObPagerPopup
, 1);
500 self
->popup
= popup_new();
503 self
->wins
= g_new(Window
, self
->desks
);
504 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
505 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
507 self
->popup
->hasicon
= TRUE
;
508 self
->popup
->draw_icon
= pager_popup_draw_icon
;
509 self
->popup
->draw_icon_data
= self
;
514 void pager_popup_free(ObPagerPopup
*self
)
519 for (i
= 0; i
< self
->desks
; ++i
)
520 XDestroyWindow(ob_display
, self
->wins
[i
]);
522 RrAppearanceFree(self
->hilight
);
523 RrAppearanceFree(self
->unhilight
);
524 popup_free(self
->popup
);
529 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
530 gchar
*text
, guint desk
)
534 if (screen_num_desktops
< self
->desks
)
535 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
536 XDestroyWindow(ob_display
, self
->wins
[i
]);
538 if (screen_num_desktops
!= self
->desks
)
539 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
541 if (screen_num_desktops
> self
->desks
)
542 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
543 XSetWindowAttributes attr
;
546 RrColorPixel(ob_rr_theme
->osd_border_color
);
547 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
548 0, 0, 1, 1, ob_rr_theme
->obwidth
,
549 RrDepth(ob_rr_inst
), InputOutput
,
550 RrVisual(ob_rr_inst
), CWBorderPixel
,
552 XMapWindow(ob_display
, self
->wins
[i
]);
555 self
->desks
= screen_num_desktops
;
556 self
->curdesk
= desk
;
558 popup_delay_show(self
->popup
, usec
, text
);
561 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
564 self
->popup
->iconwm
= MAX(1, wm
);
565 self
->popup
->iconhm
= MAX(1, hm
);