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
;
149 self
->delay_timer
= 0;
152 return FALSE
; /* don't repeat */
155 void popup_delay_show(ObPopup
*self
, gulong msec
, gchar
*text
)
160 gint emptyx
, emptyy
; /* empty space between elements */
161 gint textx
, texty
, textw
, texth
;
162 gint iconx
, icony
, iconw
, iconh
;
165 gboolean hasicon
= self
->hasicon
;
167 /* when there is no icon and the text is not parent relative, then
168 fill the whole dialog with the text appearance, don't use the bg at all
170 if (hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
171 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
175 /* set up the textures */
176 self
->a_text
->texture
[0].data
.text
.string
= text
;
178 /* measure the text out */
179 if (text
[0] != '\0') {
180 RrMinSize(self
->a_text
, &textw
, &texth
);
183 texth
= RrMinHeight(self
->a_text
);
186 /* get the height, which is also used for the icon width */
187 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
189 texth
= self
->h
- emptyy
;
190 h
= texth
* self
->iconhm
+ emptyy
;
195 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
197 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
199 iconw
= texth
* self
->iconwm
;
200 iconh
= texth
* self
->iconhm
;
201 textx
+= iconw
+ ob_rr_theme
->paddingx
;
203 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
207 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
208 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
210 /* when there is no icon, then fill the whole dialog with the text
221 w
= textw
+ emptyx
+ iconw
;
222 /* cap it at maxw/minw */
223 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
224 if (self
->minw
) w
= MAX(w
, self
->minw
);
225 textw
= w
- emptyx
- iconw
;
227 /* sanity checks to avoid crashes! */
230 if (texth
< 1) texth
= 1;
232 /* set up the x coord */
234 switch (self
->gravity
) {
235 case NorthGravity
: case CenterGravity
: case SouthGravity
:
238 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
243 /* set up the y coord */
245 switch (self
->gravity
) {
246 case WestGravity
: case CenterGravity
: case EastGravity
:
249 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
254 /* Find the monitor which contains the biggest part of the popup.
255 * If the popup is completely off screen, limit it to the intersection
256 * of all monitors and then try again. If it's still off screen, put it
258 RECT_SET(mon
, x
, y
, w
, h
);
259 m
= screen_find_monitor(&mon
);
260 area
= screen_physical_area_monitor(m
);
262 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
263 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
265 if (m
== screen_num_monitors
) {
266 RECT_SET(mon
, x
, y
, w
, h
);
267 m
= screen_find_monitor(&mon
);
268 if (m
== screen_num_monitors
)
270 area
= screen_physical_area_monitor(m
);
272 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
273 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
276 /* set the windows/appearances up */
277 XMoveResizeWindow(obt_display
, self
->bg
, x
, y
, w
, h
);
278 /* when there is no icon and the text is not parent relative, then
279 fill the whole dialog with the text appearance, don't use the bg at all
281 if (hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
282 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
285 self
->a_text
->surface
.parent
= self
->a_bg
;
286 self
->a_text
->surface
.parentx
= textx
;
287 self
->a_text
->surface
.parenty
= texty
;
288 XMoveResizeWindow(obt_display
, self
->text
, textx
, texty
, textw
, texth
);
289 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
293 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
295 /* do the actual showing */
298 /* don't kill previous show timers */
299 if (!self
->delay_mapped
) {
301 g_timeout_add(msec
, popup_show_timeout
, self
);
302 self
->delay_mapped
= TRUE
;
305 popup_show_timeout(self
);
310 void popup_hide(ObPopup
*self
)
315 /* kill enter events cause by this unmapping */
316 ignore_start
= event_start_ignore_all_enters();
318 XUnmapWindow(obt_display
, self
->bg
);
319 self
->mapped
= FALSE
;
321 event_end_ignore_all_enters(ignore_start
);
322 } else if (self
->delay_mapped
) {
323 g_source_remove(self
->delay_timer
);
324 self
->delay_timer
= 0;
325 self
->delay_mapped
= FALSE
;
329 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
331 ObIconPopup
*self
= data
;
333 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
334 self
->a_icon
->surface
.parentx
= x
;
335 self
->a_icon
->surface
.parenty
= y
;
336 XMoveResizeWindow(obt_display
, self
->icon
, x
, y
, w
, h
);
337 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
340 ObIconPopup
*icon_popup_new(void)
344 self
= g_slice_new0(ObIconPopup
);
345 self
->popup
= popup_new();
346 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
347 self
->icon
= XCreateWindow(obt_display
, self
->popup
->bg
,
349 RrDepth(ob_rr_inst
), InputOutput
,
350 RrVisual(ob_rr_inst
), 0, NULL
);
351 XMapWindow(obt_display
, self
->icon
);
353 self
->popup
->hasicon
= TRUE
;
354 self
->popup
->draw_icon
= icon_popup_draw_icon
;
355 self
->popup
->draw_icon_data
= self
;
360 void icon_popup_free(ObIconPopup
*self
)
363 XDestroyWindow(obt_display
, self
->icon
);
364 RrAppearanceFree(self
->a_icon
);
365 popup_free(self
->popup
);
366 g_slice_free(ObIconPopup
, self
);
370 void icon_popup_delay_show(ObIconPopup
*self
, gulong msec
,
371 gchar
*text
, RrImage
*icon
)
374 RrAppearanceClearTextures(self
->a_icon
);
375 self
->a_icon
->texture
[0].type
= RR_TEXTURE_IMAGE
;
376 self
->a_icon
->texture
[0].data
.image
.alpha
= 0xff;
377 self
->a_icon
->texture
[0].data
.image
.image
= icon
;
379 RrAppearanceClearTextures(self
->a_icon
);
380 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
383 popup_delay_show(self
->popup
, msec
, text
);
386 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
389 self
->popup
->iconwm
= MAX(1, wm
);
390 self
->popup
->iconhm
= MAX(1, hm
);
393 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
396 ObPagerPopup
*self
= data
;
403 const guint cols
= screen_desktop_layout
.columns
;
404 const guint rows
= screen_desktop_layout
.rows
;
405 const gint linewidth
= ob_rr_theme
->obwidth
;
407 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
408 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
409 /* make them squares */
410 eachw
= eachh
= MIN(eachw
, eachh
);
413 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
414 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
416 if (eachw
<= 0 || eachh
<= 0)
419 switch (screen_desktop_layout
.orientation
) {
420 case OB_ORIENTATION_HORZ
:
421 switch (screen_desktop_layout
.start_corner
) {
422 case OB_CORNER_TOPLEFT
:
427 case OB_CORNER_TOPRIGHT
:
432 case OB_CORNER_BOTTOMRIGHT
:
435 vert_inc
= -screen_desktop_layout
.columns
;
437 case OB_CORNER_BOTTOMLEFT
:
438 n
= (rows
- 1) * cols
;
443 g_assert_not_reached();
446 case OB_ORIENTATION_VERT
:
447 switch (screen_desktop_layout
.start_corner
) {
448 case OB_CORNER_TOPLEFT
:
453 case OB_CORNER_TOPRIGHT
:
454 n
= rows
* (cols
- 1);
458 case OB_CORNER_BOTTOMRIGHT
:
463 case OB_CORNER_BOTTOMLEFT
:
469 g_assert_not_reached();
473 g_assert_not_reached();
477 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
479 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
483 if (n
< self
->desks
) {
484 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
486 a
->surface
.parent
= self
->popup
->a_bg
;
487 a
->surface
.parentx
= x
+ px
;
488 a
->surface
.parenty
= y
+ py
;
489 XMoveResizeWindow(obt_display
, self
->wins
[n
],
490 x
+ px
, y
+ py
, eachw
, eachh
);
491 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
495 n
= rown
+= vert_inc
;
499 ObPagerPopup
*pager_popup_new(void)
503 self
= g_slice_new(ObPagerPopup
);
504 self
->popup
= popup_new();
507 self
->wins
= g_new(Window
, self
->desks
);
508 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
509 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_bg
);
511 self
->popup
->hasicon
= TRUE
;
512 self
->popup
->draw_icon
= pager_popup_draw_icon
;
513 self
->popup
->draw_icon_data
= self
;
518 void pager_popup_free(ObPagerPopup
*self
)
523 for (i
= 0; i
< self
->desks
; ++i
)
524 XDestroyWindow(obt_display
, self
->wins
[i
]);
526 RrAppearanceFree(self
->hilight
);
527 RrAppearanceFree(self
->unhilight
);
528 popup_free(self
->popup
);
529 g_slice_free(ObPagerPopup
, self
);
533 void pager_popup_delay_show(ObPagerPopup
*self
, gulong msec
,
534 gchar
*text
, guint desk
)
538 if (screen_num_desktops
< self
->desks
)
539 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
540 XDestroyWindow(obt_display
, self
->wins
[i
]);
542 if (screen_num_desktops
!= self
->desks
)
543 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
545 if (screen_num_desktops
> self
->desks
)
546 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
547 XSetWindowAttributes attr
;
550 RrColorPixel(ob_rr_theme
->osd_border_color
);
551 self
->wins
[i
] = XCreateWindow(obt_display
, self
->popup
->bg
,
552 0, 0, 1, 1, ob_rr_theme
->obwidth
,
553 RrDepth(ob_rr_inst
), InputOutput
,
554 RrVisual(ob_rr_inst
), CWBorderPixel
,
556 XMapWindow(obt_display
, self
->wins
[i
]);
559 self
->desks
= screen_num_desktops
;
560 self
->curdesk
= desk
;
562 popup_delay_show(self
->popup
, msec
, text
);
565 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
568 self
->popup
->iconwm
= MAX(1, wm
);
569 self
->popup
->iconhm
= MAX(1, hm
);