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 popup_hide(self
); /* make sure it's not showing or is being delayed and
70 XDestroyWindow(ob_display
, self
->bg
);
71 XDestroyWindow(ob_display
, self
->text
);
72 RrAppearanceFree(self
->a_bg
);
73 RrAppearanceFree(self
->a_text
);
74 g_hash_table_remove(window_map
, &self
->bg
);
75 stacking_remove(self
);
80 void popup_position(ObPopup
*self
, gint gravity
, gint x
, gint y
)
82 self
->gravity
= gravity
;
87 void popup_text_width(ObPopup
*self
, gint w
)
92 void popup_min_width(ObPopup
*self
, gint minw
)
97 void popup_max_width(ObPopup
*self
, gint maxw
)
102 void popup_height(ObPopup
*self
, gint h
)
106 /* don't let the height be smaller than the text */
107 texth
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
108 self
->h
= MAX(h
, texth
);
111 void popup_text_width_to_string(ObPopup
*self
, gchar
*text
)
113 if (text
[0] != '\0') {
114 self
->a_text
->texture
[0].data
.text
.string
= text
;
115 self
->textw
= RrMinWidth(self
->a_text
);
120 void popup_height_to_string(ObPopup
*self
, gchar
*text
)
122 self
->h
= RrMinHeight(self
->a_text
) + ob_rr_theme
->paddingy
* 2;
125 void popup_text_width_to_strings(ObPopup
*self
, gchar
**strings
, gint num
)
130 for (i
= 0; i
< num
; ++i
) {
131 popup_text_width_to_string(self
, strings
[i
]);
132 maxw
= MAX(maxw
, self
->textw
);
137 void popup_set_text_align(ObPopup
*self
, RrJustify align
)
139 self
->a_text
->texture
[0].data
.text
.justify
= align
;
142 static gboolean
popup_show_timeout(gpointer data
)
144 ObPopup
*self
= data
;
146 XMapWindow(ob_display
, self
->bg
);
147 stacking_raise(INTERNAL_AS_WINDOW(self
));
149 self
->delay_mapped
= FALSE
;
151 return FALSE
; /* don't repeat */
154 void popup_delay_show(ObPopup
*self
, gulong usec
, gchar
*text
)
159 gint emptyx
, emptyy
; /* empty space between elements */
160 gint textx
, texty
, textw
, texth
;
161 gint iconx
, icony
, iconw
, iconh
;
164 /* when there is no icon and the text is not parent relative, then
165 fill the whole dialog with the text appearance, don't use the bg at all
167 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
168 RrMargins(self
->a_bg
, &l
, &t
, &r
, &b
);
172 /* set up the textures */
173 self
->a_text
->texture
[0].data
.text
.string
= text
;
175 /* measure the text out */
176 if (text
[0] != '\0') {
177 RrMinSize(self
->a_text
, &textw
, &texth
);
180 texth
= RrMinHeight(self
->a_text
);
183 /* get the height, which is also used for the icon width */
184 emptyy
= t
+ b
+ ob_rr_theme
->paddingy
* 2;
186 texth
= self
->h
- emptyy
;
187 h
= texth
* self
->iconhm
+ emptyy
;
192 iconx
= textx
= l
+ ob_rr_theme
->paddingx
;
194 emptyx
= l
+ r
+ ob_rr_theme
->paddingx
* 2;
196 iconw
= texth
* self
->iconwm
;
197 iconh
= texth
* self
->iconhm
;
198 textx
+= iconw
+ ob_rr_theme
->paddingx
;
200 emptyx
+= ob_rr_theme
->paddingx
; /* between the icon and text */
204 texty
= (h
- texth
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
205 icony
= (h
- iconh
- emptyy
) / 2 + t
+ ob_rr_theme
->paddingy
;
207 /* when there is no icon, then fill the whole dialog with the text
218 w
= textw
+ emptyx
+ iconw
;
219 /* cap it at maxw/minw */
220 if (self
->maxw
) w
= MIN(w
, self
->maxw
);
221 if (self
->minw
) w
= MAX(w
, self
->minw
);
222 textw
= w
- emptyx
- iconw
;
224 /* sanity checks to avoid crashes! */
227 if (texth
< 1) texth
= 1;
229 /* set up the x coord */
231 switch (self
->gravity
) {
232 case NorthGravity
: case CenterGravity
: case SouthGravity
:
235 case NorthEastGravity
: case EastGravity
: case SouthEastGravity
:
240 /* set up the y coord */
242 switch (self
->gravity
) {
243 case WestGravity
: case CenterGravity
: case EastGravity
:
246 case SouthWestGravity
: case SouthGravity
: case SouthEastGravity
:
251 /* Find the monitor which contains the biggest part of the popup.
252 * If the popup is completely off screen, limit it to the intersection
253 * of all monitors and then try again. If it's still off screen, put it
255 RECT_SET(mon
, x
, y
, w
, h
);
256 m
= screen_find_monitor(&mon
);
257 area
= screen_physical_area_monitor(m
);
259 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
260 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
264 if (m
== screen_num_monitors
) {
265 RECT_SET(mon
, x
, y
, w
, h
);
266 m
= screen_find_monitor(&mon
);
267 if (m
== screen_num_monitors
)
269 area
= screen_physical_area_monitor(m
);
271 x
=MAX(MIN(x
, area
->x
+area
->width
-w
),area
->x
);
272 y
=MAX(MIN(y
, area
->y
+area
->height
-h
),area
->y
);
277 /* set the windows/appearances up */
278 XMoveResizeWindow(ob_display
, self
->bg
, x
, y
, w
, h
);
279 /* when there is no icon and the text is not parent relative, then
280 fill the whole dialog with the text appearance, don't use the bg at all
282 if (self
->hasicon
|| self
->a_text
->surface
.grad
== RR_SURFACE_PARENTREL
)
283 RrPaint(self
->a_bg
, self
->bg
, w
, h
);
286 self
->a_text
->surface
.parent
= self
->a_bg
;
287 self
->a_text
->surface
.parentx
= textx
;
288 self
->a_text
->surface
.parenty
= texty
;
289 XMoveResizeWindow(ob_display
, self
->text
, textx
, texty
, textw
, texth
);
290 RrPaint(self
->a_text
, self
->text
, textw
, texth
);
294 self
->draw_icon(iconx
, icony
, iconw
, iconh
, self
->draw_icon_data
);
296 /* do the actual showing */
299 /* don't kill previous show timers */
300 if (!self
->delay_mapped
) {
301 ob_main_loop_timeout_add(ob_main_loop
, usec
,
302 popup_show_timeout
, self
,
303 g_direct_equal
, NULL
);
304 self
->delay_mapped
= TRUE
;
307 popup_show_timeout(self
);
312 void popup_hide(ObPopup
*self
)
317 /* kill enter events cause by this unmapping */
318 ignore_start
= event_start_ignore_all_enters();
320 XUnmapWindow(ob_display
, self
->bg
);
321 self
->mapped
= FALSE
;
323 event_end_ignore_all_enters(ignore_start
);
324 } else if (self
->delay_mapped
) {
325 ob_main_loop_timeout_remove_data(ob_main_loop
, popup_show_timeout
, self
, FALSE
);
326 self
->delay_mapped
= FALSE
;
330 static void icon_popup_draw_icon(gint x
, gint y
, gint w
, gint h
, gpointer data
)
332 ObIconPopup
*self
= data
;
334 self
->a_icon
->surface
.parent
= self
->popup
->a_bg
;
335 self
->a_icon
->surface
.parentx
= x
;
336 self
->a_icon
->surface
.parenty
= y
;
337 XMoveResizeWindow(ob_display
, self
->icon
, x
, y
, w
, h
);
338 RrPaint(self
->a_icon
, self
->icon
, w
, h
);
341 ObIconPopup
*icon_popup_new(void)
345 self
= g_new0(ObIconPopup
, 1);
346 self
->popup
= popup_new();
347 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_clear_tex
);
348 self
->icon
= XCreateWindow(ob_display
, self
->popup
->bg
,
350 RrDepth(ob_rr_inst
), InputOutput
,
351 RrVisual(ob_rr_inst
), 0, NULL
);
352 XMapWindow(ob_display
, self
->icon
);
354 self
->popup
->hasicon
= TRUE
;
355 self
->popup
->draw_icon
= icon_popup_draw_icon
;
356 self
->popup
->draw_icon_data
= self
;
361 void icon_popup_free(ObIconPopup
*self
)
364 XDestroyWindow(ob_display
, self
->icon
);
365 RrAppearanceFree(self
->a_icon
);
366 popup_free(self
->popup
);
371 void icon_popup_delay_show(ObIconPopup
*self
, gulong usec
,
372 gchar
*text
, RrImage
*icon
)
375 RrAppearanceClearTextures(self
->a_icon
);
376 self
->a_icon
->texture
[0].type
= RR_TEXTURE_IMAGE
;
377 self
->a_icon
->texture
[0].data
.image
.alpha
= 0xff;
378 self
->a_icon
->texture
[0].data
.image
.image
= icon
;
380 RrAppearanceClearTextures(self
->a_icon
);
381 self
->a_icon
->texture
[0].type
= RR_TEXTURE_NONE
;
384 popup_delay_show(self
->popup
, usec
, text
);
387 void icon_popup_icon_size_multiplier(ObIconPopup
*self
, guint wm
, guint hm
)
390 self
->popup
->iconwm
= MAX(1, wm
);
391 self
->popup
->iconhm
= MAX(1, hm
);
394 static void pager_popup_draw_icon(gint px
, gint py
, gint w
, gint h
,
397 ObPagerPopup
*self
= data
;
404 const guint cols
= screen_desktop_layout
.columns
;
405 const guint rows
= screen_desktop_layout
.rows
;
406 const gint linewidth
= ob_rr_theme
->obwidth
;
408 eachw
= (w
- ((cols
+ 1) * linewidth
)) / cols
;
409 eachh
= (h
- ((rows
+ 1) * linewidth
)) / rows
;
410 /* make them squares */
411 eachw
= eachh
= MIN(eachw
, eachh
);
414 px
+= (w
- (cols
* (eachw
+ linewidth
) + linewidth
)) / 2;
415 py
+= (h
- (rows
* (eachh
+ linewidth
) + linewidth
)) / 2;
417 if (eachw
<= 0 || eachh
<= 0)
420 switch (screen_desktop_layout
.orientation
) {
421 case OB_ORIENTATION_HORZ
:
422 switch (screen_desktop_layout
.start_corner
) {
423 case OB_CORNER_TOPLEFT
:
428 case OB_CORNER_TOPRIGHT
:
433 case OB_CORNER_BOTTOMRIGHT
:
436 vert_inc
= -screen_desktop_layout
.columns
;
438 case OB_CORNER_BOTTOMLEFT
:
439 n
= (rows
- 1) * cols
;
444 g_assert_not_reached();
447 case OB_ORIENTATION_VERT
:
448 switch (screen_desktop_layout
.start_corner
) {
449 case OB_CORNER_TOPLEFT
:
454 case OB_CORNER_TOPRIGHT
:
455 n
= rows
* (cols
- 1);
459 case OB_CORNER_BOTTOMRIGHT
:
464 case OB_CORNER_BOTTOMLEFT
:
470 g_assert_not_reached();
474 g_assert_not_reached();
478 for (r
= 0, y
= 0; r
< rows
; ++r
, y
+= eachh
+ linewidth
)
480 for (c
= 0, x
= 0; c
< cols
; ++c
, x
+= eachw
+ linewidth
)
484 if (n
< self
->desks
) {
485 a
= (n
== self
->curdesk
? self
->hilight
: self
->unhilight
);
487 a
->surface
.parent
= self
->popup
->a_bg
;
488 a
->surface
.parentx
= x
+ px
;
489 a
->surface
.parenty
= y
+ py
;
490 XMoveResizeWindow(ob_display
, self
->wins
[n
],
491 x
+ px
, y
+ py
, eachw
, eachh
);
492 RrPaint(a
, self
->wins
[n
], eachw
, eachh
);
496 n
= rown
+= vert_inc
;
500 ObPagerPopup
*pager_popup_new(void)
504 self
= g_new(ObPagerPopup
, 1);
505 self
->popup
= popup_new();
508 self
->wins
= g_new(Window
, self
->desks
);
509 self
->hilight
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_fg
);
510 self
->unhilight
= RrAppearanceCopy(ob_rr_theme
->osd_unhilite_fg
);
512 self
->popup
->hasicon
= TRUE
;
513 self
->popup
->draw_icon
= pager_popup_draw_icon
;
514 self
->popup
->draw_icon_data
= self
;
519 void pager_popup_free(ObPagerPopup
*self
)
524 for (i
= 0; i
< self
->desks
; ++i
)
525 XDestroyWindow(ob_display
, self
->wins
[i
]);
527 RrAppearanceFree(self
->hilight
);
528 RrAppearanceFree(self
->unhilight
);
529 popup_free(self
->popup
);
534 void pager_popup_delay_show(ObPagerPopup
*self
, gulong usec
,
535 gchar
*text
, guint desk
)
539 if (screen_num_desktops
< self
->desks
)
540 for (i
= screen_num_desktops
; i
< self
->desks
; ++i
)
541 XDestroyWindow(ob_display
, self
->wins
[i
]);
543 if (screen_num_desktops
!= self
->desks
)
544 self
->wins
= g_renew(Window
, self
->wins
, screen_num_desktops
);
546 if (screen_num_desktops
> self
->desks
)
547 for (i
= self
->desks
; i
< screen_num_desktops
; ++i
) {
548 XSetWindowAttributes attr
;
551 RrColorPixel(ob_rr_theme
->osd_border_color
);
552 self
->wins
[i
] = XCreateWindow(ob_display
, self
->popup
->bg
,
553 0, 0, 1, 1, ob_rr_theme
->obwidth
,
554 RrDepth(ob_rr_inst
), InputOutput
,
555 RrVisual(ob_rr_inst
), CWBorderPixel
,
557 XMapWindow(ob_display
, self
->wins
[i
]);
560 self
->desks
= screen_num_desktops
;
561 self
->curdesk
= desk
;
563 popup_delay_show(self
->popup
, usec
, text
);
566 void pager_popup_icon_size_multiplier(ObPagerPopup
*self
, guint wm
, guint hm
)
569 self
->popup
->iconwm
= MAX(1, wm
);
570 self
->popup
->iconhm
= MAX(1, hm
);