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
;
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
));
64 void popup_free(ObPopup
*self
)
67 popup_hide(self
); /* make sure it's not showing or is being delayed and
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
;
86 void popup_text_width(ObPopup
*self
, gint w
)
91 void popup_min_width(ObPopup
*self
, gint minw
)
96 void popup_max_width(ObPopup
*self
, gint maxw
)
101 void popup_height(ObPopup
*self
, gint h
)
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
);
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
)
129 for (i
= 0; i
< num
; ++i
) {
130 popup_text_width_to_string(self
, strings
[i
]);
131 maxw
= MAX(maxw
, self
->textw
);
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
));
148 self
->delay_mapped
= FALSE
;
150 return FALSE
; /* don't repeat */
153 void popup_delay_show(ObPopup
*self
, gulong usec
, gchar
*text
)
158 gint emptyx
, emptyy
; /* empty space between elements */
159 gint textx
, texty
, textw
, texth
;
160 gint iconx
, icony
, iconw
, iconh
;
163 gboolean hasicon
= self
->hasicon
;
165 /* when there is no icon and the text is not parent relative, then
166 fill the whole dialog with the text appearance, don't use the bg at all
168 if (hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
169 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
173 /* set up the textures */
174 self
->a_text
->texture
[0].data
.text
.string
= text
;
176 /* measure the text out */
177 if (text
[0] != '\0') {
178 RrMinSize(self
->a_text
, &textw
, &texth
);
181 texth
= RrMinHeight(self
->a_text
);
184 /* get the height, which is also used for the icon width */
185 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
187 texth
= self
->h
- emptyy
;
188 h
= texth
* self
->iconhm
+ emptyy
;
193 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
195 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
197 iconw
= texth
* self
->iconwm
;
198 iconh
= texth
* self
->iconhm
;
199 textx
+= iconw
+ ob_rr_theme
->paddingx
;
201 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
205 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
206 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
208 /* when there is no icon, then fill the whole dialog with the text
219 w
= textw
+ emptyx
+ iconw
;
220 /* cap it at maxw/minw */
221 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
222 if (self
->minw
) w
= MAX(w
, self
->minw
);
223 textw
= w
- emptyx
- iconw
;
225 /* sanity checks to avoid crashes! */
228 if (texth
< 1) texth
= 1;
230 /* set up the x coord */
232 switch (self
->gravity
) {
233 case NorthGravity
: case CenterGravity
: case SouthGravity
:
236 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
241 /* set up the y coord */
243 switch (self
->gravity
) {
244 case WestGravity
: case CenterGravity
: case EastGravity
:
247 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
252 /* Find the monitor which contains the biggest part of the popup.
253 * If the popup is completely off screen, limit it to the intersection
254 * of all monitors and then try again. If it's still off screen, put it
256 RECT_SET(mon
, x
, y
, w
, h
);
257 m
= screen_find_monitor(&mon
);
258 area
= screen_physical_area_monitor(m
);
260 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
261 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
263 if (m
== screen_num_monitors
) {
264 RECT_SET(mon
, x
, y
, w
, h
);
265 m
= screen_find_monitor(&mon
);
266 if (m
== screen_num_monitors
)
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
);
274 /* set the windows/appearances up */
275 XMoveResizeWindow(obt_display
, self
->bg
, x
, y
, w
, h
);
276 /* when there is no icon and the text is not parent relative, then
277 fill the whole dialog with the text appearance, don't use the bg at all
279 if (hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
280 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
283 self
->a_text
->surface
.parent
= self
->a_bg
;
284 self
->a_text
->surface
.parentx
= textx
;
285 self
->a_text
->surface
.parenty
= texty
;
286 XMoveResizeWindow(obt_display
, self
->text
, textx
, texty
, textw
, texth
);
287 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
291 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
293 /* do the actual showing */
296 /* don't kill previous show timers */
297 if (!self
->delay_mapped
) {
298 obt_main_loop_timeout_add(ob_main_loop
, usec
,
299 popup_show_timeout
, self
,
300 g_direct_equal
, NULL
);
301 self
->delay_mapped
= TRUE
;
304 popup_show_timeout(self
);
309 void popup_hide(ObPopup
*self
)
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 obt_main_loop_timeout_remove_data(ob_main_loop
, popup_show_timeout
, self
, FALSE
);
323 self
->delay_mapped
= FALSE
;
327 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
329 ObIconPopup
*self
= data
;
331 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
332 self
->a_icon
->surface
.parentx
= x
;
333 self
->a_icon
->surface
.parenty
= y
;
334 XMoveResizeWindow(obt_display
, self
->icon
, x
, y
, w
, h
);
335 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
338 ObIconPopup
*icon_popup_new(void)
342 self
= g_slice_new0(ObIconPopup
);
343 self
->popup
= popup_new();
344 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
345 self
->icon
= XCreateWindow(obt_display
, self
->popup
->bg
,
347 RrDepth(ob_rr_inst
), InputOutput
,
348 RrVisual(ob_rr_inst
), 0, NULL
);
349 XMapWindow(obt_display
, self
->icon
);
351 self
->popup
->hasicon
= TRUE
;
352 self
->popup
->draw_icon
= icon_popup_draw_icon
;
353 self
->popup
->draw_icon_data
= self
;
358 void icon_popup_free(ObIconPopup
*self
)
361 XDestroyWindow(obt_display
, self
->icon
);
362 RrAppearanceFree(self
->a_icon
);
363 popup_free(self
->popup
);
364 g_slice_free(ObIconPopup
, self
);
368 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
369 gchar
*text
, RrImage
*icon
)
372 RrAppearanceClearTextures(self
->a_icon
);
373 self
->a_icon
->texture
[0].type
= RR_TEXTURE_IMAGE
;
374 self
->a_icon
->texture
[0].data
.image
.alpha
= 0xff;
375 self
->a_icon
->texture
[0].data
.image
.image
= icon
;
377 RrAppearanceClearTextures(self
->a_icon
);
378 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
381 popup_delay_show(self
->popup
, usec
, text
);
384 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
387 self
->popup
->iconwm
= MAX(1, wm
);
388 self
->popup
->iconhm
= MAX(1, hm
);
391 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
394 ObPagerPopup
*self
= data
;
401 const guint cols
= screen_desktop_layout
.columns
;
402 const guint rows
= screen_desktop_layout
.rows
;
403 const gint linewidth
= ob_rr_theme
->obwidth
;
405 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
406 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
407 /* make them squares */
408 eachw
= eachh
= MIN(eachw
, eachh
);
411 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
412 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
414 if (eachw
<= 0 || eachh
<= 0)
417 switch (screen_desktop_layout
.orientation
) {
418 case OB_ORIENTATION_HORZ
:
419 switch (screen_desktop_layout
.start_corner
) {
420 case OB_CORNER_TOPLEFT
:
425 case OB_CORNER_TOPRIGHT
:
430 case OB_CORNER_BOTTOMRIGHT
:
433 vert_inc
= -screen_desktop_layout
.columns
;
435 case OB_CORNER_BOTTOMLEFT
:
436 n
= (rows
- 1) * cols
;
441 g_assert_not_reached();
444 case OB_ORIENTATION_VERT
:
445 switch (screen_desktop_layout
.start_corner
) {
446 case OB_CORNER_TOPLEFT
:
451 case OB_CORNER_TOPRIGHT
:
452 n
= rows
* (cols
- 1);
456 case OB_CORNER_BOTTOMRIGHT
:
461 case OB_CORNER_BOTTOMLEFT
:
467 g_assert_not_reached();
471 g_assert_not_reached();
475 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
477 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
481 if (n
< self
->desks
) {
482 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
484 a
->surface
.parent
= self
->popup
->a_bg
;
485 a
->surface
.parentx
= x
+ px
;
486 a
->surface
.parenty
= y
+ py
;
487 XMoveResizeWindow(obt_display
, self
->wins
[n
],
488 x
+ px
, y
+ py
, eachw
, eachh
);
489 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
493 n
= rown
+= vert_inc
;
497 ObPagerPopup
*pager_popup_new(void)
501 self
= g_slice_new(ObPagerPopup
);
502 self
->popup
= popup_new();
505 self
->wins
= g_new(Window
, self
->desks
);
506 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
507 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_bg
);
509 self
->popup
->hasicon
= TRUE
;
510 self
->popup
->draw_icon
= pager_popup_draw_icon
;
511 self
->popup
->draw_icon_data
= self
;
516 void pager_popup_free(ObPagerPopup
*self
)
521 for (i
= 0; i
< self
->desks
; ++i
)
522 XDestroyWindow(obt_display
, self
->wins
[i
]);
524 RrAppearanceFree(self
->hilight
);
525 RrAppearanceFree(self
->unhilight
);
526 popup_free(self
->popup
);
527 g_slice_free(ObPagerPopup
, self
);
531 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
532 gchar
*text
, guint desk
)
536 if (screen_num_desktops
< self
->desks
)
537 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
538 XDestroyWindow(obt_display
, self
->wins
[i
]);
540 if (screen_num_desktops
!= self
->desks
)
541 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
543 if (screen_num_desktops
> self
->desks
)
544 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
545 XSetWindowAttributes attr
;
548 RrColorPixel(ob_rr_theme
->osd_border_color
);
549 self
->wins
[i
] = XCreateWindow(obt_display
, self
->popup
->bg
,
550 0, 0, 1, 1, ob_rr_theme
->obwidth
,
551 RrDepth(ob_rr_inst
), InputOutput
,
552 RrVisual(ob_rr_inst
), CWBorderPixel
,
554 XMapWindow(obt_display
, self
->wins
[i
]);
557 self
->desks
= screen_num_desktops
;
558 self
->curdesk
= desk
;
560 popup_delay_show(self
->popup
, usec
, text
);
563 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
566 self
->popup
->iconwm
= MAX(1, wm
);
567 self
->popup
->iconhm
= MAX(1, hm
);