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 RECT_SET(mon
, self
->x
, self
->y
, 1, 1);
160 area
= screen_physical_area_monitor(screen_find_monitor(&mon
));
162 /* when there is no icon and the text is not parent relative, then
163 fill the whole dialog with the text appearance, don't use the bg at all
165 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
166 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
170 /* set up the textures */
171 self
->a_text
->texture
[0].data
.text
.string
= text
;
173 /* measure the text out */
174 if (text
[0] != '\0') {
175 RrMinSize(self
->a_text
, &textw
, &texth
);
178 texth
= RrMinHeight(self
->a_text
);
181 /* get the height, which is also used for the icon width */
182 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
184 texth
= self
->h
- emptyy
;
185 h
= texth
* self
->iconhm
+ emptyy
;
190 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
192 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
194 iconw
= texth
* self
->iconwm
;
195 iconh
= texth
* self
->iconhm
;
196 textx
+= iconw
+ ob_rr_theme
->paddingx
;
198 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
202 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
203 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
205 /* when there is no icon, then fill the whole dialog with the text
216 w
= textw
+ emptyx
+ iconw
;
217 /* cap it at maxw/minw */
218 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
219 if (self
->minw
) w
= MAX(w
, self
->minw
);
220 textw
= w
- emptyx
- iconw
;
222 /* sanity checks to avoid crashes! */
225 if (texth
< 1) texth
= 1;
227 /* set up the x coord */
229 switch (self
->gravity
) {
230 case NorthGravity
: case CenterGravity
: case SouthGravity
:
233 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
238 /* set up the y coord */
240 switch (self
->gravity
) {
241 case WestGravity
: case CenterGravity
: case EastGravity
:
244 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
249 x
=MAX(MIN(x
, area
->width
-w
),0);
250 y
=MAX(MIN(y
, area
->height
-h
),0);
252 /* set the windows/appearances up */
253 XMoveResizeWindow(ob_display
, self
->bg
, x
, y
, w
, h
);
254 /* when there is no icon and the text is not parent relative, then
255 fill the whole dialog with the text appearance, don't use the bg at all
257 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
258 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
261 self
->a_text
->surface
.parent
= self
->a_bg
;
262 self
->a_text
->surface
.parentx
= textx
;
263 self
->a_text
->surface
.parenty
= texty
;
264 XMoveResizeWindow(ob_display
, self
->text
, textx
, texty
, textw
, texth
);
265 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
269 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
271 /* do the actual showing */
274 /* don't kill previous show timers */
275 if (!self
->delay_mapped
) {
276 ob_main_loop_timeout_add(ob_main_loop
, usec
,
277 popup_show_timeout
, self
,
278 g_direct_equal
, NULL
);
279 self
->delay_mapped
= TRUE
;
282 popup_show_timeout(self
);
289 void popup_hide(ObPopup
*self
)
294 /* kill enter events cause by this unmapping */
295 ignore_start
= event_start_ignore_all_enters();
297 XUnmapWindow(ob_display
, self
->bg
);
298 self
->mapped
= FALSE
;
300 event_end_ignore_all_enters(ignore_start
);
301 } else if (self
->delay_mapped
) {
302 ob_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
303 self
->delay_mapped
= FALSE
;
307 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
309 ObIconPopup
*self
= data
;
311 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
312 self
->a_icon
->surface
.parentx
= x
;
313 self
->a_icon
->surface
.parenty
= y
;
314 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
315 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
318 ObIconPopup
*icon_popup_new()
322 self
= g_new0(ObIconPopup
, 1);
323 self
->popup
= popup_new(TRUE
);
324 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
325 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
327 RrDepth(ob_rr_inst
), InputOutput
,
328 RrVisual(ob_rr_inst
), 0, NULL
);
329 XMapWindow(ob_display
, self
->icon
);
331 self
->popup
->hasicon
= TRUE
;
332 self
->popup
->draw_icon
= icon_popup_draw_icon
;
333 self
->popup
->draw_icon_data
= self
;
338 void icon_popup_free(ObIconPopup
*self
)
341 XDestroyWindow(ob_display
, self
->icon
);
342 RrAppearanceFree(self
->a_icon
);
343 popup_free(self
->popup
);
348 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
349 gchar
*text
, const ObClientIcon
*icon
)
352 self
->a_icon
->texture
[0].type
= RR_TEXTURE_RGBA
;
353 self
->a_icon
->texture
[0].data
.rgba
.width
= icon
->width
;
354 self
->a_icon
->texture
[0].data
.rgba
.height
= icon
->height
;
355 self
->a_icon
->texture
[0].data
.rgba
.alpha
= 0xff;
356 self
->a_icon
->texture
[0].data
.rgba
.data
= icon
->data
;
358 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
360 popup_delay_show(self
->popup
, usec
, text
);
363 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
366 self
->popup
->iconwm
= MAX(1, wm
);
367 self
->popup
->iconhm
= MAX(1, hm
);
370 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
373 ObPagerPopup
*self
= data
;
380 const guint cols
= screen_desktop_layout
.columns
;
381 const guint rows
= screen_desktop_layout
.rows
;
382 const gint linewidth
= ob_rr_theme
->obwidth
;
384 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
385 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
386 /* make them squares */
387 eachw
= eachh
= MIN(eachw
, eachh
);
390 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
391 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
393 if (eachw
<= 0 || eachh
<= 0)
396 switch (screen_desktop_layout
.orientation
) {
397 case OB_ORIENTATION_HORZ
:
398 switch (screen_desktop_layout
.start_corner
) {
399 case OB_CORNER_TOPLEFT
:
404 case OB_CORNER_TOPRIGHT
:
409 case OB_CORNER_BOTTOMRIGHT
:
412 vert_inc
= -screen_desktop_layout
.columns
;
414 case OB_CORNER_BOTTOMLEFT
:
415 n
= (rows
- 1) * cols
;
420 g_assert_not_reached();
423 case OB_ORIENTATION_VERT
:
424 switch (screen_desktop_layout
.start_corner
) {
425 case OB_CORNER_TOPLEFT
:
430 case OB_CORNER_TOPRIGHT
:
431 n
= rows
* (cols
- 1);
435 case OB_CORNER_BOTTOMRIGHT
:
440 case OB_CORNER_BOTTOMLEFT
:
446 g_assert_not_reached();
450 g_assert_not_reached();
454 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
456 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
460 if (n
< self
->desks
) {
461 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
463 a
->surface
.parent
= self
->popup
->a_bg
;
464 a
->surface
.parentx
= x
+ px
;
465 a
->surface
.parenty
= y
+ py
;
466 XMoveResizeWindow(ob_display
, self
->wins
[n
],
467 x
+ px
, y
+ py
, eachw
, eachh
);
468 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
472 n
= rown
+= vert_inc
;
476 ObPagerPopup
*pager_popup_new()
480 self
= g_new(ObPagerPopup
, 1);
481 self
->popup
= popup_new(TRUE
);
484 self
->wins
= g_new(Window
, self
->desks
);
485 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
486 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
488 self
->popup
->hasicon
= TRUE
;
489 self
->popup
->draw_icon
= pager_popup_draw_icon
;
490 self
->popup
->draw_icon_data
= self
;
495 void pager_popup_free(ObPagerPopup
*self
)
500 for (i
= 0; i
< self
->desks
; ++i
)
501 XDestroyWindow(ob_display
, self
->wins
[i
]);
503 RrAppearanceFree(self
->hilight
);
504 RrAppearanceFree(self
->unhilight
);
505 popup_free(self
->popup
);
510 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
511 gchar
*text
, guint desk
)
515 if (screen_num_desktops
< self
->desks
)
516 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
517 XDestroyWindow(ob_display
, self
->wins
[i
]);
519 if (screen_num_desktops
!= self
->desks
)
520 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
522 if (screen_num_desktops
> self
->desks
)
523 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
524 XSetWindowAttributes attr
;
527 RrColorPixel(ob_rr_theme
->osd_border_color
);
528 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
529 0, 0, 1, 1, ob_rr_theme
->obwidth
,
530 RrDepth(ob_rr_inst
), InputOutput
,
531 RrVisual(ob_rr_inst
), CWBorderPixel
,
533 XMapWindow(ob_display
, self
->wins
[i
]);
536 self
->desks
= screen_num_desktops
;
537 self
->curdesk
= desk
;
539 popup_delay_show(self
->popup
, usec
, text
);
542 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
545 self
->popup
->iconwm
= MAX(1, wm
);
546 self
->popup
->iconhm
= MAX(1, hm
);