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"
32 ObPopup
*popup_new(void)
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
));
61 g_hash_table_insert(window_map
, &self
->bg
, self
);
65 void popup_free(ObPopup
*self
)
68 XDestroyWindow(ob_display
, self
->bg
);
69 XDestroyWindow(ob_display
, self
->text
);
70 RrAppearanceFree(self
->a_bg
);
71 RrAppearanceFree(self
->a_text
);
72 g_hash_table_remove(window_map
, &self
->bg
);
73 stacking_remove(self
);
78 void popup_position(ObPopup
*self
, gint gravity
, gint x
, gint y
)
80 self
->gravity
= gravity
;
85 void popup_text_width(ObPopup
*self
, gint w
)
90 void popup_min_width(ObPopup
*self
, gint minw
)
95 void popup_max_width(ObPopup
*self
, gint maxw
)
100 void popup_height(ObPopup
*self
, gint h
)
104 /* don't let the height be smaller than the text */
105 texth
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
106 self
->h
= MAX(h
, texth
);
109 void popup_text_width_to_string(ObPopup
*self
, gchar
*text
)
111 if (text
[0] != '\0') {
112 self
->a_text
->texture
[0].data
.text
.string
= text
;
113 self
->textw
= RrMinWidth(self
->a_text
);
118 void popup_height_to_string(ObPopup
*self
, gchar
*text
)
120 self
->h
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
123 void popup_text_width_to_strings(ObPopup
*self
, gchar
**strings
, gint num
)
128 for (i
= 0; i
< num
; ++i
) {
129 popup_text_width_to_string(self
, strings
[i
]);
130 maxw
= MAX(maxw
, self
->textw
);
135 void popup_set_text_align(ObPopup
*self
, RrJustify align
)
137 self
->a_text
->texture
[0].data
.text
.justify
= align
;
140 static gboolean
popup_show_timeout(gpointer data
)
142 ObPopup
*self
= data
;
144 XMapWindow(ob_display
, self
->bg
);
145 stacking_raise(INTERNAL_AS_WINDOW(self
));
147 self
->delay_mapped
= FALSE
;
149 return FALSE
; /* don't repeat */
152 void popup_delay_show(ObPopup
*self
, gulong usec
, gchar
*text
)
157 gint emptyx
, emptyy
; /* empty space between elements */
158 gint textx
, texty
, textw
, texth
;
159 gint iconx
, icony
, iconw
, iconh
;
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 /* Find the monitor which contains the biggest part of the popup.
250 * If the popup is completely off screen, limit it to the intersection
251 * of all monitors and then try again. If it's still off screen, put it
253 RECT_SET(mon
, x
, y
, w
, h
);
254 m
= screen_find_monitor(&mon
);
255 area
= screen_physical_area_monitor(m
);
257 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
258 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
262 if (m
== screen_num_monitors
) {
263 RECT_SET(mon
, x
, y
, w
, h
);
264 m
= screen_find_monitor(&mon
);
265 if (m
== screen_num_monitors
)
267 area
= screen_physical_area_monitor(m
);
269 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
270 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
275 /* set the windows/appearances up */
276 XMoveResizeWindow(ob_display
, self
->bg
, x
, y
, w
, h
);
277 /* when there is no icon and the text is not parent relative, then
278 fill the whole dialog with the text appearance, don't use the bg at all
280 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
281 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
284 self
->a_text
->surface
.parent
= self
->a_bg
;
285 self
->a_text
->surface
.parentx
= textx
;
286 self
->a_text
->surface
.parenty
= texty
;
287 XMoveResizeWindow(ob_display
, self
->text
, textx
, texty
, textw
, texth
);
288 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
292 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
294 /* do the actual showing */
297 /* don't kill previous show timers */
298 if (!self
->delay_mapped
) {
299 ob_main_loop_timeout_add(ob_main_loop
, usec
,
300 popup_show_timeout
, self
,
301 g_direct_equal
, NULL
);
302 self
->delay_mapped
= TRUE
;
305 popup_show_timeout(self
);
310 void popup_hide(ObPopup
*self
)
315 /* kill enter events cause by this unmapping */
316 ignore_start
= event_start_ignore_all_enters();
318 XUnmapWindow(ob_display
, self
->bg
);
319 self
->mapped
= FALSE
;
321 event_end_ignore_all_enters(ignore_start
);
322 } else if (self
->delay_mapped
) {
323 ob_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
324 self
->delay_mapped
= FALSE
;
328 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
330 ObIconPopup
*self
= data
;
332 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
333 self
->a_icon
->surface
.parentx
= x
;
334 self
->a_icon
->surface
.parenty
= y
;
335 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
336 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
339 ObIconPopup
*icon_popup_new(void)
343 self
= g_new0(ObIconPopup
, 1);
344 self
->popup
= popup_new();
345 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
346 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
348 RrDepth(ob_rr_inst
), InputOutput
,
349 RrVisual(ob_rr_inst
), 0, NULL
);
350 XMapWindow(ob_display
, self
->icon
);
352 self
->popup
->hasicon
= TRUE
;
353 self
->popup
->draw_icon
= icon_popup_draw_icon
;
354 self
->popup
->draw_icon_data
= self
;
359 void icon_popup_free(ObIconPopup
*self
)
362 XDestroyWindow(ob_display
, self
->icon
);
363 RrAppearanceFree(self
->a_icon
);
364 popup_free(self
->popup
);
369 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
370 gchar
*text
, RrImage
*icon
)
373 RrAppearanceClearTextures(self
->a_icon
);
374 self
->a_icon
->texture
[0].type
= RR_TEXTURE_IMAGE
;
375 self
->a_icon
->texture
[0].data
.image
.alpha
= 0xff;
376 self
->a_icon
->texture
[0].data
.image
.image
= icon
;
378 RrAppearanceClearTextures(self
->a_icon
);
379 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
382 popup_delay_show(self
->popup
, usec
, text
);
385 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
388 self
->popup
->iconwm
= MAX(1, wm
);
389 self
->popup
->iconhm
= MAX(1, hm
);
392 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
395 ObPagerPopup
*self
= data
;
402 const guint cols
= screen_desktop_layout
.columns
;
403 const guint rows
= screen_desktop_layout
.rows
;
404 const gint linewidth
= ob_rr_theme
->obwidth
;
406 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
407 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
408 /* make them squares */
409 eachw
= eachh
= MIN(eachw
, eachh
);
412 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
413 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
415 if (eachw
<= 0 || eachh
<= 0)
418 switch (screen_desktop_layout
.orientation
) {
419 case OB_ORIENTATION_HORZ
:
420 switch (screen_desktop_layout
.start_corner
) {
421 case OB_CORNER_TOPLEFT
:
426 case OB_CORNER_TOPRIGHT
:
431 case OB_CORNER_BOTTOMRIGHT
:
434 vert_inc
= -screen_desktop_layout
.columns
;
436 case OB_CORNER_BOTTOMLEFT
:
437 n
= (rows
- 1) * cols
;
442 g_assert_not_reached();
445 case OB_ORIENTATION_VERT
:
446 switch (screen_desktop_layout
.start_corner
) {
447 case OB_CORNER_TOPLEFT
:
452 case OB_CORNER_TOPRIGHT
:
453 n
= rows
* (cols
- 1);
457 case OB_CORNER_BOTTOMRIGHT
:
462 case OB_CORNER_BOTTOMLEFT
:
468 g_assert_not_reached();
472 g_assert_not_reached();
476 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
478 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
482 if (n
< self
->desks
) {
483 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
485 a
->surface
.parent
= self
->popup
->a_bg
;
486 a
->surface
.parentx
= x
+ px
;
487 a
->surface
.parenty
= y
+ py
;
488 XMoveResizeWindow(ob_display
, self
->wins
[n
],
489 x
+ px
, y
+ py
, eachw
, eachh
);
490 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
494 n
= rown
+= vert_inc
;
498 ObPagerPopup
*pager_popup_new(void)
502 self
= g_new(ObPagerPopup
, 1);
503 self
->popup
= popup_new();
506 self
->wins
= g_new(Window
, self
->desks
);
507 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
508 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
510 self
->popup
->hasicon
= TRUE
;
511 self
->popup
->draw_icon
= pager_popup_draw_icon
;
512 self
->popup
->draw_icon_data
= self
;
517 void pager_popup_free(ObPagerPopup
*self
)
522 for (i
= 0; i
< self
->desks
; ++i
)
523 XDestroyWindow(ob_display
, self
->wins
[i
]);
525 RrAppearanceFree(self
->hilight
);
526 RrAppearanceFree(self
->unhilight
);
527 popup_free(self
->popup
);
532 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
533 gchar
*text
, guint desk
)
537 if (screen_num_desktops
< self
->desks
)
538 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
539 XDestroyWindow(ob_display
, self
->wins
[i
]);
541 if (screen_num_desktops
!= self
->desks
)
542 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
544 if (screen_num_desktops
> self
->desks
)
545 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
546 XSetWindowAttributes attr
;
549 RrColorPixel(ob_rr_theme
->osd_border_color
);
550 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
551 0, 0, 1, 1, ob_rr_theme
->obwidth
,
552 RrDepth(ob_rr_inst
), InputOutput
,
553 RrVisual(ob_rr_inst
), CWBorderPixel
,
555 XMapWindow(ob_display
, self
->wins
[i
]);
558 self
->desks
= screen_num_desktops
;
559 self
->curdesk
= desk
;
561 popup_delay_show(self
->popup
, usec
, text
);
564 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
567 self
->popup
->iconwm
= MAX(1, wm
);
568 self
->popup
->iconhm
= MAX(1, hm
);