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 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
162 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
166 /* set up the textures */
167 self
->a_text
->texture
[0].data
.text
.string
= text
;
169 /* measure the text out */
170 if (text
[0] != '\0') {
171 RrMinSize(self
->a_text
, &textw
, &texth
);
174 texth
= RrMinHeight(self
->a_text
);
177 /* get the height, which is also used for the icon width */
178 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
180 texth
= self
->h
- emptyy
;
181 h
= texth
* self
->iconhm
+ emptyy
;
186 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
188 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
190 iconw
= texth
* self
->iconwm
;
191 iconh
= texth
* self
->iconhm
;
192 textx
+= iconw
+ ob_rr_theme
->paddingx
;
194 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
198 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
199 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
201 if (!(self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
))
209 w
= textw
+ emptyx
+ iconw
;
210 /* cap it at maxw/minw */
211 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
212 if (self
->minw
) w
= MAX(w
, self
->minw
);
213 textw
= w
- emptyx
- iconw
;
215 /* sanity checks to avoid crashes! */
218 if (texth
< 1) texth
= 1;
220 /* set up the x coord */
222 switch (self
->gravity
) {
223 case NorthGravity
: case CenterGravity
: case SouthGravity
:
226 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
231 /* set up the y coord */
233 switch (self
->gravity
) {
234 case WestGravity
: case CenterGravity
: case EastGravity
:
237 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
242 x
=MAX(MIN(x
, area
->width
-w
),0);
243 y
=MAX(MIN(y
, area
->height
-h
),0);
245 /* set the windows/appearances up */
246 XMoveResizeWindow(ob_display
, self
->bg
, x
, y
, w
, h
);
247 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
248 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
251 self
->a_text
->surface
.parent
= self
->a_bg
;
252 self
->a_text
->surface
.parentx
= textx
;
253 self
->a_text
->surface
.parenty
= texty
;
254 XMoveResizeWindow(ob_display
, self
->text
, textx
, texty
, textw
, texth
);
255 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
259 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
261 /* do the actual showing */
264 /* don't kill previous show timers */
265 if (!self
->delay_mapped
) {
266 ob_main_loop_timeout_add(ob_main_loop
, usec
,
267 popup_show_timeout
, self
,
268 g_direct_equal
, NULL
);
269 self
->delay_mapped
= TRUE
;
272 popup_show_timeout(self
);
277 void popup_hide(ObPopup
*self
)
282 /* kill enter events cause by this unmapping */
283 ignore_start
= event_start_ignore_all_enters();
285 XUnmapWindow(ob_display
, self
->bg
);
286 self
->mapped
= FALSE
;
288 event_end_ignore_all_enters(ignore_start
);
289 } else if (self
->delay_mapped
) {
290 ob_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
291 self
->delay_mapped
= FALSE
;
295 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
297 ObIconPopup
*self
= data
;
299 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
300 self
->a_icon
->surface
.parentx
= x
;
301 self
->a_icon
->surface
.parenty
= y
;
302 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
303 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
306 ObIconPopup
*icon_popup_new()
310 self
= g_new0(ObIconPopup
, 1);
311 self
->popup
= popup_new(TRUE
);
312 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
313 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
315 RrDepth(ob_rr_inst
), InputOutput
,
316 RrVisual(ob_rr_inst
), 0, NULL
);
317 XMapWindow(ob_display
, self
->icon
);
319 self
->popup
->hasicon
= TRUE
;
320 self
->popup
->draw_icon
= icon_popup_draw_icon
;
321 self
->popup
->draw_icon_data
= self
;
326 void icon_popup_free(ObIconPopup
*self
)
329 XDestroyWindow(ob_display
, self
->icon
);
330 RrAppearanceFree(self
->a_icon
);
331 popup_free(self
->popup
);
336 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
337 gchar
*text
, const ObClientIcon
*icon
)
340 self
->a_icon
->texture
[0].type
= RR_TEXTURE_RGBA
;
341 self
->a_icon
->texture
[0].data
.rgba
.width
= icon
->width
;
342 self
->a_icon
->texture
[0].data
.rgba
.height
= icon
->height
;
343 self
->a_icon
->texture
[0].data
.rgba
.alpha
= 0xff;
344 self
->a_icon
->texture
[0].data
.rgba
.data
= icon
->data
;
346 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
348 popup_delay_show(self
->popup
, usec
, text
);
351 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
354 self
->popup
->iconwm
= MAX(1, wm
);
355 self
->popup
->iconhm
= MAX(1, hm
);
358 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
361 ObPagerPopup
*self
= data
;
368 const guint cols
= screen_desktop_layout
.columns
;
369 const guint rows
= screen_desktop_layout
.rows
;
370 const gint linewidth
= ob_rr_theme
->obwidth
;
372 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
373 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
374 /* make them squares */
375 eachw
= eachh
= MIN(eachw
, eachh
);
378 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
379 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
381 if (eachw
<= 0 || eachh
<= 0)
384 switch (screen_desktop_layout
.orientation
) {
385 case OB_ORIENTATION_HORZ
:
386 switch (screen_desktop_layout
.start_corner
) {
387 case OB_CORNER_TOPLEFT
:
392 case OB_CORNER_TOPRIGHT
:
397 case OB_CORNER_BOTTOMRIGHT
:
400 vert_inc
= -screen_desktop_layout
.columns
;
402 case OB_CORNER_BOTTOMLEFT
:
403 n
= (rows
- 1) * cols
;
408 g_assert_not_reached();
411 case OB_ORIENTATION_VERT
:
412 switch (screen_desktop_layout
.start_corner
) {
413 case OB_CORNER_TOPLEFT
:
418 case OB_CORNER_TOPRIGHT
:
419 n
= rows
* (cols
- 1);
423 case OB_CORNER_BOTTOMRIGHT
:
428 case OB_CORNER_BOTTOMLEFT
:
434 g_assert_not_reached();
438 g_assert_not_reached();
442 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
444 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
448 if (n
< self
->desks
) {
449 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
451 a
->surface
.parent
= self
->popup
->a_bg
;
452 a
->surface
.parentx
= x
+ px
;
453 a
->surface
.parenty
= y
+ py
;
454 XMoveResizeWindow(ob_display
, self
->wins
[n
],
455 x
+ px
, y
+ py
, eachw
, eachh
);
456 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
460 n
= rown
+= vert_inc
;
464 ObPagerPopup
*pager_popup_new()
468 self
= g_new(ObPagerPopup
, 1);
469 self
->popup
= popup_new(TRUE
);
472 self
->wins
= g_new(Window
, self
->desks
);
473 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
474 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
476 self
->popup
->hasicon
= TRUE
;
477 self
->popup
->draw_icon
= pager_popup_draw_icon
;
478 self
->popup
->draw_icon_data
= self
;
483 void pager_popup_free(ObPagerPopup
*self
)
488 for (i
= 0; i
< self
->desks
; ++i
)
489 XDestroyWindow(ob_display
, self
->wins
[i
]);
491 RrAppearanceFree(self
->hilight
);
492 RrAppearanceFree(self
->unhilight
);
493 popup_free(self
->popup
);
498 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
499 gchar
*text
, guint desk
)
503 if (screen_num_desktops
< self
->desks
)
504 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
505 XDestroyWindow(ob_display
, self
->wins
[i
]);
507 if (screen_num_desktops
!= self
->desks
)
508 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
510 if (screen_num_desktops
> self
->desks
)
511 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
512 XSetWindowAttributes attr
;
515 RrColorPixel(ob_rr_theme
->osd_border_color
);
516 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
517 0, 0, 1, 1, ob_rr_theme
->obwidth
,
518 RrDepth(ob_rr_inst
), InputOutput
,
519 RrVisual(ob_rr_inst
), CWBorderPixel
,
521 XMapWindow(ob_display
, self
->wins
[i
]);
524 self
->desks
= screen_num_desktops
;
525 self
->curdesk
= desk
;
527 popup_delay_show(self
->popup
, usec
, text
);
530 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
533 self
->popup
->iconwm
= MAX(1, wm
);
534 self
->popup
->iconhm
= MAX(1, hm
);