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
));
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
)
155 gint emptyx
, emptyy
; /* empty space between elements */
156 gint textx
, texty
, textw
, texth
;
157 gint iconx
, icony
, iconw
, iconh
;
160 /* when there is no icon and the text is not parent relative, then
161 fill the whole dialog with the text appearance, don't use the bg at all
163 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
164 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
168 /* set up the textures */
169 self
->a_text
->texture
[0].data
.text
.string
= text
;
171 /* measure the text out */
172 if (text
[0] != '\0') {
173 RrMinSize(self
->a_text
, &textw
, &texth
);
176 texth
= RrMinHeight(self
->a_text
);
179 /* get the height, which is also used for the icon width */
180 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
182 texth
= self
->h
- emptyy
;
183 h
= texth
* self
->iconhm
+ emptyy
;
188 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
190 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
192 iconw
= texth
* self
->iconwm
;
193 iconh
= texth
* self
->iconhm
;
194 textx
+= iconw
+ ob_rr_theme
->paddingx
;
196 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
200 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
201 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
203 /* when there is no icon, then fill the whole dialog with the text
214 w
= textw
+ emptyx
+ iconw
;
215 /* cap it at maxw/minw */
216 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
217 if (self
->minw
) w
= MAX(w
, self
->minw
);
218 textw
= w
- emptyx
- iconw
;
220 /* sanity checks to avoid crashes! */
223 if (texth
< 1) texth
= 1;
225 /* set up the x coord */
227 switch (self
->gravity
) {
228 case NorthGravity
: case CenterGravity
: case SouthGravity
:
231 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
236 /* set up the y coord */
238 switch (self
->gravity
) {
239 case WestGravity
: case CenterGravity
: case EastGravity
:
242 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
247 /* Find the monitor which contains the biggest part of the popup.
248 * If the popup is completely off screen, limit it to the intersection
249 * of all monitors and then try again. If it's still off screen, put it
251 RECT_SET(mon
, x
, y
, w
, h
);
252 m
= screen_find_monitor(&mon
);
253 area
= screen_physical_area_monitor(m
);
255 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
256 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
258 if (m
== screen_num_monitors
) {
259 RECT_SET(mon
, x
, y
, w
, h
);
260 m
= screen_find_monitor(&mon
);
261 if (m
== screen_num_monitors
)
263 area
= screen_physical_area_monitor(m
);
265 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
266 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
269 /* set the windows/appearances up */
270 XMoveResizeWindow(ob_display
, self
->bg
, x
, y
, w
, h
);
271 /* when there is no icon and the text is not parent relative, then
272 fill the whole dialog with the text appearance, don't use the bg at all
274 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
275 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
278 self
->a_text
->surface
.parent
= self
->a_bg
;
279 self
->a_text
->surface
.parentx
= textx
;
280 self
->a_text
->surface
.parenty
= texty
;
281 XMoveResizeWindow(ob_display
, self
->text
, textx
, texty
, textw
, texth
);
282 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
286 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
288 /* do the actual showing */
291 /* don't kill previous show timers */
292 if (!self
->delay_mapped
) {
293 ob_main_loop_timeout_add(ob_main_loop
, usec
,
294 popup_show_timeout
, self
,
295 g_direct_equal
, NULL
);
296 self
->delay_mapped
= TRUE
;
299 popup_show_timeout(self
);
306 void popup_hide(ObPopup
*self
)
311 /* kill enter events cause by this unmapping */
312 ignore_start
= event_start_ignore_all_enters();
314 XUnmapWindow(ob_display
, self
->bg
);
315 self
->mapped
= FALSE
;
317 event_end_ignore_all_enters(ignore_start
);
318 } else if (self
->delay_mapped
) {
319 ob_main_loop_timeout_remove(ob_main_loop
, popup_show_timeout
);
320 self
->delay_mapped
= FALSE
;
324 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
326 ObIconPopup
*self
= data
;
328 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
329 self
->a_icon
->surface
.parentx
= x
;
330 self
->a_icon
->surface
.parenty
= y
;
331 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
332 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
335 ObIconPopup
*icon_popup_new(void)
339 self
= g_new0(ObIconPopup
, 1);
340 self
->popup
= popup_new();
341 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
342 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
344 RrDepth(ob_rr_inst
), InputOutput
,
345 RrVisual(ob_rr_inst
), 0, NULL
);
346 XMapWindow(ob_display
, self
->icon
);
348 self
->popup
->hasicon
= TRUE
;
349 self
->popup
->draw_icon
= icon_popup_draw_icon
;
350 self
->popup
->draw_icon_data
= self
;
355 void icon_popup_free(ObIconPopup
*self
)
358 XDestroyWindow(ob_display
, self
->icon
);
359 RrAppearanceFree(self
->a_icon
);
360 popup_free(self
->popup
);
365 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
366 gchar
*text
, const ObClientIcon
*icon
)
369 self
->a_icon
->texture
[0].type
= RR_TEXTURE_RGBA
;
370 self
->a_icon
->texture
[0].data
.rgba
.width
= icon
->width
;
371 self
->a_icon
->texture
[0].data
.rgba
.height
= icon
->height
;
372 self
->a_icon
->texture
[0].data
.rgba
.alpha
= 0xff;
373 self
->a_icon
->texture
[0].data
.rgba
.data
= icon
->data
;
375 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
377 popup_delay_show(self
->popup
, usec
, text
);
380 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
383 self
->popup
->iconwm
= MAX(1, wm
);
384 self
->popup
->iconhm
= MAX(1, hm
);
387 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
390 ObPagerPopup
*self
= data
;
397 const guint cols
= screen_desktop_layout
.columns
;
398 const guint rows
= screen_desktop_layout
.rows
;
399 const gint linewidth
= ob_rr_theme
->obwidth
;
401 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
402 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
403 /* make them squares */
404 eachw
= eachh
= MIN(eachw
, eachh
);
407 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
408 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
410 if (eachw
<= 0 || eachh
<= 0)
413 switch (screen_desktop_layout
.orientation
) {
414 case OB_ORIENTATION_HORZ
:
415 switch (screen_desktop_layout
.start_corner
) {
416 case OB_CORNER_TOPLEFT
:
421 case OB_CORNER_TOPRIGHT
:
426 case OB_CORNER_BOTTOMRIGHT
:
429 vert_inc
= -screen_desktop_layout
.columns
;
431 case OB_CORNER_BOTTOMLEFT
:
432 n
= (rows
- 1) * cols
;
437 g_assert_not_reached();
440 case OB_ORIENTATION_VERT
:
441 switch (screen_desktop_layout
.start_corner
) {
442 case OB_CORNER_TOPLEFT
:
447 case OB_CORNER_TOPRIGHT
:
448 n
= rows
* (cols
- 1);
452 case OB_CORNER_BOTTOMRIGHT
:
457 case OB_CORNER_BOTTOMLEFT
:
463 g_assert_not_reached();
467 g_assert_not_reached();
471 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
473 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
477 if (n
< self
->desks
) {
478 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
480 a
->surface
.parent
= self
->popup
->a_bg
;
481 a
->surface
.parentx
= x
+ px
;
482 a
->surface
.parenty
= y
+ py
;
483 XMoveResizeWindow(ob_display
, self
->wins
[n
],
484 x
+ px
, y
+ py
, eachw
, eachh
);
485 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
489 n
= rown
+= vert_inc
;
493 ObPagerPopup
*pager_popup_new(void)
497 self
= g_new(ObPagerPopup
, 1);
498 self
->popup
= popup_new();
501 self
->wins
= g_new(Window
, self
->desks
);
502 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
503 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
505 self
->popup
->hasicon
= TRUE
;
506 self
->popup
->draw_icon
= pager_popup_draw_icon
;
507 self
->popup
->draw_icon_data
= self
;
512 void pager_popup_free(ObPagerPopup
*self
)
517 for (i
= 0; i
< self
->desks
; ++i
)
518 XDestroyWindow(ob_display
, self
->wins
[i
]);
520 RrAppearanceFree(self
->hilight
);
521 RrAppearanceFree(self
->unhilight
);
522 popup_free(self
->popup
);
527 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
528 gchar
*text
, guint desk
)
532 if (screen_num_desktops
< self
->desks
)
533 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
534 XDestroyWindow(ob_display
, self
->wins
[i
]);
536 if (screen_num_desktops
!= self
->desks
)
537 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
539 if (screen_num_desktops
> self
->desks
)
540 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
541 XSetWindowAttributes attr
;
544 RrColorPixel(ob_rr_theme
->osd_border_color
);
545 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
546 0, 0, 1, 1, ob_rr_theme
->obwidth
,
547 RrDepth(ob_rr_inst
), InputOutput
,
548 RrVisual(ob_rr_inst
), CWBorderPixel
,
550 XMapWindow(ob_display
, self
->wins
[i
]);
553 self
->desks
= screen_num_desktops
;
554 self
->curdesk
= desk
;
556 popup_delay_show(self
->popup
, usec
, text
);
559 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
562 self
->popup
->iconwm
= MAX(1, wm
);
563 self
->popup
->iconhm
= MAX(1, hm
);