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
);
288 void popup_hide(ObPopup
*self
)
293 /* kill enter events cause by this unmapping */
294 ignore_start
= event_start_ignore_all_enters();
296 XUnmapWindow(ob_display
, self
->bg
);
297 self
->mapped
= FALSE
;
299 event_end_ignore_all_enters(ignore_start
);
300 } else if (self
->delay_mapped
) {
301 ob_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
302 self
->delay_mapped
= FALSE
;
306 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
308 ObIconPopup
*self
= data
;
310 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
311 self
->a_icon
->surface
.parentx
= x
;
312 self
->a_icon
->surface
.parenty
= y
;
313 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
314 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
317 ObIconPopup
*icon_popup_new()
321 self
= g_new0(ObIconPopup
, 1);
322 self
->popup
= popup_new(TRUE
);
323 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
324 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
326 RrDepth(ob_rr_inst
), InputOutput
,
327 RrVisual(ob_rr_inst
), 0, NULL
);
328 XMapWindow(ob_display
, self
->icon
);
330 self
->popup
->hasicon
= TRUE
;
331 self
->popup
->draw_icon
= icon_popup_draw_icon
;
332 self
->popup
->draw_icon_data
= self
;
337 void icon_popup_free(ObIconPopup
*self
)
340 XDestroyWindow(ob_display
, self
->icon
);
341 RrAppearanceFree(self
->a_icon
);
342 popup_free(self
->popup
);
347 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
348 gchar
*text
, const ObClientIcon
*icon
)
351 self
->a_icon
->texture
[0].type
= RR_TEXTURE_RGBA
;
352 self
->a_icon
->texture
[0].data
.rgba
.width
= icon
->width
;
353 self
->a_icon
->texture
[0].data
.rgba
.height
= icon
->height
;
354 self
->a_icon
->texture
[0].data
.rgba
.alpha
= 0xff;
355 self
->a_icon
->texture
[0].data
.rgba
.data
= icon
->data
;
357 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
359 popup_delay_show(self
->popup
, usec
, text
);
362 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
365 self
->popup
->iconwm
= MAX(1, wm
);
366 self
->popup
->iconhm
= MAX(1, hm
);
369 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
372 ObPagerPopup
*self
= data
;
379 const guint cols
= screen_desktop_layout
.columns
;
380 const guint rows
= screen_desktop_layout
.rows
;
381 const gint linewidth
= ob_rr_theme
->obwidth
;
383 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
384 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
385 /* make them squares */
386 eachw
= eachh
= MIN(eachw
, eachh
);
389 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
390 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
392 if (eachw
<= 0 || eachh
<= 0)
395 switch (screen_desktop_layout
.orientation
) {
396 case OB_ORIENTATION_HORZ
:
397 switch (screen_desktop_layout
.start_corner
) {
398 case OB_CORNER_TOPLEFT
:
403 case OB_CORNER_TOPRIGHT
:
408 case OB_CORNER_BOTTOMRIGHT
:
411 vert_inc
= -screen_desktop_layout
.columns
;
413 case OB_CORNER_BOTTOMLEFT
:
414 n
= (rows
- 1) * cols
;
419 g_assert_not_reached();
422 case OB_ORIENTATION_VERT
:
423 switch (screen_desktop_layout
.start_corner
) {
424 case OB_CORNER_TOPLEFT
:
429 case OB_CORNER_TOPRIGHT
:
430 n
= rows
* (cols
- 1);
434 case OB_CORNER_BOTTOMRIGHT
:
439 case OB_CORNER_BOTTOMLEFT
:
445 g_assert_not_reached();
449 g_assert_not_reached();
453 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
455 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
459 if (n
< self
->desks
) {
460 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
462 a
->surface
.parent
= self
->popup
->a_bg
;
463 a
->surface
.parentx
= x
+ px
;
464 a
->surface
.parenty
= y
+ py
;
465 XMoveResizeWindow(ob_display
, self
->wins
[n
],
466 x
+ px
, y
+ py
, eachw
, eachh
);
467 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
471 n
= rown
+= vert_inc
;
475 ObPagerPopup
*pager_popup_new()
479 self
= g_new(ObPagerPopup
, 1);
480 self
->popup
= popup_new(TRUE
);
483 self
->wins
= g_new(Window
, self
->desks
);
484 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
485 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
487 self
->popup
->hasicon
= TRUE
;
488 self
->popup
->draw_icon
= pager_popup_draw_icon
;
489 self
->popup
->draw_icon_data
= self
;
494 void pager_popup_free(ObPagerPopup
*self
)
499 for (i
= 0; i
< self
->desks
; ++i
)
500 XDestroyWindow(ob_display
, self
->wins
[i
]);
502 RrAppearanceFree(self
->hilight
);
503 RrAppearanceFree(self
->unhilight
);
504 popup_free(self
->popup
);
509 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
510 gchar
*text
, guint desk
)
514 if (screen_num_desktops
< self
->desks
)
515 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
516 XDestroyWindow(ob_display
, self
->wins
[i
]);
518 if (screen_num_desktops
!= self
->desks
)
519 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
521 if (screen_num_desktops
> self
->desks
)
522 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
523 XSetWindowAttributes attr
;
526 RrColorPixel(ob_rr_theme
->osd_border_color
);
527 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
528 0, 0, 1, 1, ob_rr_theme
->obwidth
,
529 RrDepth(ob_rr_inst
), InputOutput
,
530 RrVisual(ob_rr_inst
), CWBorderPixel
,
532 XMapWindow(ob_display
, self
->wins
[i
]);
535 self
->desks
= screen_num_desktops
;
536 self
->curdesk
= desk
;
538 popup_delay_show(self
->popup
, usec
, text
);
541 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
544 self
->popup
->iconwm
= MAX(1, wm
);
545 self
->popup
->iconhm
= MAX(1, hm
);