1 /**************************************************************************
3 * Copyright (C) 2009 Andreas.Fink (Andreas.Fink85@gmail.com)
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 **************************************************************************/
23 #include <cairo-xlib.h>
30 static int x
, y
, width
, height
;
32 // the next functions are helper functions for tooltip handling
33 void start_show_timeout();
34 void start_hide_timeout();
35 void stop_tooltip_timeout();
40 void default_tooltip()
42 // give the tooltip some reasonable default values
43 memset(&g_tooltip
, 0, sizeof(Tooltip
));
45 g_tooltip
.font_color
.color
[0] = 1;
46 g_tooltip
.font_color
.color
[1] = 1;
47 g_tooltip
.font_color
.color
[2] = 1;
48 g_tooltip
.font_color
.alpha
= 1;
51 void cleanup_tooltip()
53 stop_tooltip_timeout();
56 if (g_tooltip
.window
) XDestroyWindow(server
.dsp
, g_tooltip
.window
);
57 if (g_tooltip
.font_desc
) pango_font_description_free(g_tooltip
.font_desc
);
63 if (!g_tooltip
.font_desc
)
64 g_tooltip
.font_desc
= pango_font_description_from_string("sans 10");
65 if (g_tooltip
.bg
== 0)
66 g_tooltip
.bg
= &g_array_index(backgrounds
, Background
, 0);
68 XSetWindowAttributes attr
;
69 attr
.override_redirect
= True
;
70 attr
.event_mask
= StructureNotifyMask
;
71 attr
.colormap
= server
.colormap
;
72 attr
.background_pixel
= 0;
73 attr
.border_pixel
= 0;
74 unsigned long mask
= CWEventMask
|CWColormap
|CWBorderPixel
|CWBackPixel
|CWOverrideRedirect
;
75 if (g_tooltip
.window
) XDestroyWindow(server
.dsp
, g_tooltip
.window
);
76 g_tooltip
.window
= XCreateWindow(server
.dsp
, server
.root_win
, 0, 0, 100, 20, 0, server
.depth
, InputOutput
, server
.visual
, mask
, &attr
);
80 void tooltip_trigger_show(Area
* area
, Panel
* p
, XEvent
*e
)
82 // Position the tooltip in the center of the area
83 x
= area
->posx
+ area
->width
/ 2 + e
->xmotion
.x_root
- e
->xmotion
.x
;
84 y
= area
->posy
+ area
->height
/ 2 + e
->xmotion
.y_root
- e
->xmotion
.y
;
85 if (!panel_horizontal
)
88 if (g_tooltip
.mapped
&& g_tooltip
.area
!= area
) {
89 tooltip_copy_text(area
);
91 stop_tooltip_timeout();
93 else if (!g_tooltip
.mapped
) {
99 void tooltip_show(void* arg
)
103 XTranslateCoordinates( server
.dsp
, server
.root_win
, g_tooltip
.panel
->main_win
, x
, y
, &mx
, &my
, &w
);
105 if (!panel_horizontal
)
106 my
+= height
/2; /* we adjusted y in tooltip_trigger_show, revert or we won't find the correct area anymore */
107 area
= click_area(g_tooltip
.panel
, mx
, my
);
108 stop_tooltip_timeout();
109 if (!g_tooltip
.mapped
&& area
->_get_tooltip_text
) {
110 tooltip_copy_text(area
);
111 g_tooltip
.mapped
= True
;
112 XMapWindow(server
.dsp
, g_tooltip
.window
);
119 void tooltip_update_geometry()
124 cs
= cairo_xlib_surface_create(server
.dsp
, g_tooltip
.window
, server
.visual
, width
, height
);
125 c
= cairo_create(cs
);
126 layout
= pango_cairo_create_layout(c
);
127 pango_layout_set_font_description(layout
, g_tooltip
.font_desc
);
128 pango_layout_set_text(layout
, g_tooltip
.tooltip_text
, -1);
129 PangoRectangle r1
, r2
;
130 pango_layout_get_pixel_extents(layout
, &r1
, &r2
);
131 width
= 2*g_tooltip
.bg
->border
.width
+ 2*g_tooltip
.paddingx
+ r2
.width
;
132 height
= 2*g_tooltip
.bg
->border
.width
+ 2*g_tooltip
.paddingy
+ r2
.height
;
134 Panel
* panel
= g_tooltip
.panel
;
135 if (panel_horizontal
&& panel_position
& BOTTOM
)
136 y
= panel
->posy
-height
;
137 else if (panel_horizontal
&& panel_position
& TOP
)
138 y
= panel
->posy
+ panel
->area
.height
;
139 else if (panel_position
& LEFT
)
140 x
= panel
->posx
+ panel
->area
.width
;
142 x
= panel
->posx
- width
;
144 g_object_unref(layout
);
146 cairo_surface_destroy(cs
);
150 void tooltip_adjust_geometry()
152 // adjust coordinates and size to not go offscreen
153 // it seems quite impossible that the height needs to be adjusted, but we do it anyway.
155 int min_x
, min_y
, max_width
, max_height
;
156 Panel
* panel
= g_tooltip
.panel
;
157 int screen_width
= server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
;
158 int screen_height
= server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
;
159 if ( x
+width
<= screen_width
&& y
+height
<= screen_height
&& x
>=server
.monitor
[panel
->monitor
].x
&& y
>=server
.monitor
[panel
->monitor
].y
)
160 return; // no adjustment needed
162 if (panel_horizontal
) {
164 max_width
=server
.monitor
[panel
->monitor
].width
;
165 max_height
=server
.monitor
[panel
->monitor
].height
-panel
->area
.height
;
166 if (panel_position
& BOTTOM
)
169 min_y
=panel
->area
.height
;
172 max_width
=server
.monitor
[panel
->monitor
].width
-panel
->area
.width
;
174 max_height
=server
.monitor
[panel
->monitor
].height
;
175 if (panel_position
& LEFT
)
176 min_x
=panel
->area
.width
;
181 if (x
+width
> server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
)
182 x
= server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
- width
;
183 if ( y
+height
> server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
)
184 y
= server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
- height
;
192 if (height
>max_height
)
196 void tooltip_update()
198 if (!g_tooltip
.tooltip_text
) {
203 tooltip_update_geometry();
204 tooltip_adjust_geometry();
205 XMoveResizeWindow(server
.dsp
, g_tooltip
.window
, x
, y
, width
, height
);
207 // Stuff for drawing the tooltip
211 cs
= cairo_xlib_surface_create(server
.dsp
, g_tooltip
.window
, server
.visual
, width
, height
);
212 c
= cairo_create(cs
);
213 Color bc
= g_tooltip
.bg
->back
;
214 Border b
= g_tooltip
.bg
->border
;
215 if (server
.real_transparency
) {
216 clear_pixmap(g_tooltip
.window
, 0, 0, width
, height
);
217 draw_rect(c
, b
.width
, b
.width
, width
-2*b
.width
, height
-2*b
.width
, b
.rounded
-b
.width
/1.571);
218 cairo_set_source_rgba(c
, bc
.color
[0], bc
.color
[1], bc
.color
[2], bc
.alpha
);
221 cairo_rectangle(c
, 0., 0, width
, height
);
222 cairo_set_source_rgb(c
, bc
.color
[0], bc
.color
[1], bc
.color
[2]);
225 cairo_set_line_width(c
, b
.width
);
226 if (server
.real_transparency
)
227 draw_rect(c
, b
.width
/2.0, b
.width
/2.0, width
- b
.width
, height
- b
.width
, b
.rounded
);
229 cairo_rectangle(c
, b
.width
/2.0, b
.width
/2.0, width
-b
.width
, height
-b
.width
);
230 cairo_set_source_rgba(c
, b
.color
[0], b
.color
[1], b
.color
[2], b
.alpha
);
233 Color fc
= g_tooltip
.font_color
;
234 cairo_set_source_rgba(c
, fc
.color
[0], fc
.color
[1], fc
.color
[2], fc
.alpha
);
235 layout
= pango_cairo_create_layout(c
);
236 pango_layout_set_font_description(layout
, g_tooltip
.font_desc
);
237 pango_layout_set_text(layout
, g_tooltip
.tooltip_text
, -1);
238 PangoRectangle r1
, r2
;
239 pango_layout_get_pixel_extents(layout
, &r1
, &r2
);
240 pango_layout_set_width(layout
, width
*PANGO_SCALE
);
241 pango_layout_set_height(layout
, height
*PANGO_SCALE
);
242 pango_layout_set_ellipsize(layout
, PANGO_ELLIPSIZE_END
);
243 // I do not know why this is the right way, but with the below cairo_move_to it seems to be centered (horiz. and vert.)
244 cairo_move_to(c
, -r1
.x
/2+g_tooltip
.bg
->border
.width
+g_tooltip
.paddingx
, -r1
.y
/2+g_tooltip
.bg
->border
.width
+g_tooltip
.paddingy
);
245 pango_cairo_show_layout (c
, layout
);
247 g_object_unref (layout
);
249 cairo_surface_destroy (cs
);
253 void tooltip_trigger_hide(Tooltip
* tooltip
)
255 if (g_tooltip
.mapped
) {
256 tooltip_copy_text(0);
257 start_hide_timeout();
260 // tooltip not visible yet, but maybe a timeout is still pending
261 stop_tooltip_timeout();
266 void tooltip_hide(void* arg
)
268 stop_tooltip_timeout();
269 if (g_tooltip
.mapped
) {
270 g_tooltip
.mapped
= False
;
271 XUnmapWindow(server
.dsp
, g_tooltip
.window
);
277 void start_show_timeout()
279 if (g_tooltip
.timeout
)
280 change_timeout(g_tooltip
.timeout
, g_tooltip
.show_timeout_msec
, 0, tooltip_show
, 0);
282 g_tooltip
.timeout
= add_timeout(g_tooltip
.show_timeout_msec
, 0, tooltip_show
, 0);
286 void start_hide_timeout()
288 if (g_tooltip
.timeout
)
289 change_timeout(g_tooltip
.timeout
, g_tooltip
.hide_timeout_msec
, 0, tooltip_hide
, 0);
291 g_tooltip
.timeout
= add_timeout(g_tooltip
.hide_timeout_msec
, 0, tooltip_hide
, 0);
295 void stop_tooltip_timeout()
297 if (g_tooltip
.timeout
) {
298 stop_timeout(g_tooltip
.timeout
);
299 g_tooltip
.timeout
= 0;
304 void tooltip_copy_text(Area
* area
)
306 free(g_tooltip
.tooltip_text
);
307 if (area
&& area
->_get_tooltip_text
)
308 g_tooltip
.tooltip_text
= strdup(area
->_get_tooltip_text(area
));
310 g_tooltip
.tooltip_text
= 0;
311 g_tooltip
.area
= area
;