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"
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
));
64 void popup_free(ObPopup
*self
)
67 XDestroyWindow(ob_display
, self
->bg
);
68 XDestroyWindow(ob_display
, self
->text
);
69 RrAppearanceFree(self
->a_bg
);
70 RrAppearanceFree(self
->a_text
);
71 stacking_remove(self
);
76 void popup_position(ObPopup
*self
, gint gravity
, gint x
, gint y
)
78 self
->gravity
= gravity
;
83 void popup_text_width(ObPopup
*self
, gint w
)
88 void popup_min_width(ObPopup
*self
, gint minw
)
93 void popup_max_width(ObPopup
*self
, gint maxw
)
98 void popup_height(ObPopup
*self
, gint h
)
102 /* don't let the height be smaller than the text */
103 texth
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
104 self
->h
= MAX(h
, texth
);
107 void popup_text_width_to_string(ObPopup
*self
, gchar
*text
)
109 if (text
[0] != '\0') {
110 self
->a_text
->texture
[0].data
.text
.string
= text
;
111 self
->textw
= RrMinWidth(self
->a_text
);
116 void popup_height_to_string(ObPopup
*self
, gchar
*text
)
118 self
->h
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
121 void popup_text_width_to_strings(ObPopup
*self
, gchar
**strings
, gint num
)
126 for (i
= 0; i
< num
; ++i
) {
127 popup_text_width_to_string(self
, strings
[i
]);
128 maxw
= MAX(maxw
, self
->textw
);
133 void popup_set_text_align(ObPopup
*self
, RrJustify align
)
135 self
->a_text
->texture
[0].data
.text
.justify
= align
;
138 static gboolean
popup_show_timeout(gpointer data
)
140 ObPopup
*self
= data
;
142 XMapWindow(ob_display
, self
->bg
);
143 stacking_raise(INTERNAL_AS_WINDOW(self
));
145 self
->delay_mapped
= FALSE
;
147 return FALSE
; /* don't repeat */
150 void popup_delay_show(ObPopup
*self
, gulong usec
, gchar
*text
)
154 gint emptyx
, emptyy
; /* empty space between elements */
155 gint textx
, texty
, textw
, texth
;
156 gint iconx
, icony
, iconw
, iconh
;
159 area
= screen_physical_area();
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 x
=MAX(MIN(x
, area
->width
-w
),0);
249 y
=MAX(MIN(y
, area
->height
-h
),0);
251 /* set the windows/appearances up */
252 XMoveResizeWindow(ob_display
, self
->bg
, x
, y
, w
, h
);
253 /* when there is no icon and the text is not parent relative, then
254 fill the whole dialog with the text appearance, don't use the bg at all
256 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
257 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
260 self
->a_text
->surface
.parent
= self
->a_bg
;
261 self
->a_text
->surface
.parentx
= textx
;
262 self
->a_text
->surface
.parenty
= texty
;
263 XMoveResizeWindow(ob_display
, self
->text
, textx
, texty
, textw
, texth
);
264 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
268 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
270 /* do the actual showing */
273 /* don't kill previous show timers */
274 if (!self
->delay_mapped
) {
275 ob_main_loop_timeout_add(ob_main_loop
, usec
,
276 popup_show_timeout
, self
,
277 g_direct_equal
, NULL
);
278 self
->delay_mapped
= TRUE
;
281 popup_show_timeout(self
);
286 void popup_hide(ObPopup
*self
)
291 /* kill enter events cause by this unmapping */
292 ignore_start
= event_start_ignore_all_enters();
294 XUnmapWindow(ob_display
, self
->bg
);
295 self
->mapped
= FALSE
;
297 event_end_ignore_all_enters(ignore_start
);
298 } else if (self
->delay_mapped
) {
299 ob_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
300 self
->delay_mapped
= FALSE
;
304 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
306 ObIconPopup
*self
= data
;
308 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
309 self
->a_icon
->surface
.parentx
= x
;
310 self
->a_icon
->surface
.parenty
= y
;
311 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
312 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
315 ObIconPopup
*icon_popup_new()
319 self
= g_new0(ObIconPopup
, 1);
320 self
->popup
= popup_new(TRUE
);
321 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
322 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
324 RrDepth(ob_rr_inst
), InputOutput
,
325 RrVisual(ob_rr_inst
), 0, NULL
);
326 XMapWindow(ob_display
, self
->icon
);
328 self
->popup
->hasicon
= TRUE
;
329 self
->popup
->draw_icon
= icon_popup_draw_icon
;
330 self
->popup
->draw_icon_data
= self
;
335 void icon_popup_free(ObIconPopup
*self
)
338 XDestroyWindow(ob_display
, self
->icon
);
339 RrAppearanceFree(self
->a_icon
);
340 popup_free(self
->popup
);
345 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
346 gchar
*text
, const ObClientIcon
*icon
)
349 self
->a_icon
->texture
[0].type
= RR_TEXTURE_RGBA
;
350 self
->a_icon
->texture
[0].data
.rgba
.width
= icon
->width
;
351 self
->a_icon
->texture
[0].data
.rgba
.height
= icon
->height
;
352 self
->a_icon
->texture
[0].data
.rgba
.alpha
= 0xff;
353 self
->a_icon
->texture
[0].data
.rgba
.data
= icon
->data
;
355 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
357 popup_delay_show(self
->popup
, usec
, text
);
360 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
363 self
->popup
->iconwm
= MAX(1, wm
);
364 self
->popup
->iconhm
= MAX(1, hm
);
367 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
370 ObPagerPopup
*self
= data
;
377 const guint cols
= screen_desktop_layout
.columns
;
378 const guint rows
= screen_desktop_layout
.rows
;
379 const gint linewidth
= ob_rr_theme
->obwidth
;
381 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
382 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
383 /* make them squares */
384 eachw
= eachh
= MIN(eachw
, eachh
);
387 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
388 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
390 if (eachw
<= 0 || eachh
<= 0)
393 switch (screen_desktop_layout
.orientation
) {
394 case OB_ORIENTATION_HORZ
:
395 switch (screen_desktop_layout
.start_corner
) {
396 case OB_CORNER_TOPLEFT
:
401 case OB_CORNER_TOPRIGHT
:
406 case OB_CORNER_BOTTOMRIGHT
:
409 vert_inc
= -screen_desktop_layout
.columns
;
411 case OB_CORNER_BOTTOMLEFT
:
412 n
= (rows
- 1) * cols
;
417 g_assert_not_reached();
420 case OB_ORIENTATION_VERT
:
421 switch (screen_desktop_layout
.start_corner
) {
422 case OB_CORNER_TOPLEFT
:
427 case OB_CORNER_TOPRIGHT
:
428 n
= rows
* (cols
- 1);
432 case OB_CORNER_BOTTOMRIGHT
:
437 case OB_CORNER_BOTTOMLEFT
:
443 g_assert_not_reached();
447 g_assert_not_reached();
451 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
453 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
457 if (n
< self
->desks
) {
458 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
460 a
->surface
.parent
= self
->popup
->a_bg
;
461 a
->surface
.parentx
= x
+ px
;
462 a
->surface
.parenty
= y
+ py
;
463 XMoveResizeWindow(ob_display
, self
->wins
[n
],
464 x
+ px
, y
+ py
, eachw
, eachh
);
465 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
469 n
= rown
+= vert_inc
;
473 ObPagerPopup
*pager_popup_new()
477 self
= g_new(ObPagerPopup
, 1);
478 self
->popup
= popup_new(TRUE
);
481 self
->wins
= g_new(Window
, self
->desks
);
482 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
483 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
485 self
->popup
->hasicon
= TRUE
;
486 self
->popup
->draw_icon
= pager_popup_draw_icon
;
487 self
->popup
->draw_icon_data
= self
;
492 void pager_popup_free(ObPagerPopup
*self
)
497 for (i
= 0; i
< self
->desks
; ++i
)
498 XDestroyWindow(ob_display
, self
->wins
[i
]);
500 RrAppearanceFree(self
->hilight
);
501 RrAppearanceFree(self
->unhilight
);
502 popup_free(self
->popup
);
507 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
508 gchar
*text
, guint desk
)
512 if (screen_num_desktops
< self
->desks
)
513 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
514 XDestroyWindow(ob_display
, self
->wins
[i
]);
516 if (screen_num_desktops
!= self
->desks
)
517 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
519 if (screen_num_desktops
> self
->desks
)
520 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
521 XSetWindowAttributes attr
;
524 RrColorPixel(ob_rr_theme
->osd_border_color
);
525 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
526 0, 0, 1, 1, ob_rr_theme
->obwidth
,
527 RrDepth(ob_rr_inst
), InputOutput
,
528 RrVisual(ob_rr_inst
), CWBorderPixel
,
530 XMapWindow(ob_display
, self
->wins
[i
]);
533 self
->desks
= screen_num_desktops
;
534 self
->curdesk
= desk
;
536 popup_delay_show(self
->popup
, usec
, text
);
539 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
542 self
->popup
->iconwm
= MAX(1, wm
);
543 self
->popup
->iconhm
= MAX(1, hm
);