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 **************************************************************************/
21 #include <cairo-xlib.h>
28 static int x
, y
, width
, height
;
30 // the next functions are helper functions for tooltip handling
31 void start_show_timeout();
32 void start_hide_timeout();
35 // give the tooltip some reasonable default values
39 .show_timeout
= { 0, 0 },
40 .hide_timeout
= { 0, 0 },
45 .font_color
= { .color
={1, 1, 1}, .alpha
=1 },
46 .background_color
= { .color
={0.5, 0.4, 0.5}, .alpha
=1 },
47 .border
= { .color
={0, 0, 0}, .alpha
=1, .width
=1, .rounded
=0 },
55 if (!g_tooltip
.font_desc
)
56 g_tooltip
.font_desc
= pango_font_description_from_string("sans 10");
58 if (g_tooltip
.show_timer_id
== 0)
59 g_tooltip
.show_timer_id
= install_timer(0, 0, 0, 0, tooltip_show
);
60 if (g_tooltip
.hide_timer_id
== 0)
61 g_tooltip
.hide_timer_id
= install_timer(0, 0, 0, 0, tooltip_hide
);
63 XSetWindowAttributes attr
;
64 attr
.override_redirect
= True
;
65 attr
.event_mask
= StructureNotifyMask
;
66 if (g_tooltip
.window
) XDestroyWindow(server
.dsp
, g_tooltip
.window
);
67 g_tooltip
.window
= XCreateWindow(server
.dsp
, server
.root_win
, 0, 0, 100, 20, 0, server
.depth
, InputOutput
, CopyFromParent
, CWOverrideRedirect
|CWEventMask
, &attr
);
71 void cleanup_tooltip()
75 g_tooltip
.enabled
= False
;
79 if (g_tooltip
.window
) {
80 XDestroyWindow(server
.dsp
, g_tooltip
.window
);
83 if (g_tooltip
.font_desc
) {
84 pango_font_description_free(g_tooltip
.font_desc
);
85 g_tooltip
.font_desc
= 0;
90 void tooltip_trigger_show(Task
* task
, int x_root
, int y_root
)
95 if (g_tooltip
.mapped
&& g_tooltip
.task
!= task
) {
96 g_tooltip
.task
= task
;
100 else if (!g_tooltip
.mapped
) {
101 g_tooltip
.task
= task
;
102 start_show_timeout();
110 if (!g_tooltip
.mapped
) {
111 g_tooltip
.mapped
= True
;
112 XMapWindow(server
.dsp
, g_tooltip
.window
);
118 void tooltip_update_geometry()
123 cs
= cairo_xlib_surface_create(server
.dsp
, g_tooltip
.window
, server
.visual
, width
, height
);
124 c
= cairo_create(cs
);
125 layout
= pango_cairo_create_layout(c
);
126 pango_layout_set_font_description(layout
, g_tooltip
.font_desc
);
127 pango_layout_set_text(layout
, g_tooltip
.task
->title
, -1);
128 PangoRectangle r1
, r2
;
129 pango_layout_get_pixel_extents(layout
, &r1
, &r2
);
130 width
= 2*g_tooltip
.border
.width
+ 2*g_tooltip
.paddingx
+ r2
.width
;
131 height
= 2*g_tooltip
.border
.width
+ 2*g_tooltip
.paddingy
+ r2
.height
;
133 Panel
* panel
= g_tooltip
.task
->area
.panel
;
134 if (panel_horizontal
&& panel_position
& BOTTOM
)
135 y
= panel
->posy
-height
;
136 else if (panel_horizontal
&& panel_position
& TOP
)
137 y
= panel
->posy
+ panel
->area
.height
;
138 else if (panel_position
& LEFT
)
139 x
= panel
->posx
+ panel
->area
.width
;
141 x
= panel
->posx
- width
;
143 g_object_unref(layout
);
145 cairo_surface_destroy(cs
);
149 void tooltip_adjust_geometry()
151 // adjust coordinates and size to not go offscreen
152 // it seems quite impossible that the height needs to be adjusted, but we do it anyway.
154 int min_x
, min_y
, max_width
, max_height
;
155 Panel
* panel
= g_tooltip
.task
->area
.panel
;
156 int screen_width
= server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
;
157 int screen_height
= server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
;
158 if ( x
+width
<= screen_width
&& y
+height
<= screen_height
&& x
>=0 && y
>=0)
159 return; // no adjustment needed
161 if (panel_horizontal
) {
163 max_width
=screen_width
;
164 max_height
=screen_height
-panel
->area
.height
;
165 if (panel_position
& BOTTOM
)
168 min_y
=panel
->area
.height
;
171 max_width
=screen_width
-panel
->area
.width
;
173 max_height
=screen_height
;
174 if (panel_position
& LEFT
)
175 min_x
=panel
->area
.width
;
180 if (x
+width
> server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
)
181 x
= server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
- width
;
182 if ( y
+height
> server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
)
183 y
= server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
- height
;
191 if (height
>max_height
)
195 void tooltip_update()
197 if (!g_tooltip
.task
) {
202 // printf("tooltip_update\n");
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
.background_color
;
214 cairo_rectangle(c
, 0, 0, width
, height
);
215 cairo_set_source_rgb(c
, bc
.color
[0], bc
.color
[1], bc
.color
[2]);
217 Border b
= g_tooltip
.border
;
218 cairo_set_source_rgba(c
, b
.color
[0], b
.color
[1], b
.color
[2], b
.alpha
);
219 cairo_set_line_width(c
, b
.width
);
220 cairo_rectangle(c
, b
.width
/2.0, b
.width
/2.0, width
-b
.width
, height
-b
.width
);
223 config_color fc
= g_tooltip
.font_color
;
224 cairo_set_source_rgba(c
, fc
.color
[0], fc
.color
[1], fc
.color
[2], fc
.alpha
);
225 layout
= pango_cairo_create_layout(c
);
226 pango_layout_set_font_description(layout
, g_tooltip
.font_desc
);
227 pango_layout_set_text(layout
, g_tooltip
.task
->title
, -1);
228 PangoRectangle r1
, r2
;
229 pango_layout_get_pixel_extents(layout
, &r1
, &r2
);
230 pango_layout_set_width(layout
, width
*PANGO_SCALE
);
231 pango_layout_set_height(layout
, height
*PANGO_SCALE
);
232 pango_layout_set_ellipsize(layout
, PANGO_ELLIPSIZE_END
);
233 // 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.)
234 cairo_move_to(c
, -r1
.x
/2+g_tooltip
.border
.width
+g_tooltip
.paddingx
, -r1
.y
/2+g_tooltip
.border
.width
+g_tooltip
.paddingy
);
235 pango_cairo_show_layout (c
, layout
);
237 g_object_unref (layout
);
239 cairo_surface_destroy (cs
);
243 void tooltip_trigger_hide(Tooltip
* tooltip
)
245 if (g_tooltip
.mapped
) {
247 start_hide_timeout();
250 // tooltip not visible yet, but maybe a timeout is still pending
259 if (g_tooltip
.mapped
) {
260 g_tooltip
.mapped
= False
;
261 XUnmapWindow(server
.dsp
, g_tooltip
.window
);
267 void start_show_timeout()
269 reset_timer(g_tooltip
.hide_timer_id
, 0, 0, 0, 0);
270 struct timespec t
= g_tooltip
.show_timeout
;
271 if (t
.tv_sec
== 0 && t
.tv_nsec
== 0)
274 reset_timer(g_tooltip
.show_timer_id
, t
.tv_sec
, t
.tv_nsec
, 0, 0);
278 void start_hide_timeout()
280 reset_timer(g_tooltip
.show_timer_id
, 0, 0, 0, 0);
281 struct timespec t
= g_tooltip
.hide_timeout
;
282 if (t
.tv_sec
== 0 && t
.tv_nsec
== 0)
285 reset_timer(g_tooltip
.hide_timer_id
, t
.tv_sec
, t
.tv_nsec
, 0, 0);
290 reset_timer(g_tooltip
.show_timer_id
, 0, 0, 0, 0);
291 reset_timer(g_tooltip
.hide_timer_id
, 0, 0, 0, 0);