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;
151 return FALSE
; /* don't repeat */
154 void popup_delay_show(ObPopup
*self
, gulong msec
, gchar
*text
)
159 gint emptyx
, emptyy
; /* empty space between elements */
160 gint textx
, texty
, textw
, texth
;
161 gint iconx
, icony
, iconw
, iconh
;
164 gboolean hasicon
= self
->hasicon
;
166 /* when there is no icon and the text is not parent relative, then
167 fill the whole dialog with the text appearance, don't use the bg at all
169 if (hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
170 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
174 /* set up the textures */
175 self
->a_text
->texture
[0].data
.text
.string
= text
;
177 /* measure the text out */
178 if (text
[0] != '\0') {
179 RrMinSize(self
->a_text
, &textw
, &texth
);
182 texth
= RrMinHeight(self
->a_text
);
185 /* get the height, which is also used for the icon width */
186 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
188 texth
= self
->h
- emptyy
;
189 h
= texth
* self
->iconhm
+ emptyy
;
194 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
196 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
198 iconw
= texth
* self
->iconwm
;
199 iconh
= texth
* self
->iconhm
;
200 textx
+= iconw
+ ob_rr_theme
->paddingx
;
202 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
203 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
207 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
209 /* when there is no icon, then fill the whole dialog with the text
220 w
= textw
+ emptyx
+ iconw
;
221 /* cap it at maxw/minw */
222 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
223 if (self
->minw
) w
= MAX(w
, self
->minw
);
224 textw
= w
- emptyx
- iconw
;
226 /* sanity checks to avoid crashes! */
229 if (texth
< 1) texth
= 1;
231 /* set up the x coord */
233 switch (self
->gravity
) {
234 case NorthGravity
: case CenterGravity
: case SouthGravity
:
237 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
242 /* set up the y coord */
244 switch (self
->gravity
) {
245 case WestGravity
: case CenterGravity
: case EastGravity
:
248 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
253 /* If the popup belongs to a client (eg, the moveresize popup), get
254 * the monitor for that client, otherwise do other stuff */
256 m
= client_monitor(self
->client
);
258 /* Find the monitor which contains the biggest part of the popup.
259 * If the popup is completely off screen, limit it to the intersection
260 * of all monitors and then try again. If it's still off screen, put it
262 RECT_SET(mon
, x
, y
, w
, h
);
263 m
= screen_find_monitor(&mon
);
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
);
270 if (m
== screen_num_monitors
) {
271 RECT_SET(mon
, x
, y
, w
, h
);
272 m
= screen_find_monitor(&mon
);
273 if (m
== screen_num_monitors
)
275 area
= screen_physical_area_monitor(m
);
277 x
= MAX(MIN(x
, area
->x
+area
->width
-w
), area
->x
);
278 y
= MAX(MIN(y
, area
->y
+area
->height
-h
), area
->y
);
281 /* set the windows/appearances up */
282 XMoveResizeWindow(obt_display
, self
->bg
, x
, y
, w
, h
);
283 /* when there is no icon and the text is not parent relative, then
284 fill the whole dialog with the text appearance, don't use the bg at all
286 if (hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
287 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
290 self
->a_text
->surface
.parent
= self
->a_bg
;
291 self
->a_text
->surface
.parentx
= textx
;
292 self
->a_text
->surface
.parenty
= texty
;
293 XMoveResizeWindow(obt_display
, self
->text
, textx
, texty
, textw
, texth
);
294 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
298 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
300 /* do the actual showing */
303 /* don't kill previous show timers */
304 if (!self
->delay_mapped
) {
306 g_timeout_add(msec
, popup_show_timeout
, self
);
307 self
->delay_mapped
= TRUE
;
310 popup_show_timeout(self
);
315 void popup_hide(ObPopup
*self
)
320 /* kill enter events cause by this unmapping */
321 ignore_start
= event_start_ignore_all_enters();
323 XUnmapWindow(obt_display
, self
->bg
);
324 self
->mapped
= FALSE
;
326 event_end_ignore_all_enters(ignore_start
);
327 } else if (self
->delay_mapped
) {
328 g_source_remove(self
->delay_timer
);
329 self
->delay_timer
= 0;
330 self
->delay_mapped
= FALSE
;
334 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
336 ObIconPopup
*self
= data
;
338 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
339 self
->a_icon
->surface
.parentx
= x
;
340 self
->a_icon
->surface
.parenty
= y
;
341 XMoveResizeWindow(obt_display
, self
->icon
, x
, y
, w
, h
);
342 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
345 ObIconPopup
*icon_popup_new(void)
349 self
= g_slice_new0(ObIconPopup
);
350 self
->popup
= popup_new();
351 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
352 self
->icon
= XCreateWindow(obt_display
, self
->popup
->bg
,
354 RrDepth(ob_rr_inst
), InputOutput
,
355 RrVisual(ob_rr_inst
), 0, NULL
);
356 XMapWindow(obt_display
, self
->icon
);
358 self
->popup
->hasicon
= TRUE
;
359 self
->popup
->draw_icon
= icon_popup_draw_icon
;
360 self
->popup
->draw_icon_data
= self
;
365 void icon_popup_free(ObIconPopup
*self
)
368 XDestroyWindow(obt_display
, self
->icon
);
369 RrAppearanceFree(self
->a_icon
);
370 popup_free(self
->popup
);
371 g_slice_free(ObIconPopup
, self
);
375 void icon_popup_delay_show(ObIconPopup
*self
, gulong msec
,
376 gchar
*text
, RrImage
*icon
)
379 RrAppearanceClearTextures(self
->a_icon
);
380 self
->a_icon
->texture
[0].type
= RR_TEXTURE_IMAGE
;
381 self
->a_icon
->texture
[0].data
.image
.alpha
= 0xff;
382 self
->a_icon
->texture
[0].data
.image
.image
= icon
;
384 RrAppearanceClearTextures(self
->a_icon
);
385 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
388 popup_delay_show(self
->popup
, msec
, text
);
391 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
394 self
->popup
->iconwm
= MAX(1, wm
);
395 self
->popup
->iconhm
= MAX(1, hm
);
398 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
401 ObPagerPopup
*self
= data
;
408 const guint cols
= screen_desktop_layout
.columns
;
409 const guint rows
= screen_desktop_layout
.rows
;
410 const gint linewidth
= ob_rr_theme
->obwidth
;
412 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
413 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
414 /* make them squares */
415 eachw
= eachh
= MIN(eachw
, eachh
);
418 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
419 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
421 if (eachw
<= 0 || eachh
<= 0)
424 switch (screen_desktop_layout
.orientation
) {
425 case OB_ORIENTATION_HORZ
:
426 switch (screen_desktop_layout
.start_corner
) {
427 case OB_CORNER_TOPLEFT
:
432 case OB_CORNER_TOPRIGHT
:
437 case OB_CORNER_BOTTOMRIGHT
:
440 vert_inc
= -screen_desktop_layout
.columns
;
442 case OB_CORNER_BOTTOMLEFT
:
443 n
= (rows
- 1) * cols
;
448 g_assert_not_reached();
451 case OB_ORIENTATION_VERT
:
452 switch (screen_desktop_layout
.start_corner
) {
453 case OB_CORNER_TOPLEFT
:
458 case OB_CORNER_TOPRIGHT
:
459 n
= rows
* (cols
- 1);
463 case OB_CORNER_BOTTOMRIGHT
:
468 case OB_CORNER_BOTTOMLEFT
:
474 g_assert_not_reached();
478 g_assert_not_reached();
482 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
484 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
488 if (n
< self
->desks
) {
489 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
491 a
->surface
.parent
= self
->popup
->a_bg
;
492 a
->surface
.parentx
= x
+ px
;
493 a
->surface
.parenty
= y
+ py
;
494 XMoveResizeWindow(obt_display
, self
->wins
[n
],
495 x
+ px
, y
+ py
, eachw
, eachh
);
496 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
500 n
= rown
+= vert_inc
;
504 ObPagerPopup
*pager_popup_new(void)
508 self
= g_slice_new(ObPagerPopup
);
509 self
->popup
= popup_new();
512 self
->wins
= g_new(Window
, self
->desks
);
513 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
514 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_bg
);
516 self
->popup
->hasicon
= TRUE
;
517 self
->popup
->draw_icon
= pager_popup_draw_icon
;
518 self
->popup
->draw_icon_data
= self
;
523 void pager_popup_free(ObPagerPopup
*self
)
528 for (i
= 0; i
< self
->desks
; ++i
)
529 XDestroyWindow(obt_display
, self
->wins
[i
]);
531 RrAppearanceFree(self
->hilight
);
532 RrAppearanceFree(self
->unhilight
);
533 popup_free(self
->popup
);
534 g_slice_free(ObPagerPopup
, self
);
538 void pager_popup_delay_show(ObPagerPopup
*self
, gulong msec
,
539 gchar
*text
, guint desk
)
543 if (screen_num_desktops
< self
->desks
)
544 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
545 XDestroyWindow(obt_display
, self
->wins
[i
]);
547 if (screen_num_desktops
!= self
->desks
)
548 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
550 if (screen_num_desktops
> self
->desks
)
551 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
552 XSetWindowAttributes attr
;
555 RrColorPixel(ob_rr_theme
->osd_border_color
);
556 self
->wins
[i
] = XCreateWindow(obt_display
, self
->popup
->bg
,
557 0, 0, 1, 1, ob_rr_theme
->obwidth
,
558 RrDepth(ob_rr_inst
), InputOutput
,
559 RrVisual(ob_rr_inst
), CWBorderPixel
,
561 XMapWindow(obt_display
, self
->wins
[i
]);
564 self
->desks
= screen_num_desktops
;
565 self
->curdesk
= desk
;
567 popup_delay_show(self
->popup
, msec
, text
);
570 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
573 self
->popup
->iconwm
= MAX(1, wm
);
574 self
->popup
->iconhm
= MAX(1, hm
);