1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail : see the 'copyright' file.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <string.h>
21 #include <math.h>
22 #include <cairo-dock.h>
23
24 #include "applet-struct.h"
25 #include "applet-decorator-tooltip.h"
26
27 #define _CAIRO_DIALOG_TOOLTIP_ARROW_WIDTH 28
28 #define _CAIRO_DIALOG_TOOLTIP_MARGIN 4
29 #define CD_ARROW_HEIGHT 8 /// maybe decrease a little if the width or the height is too small ?...
30 #define CD_ALIGN 0.5
31 #define CD_RADIUS (myDialogsParam.bUseDefaultColors ? myStyleParam.iCornerRadius : myDialogsParam.iCornerRadius)
32
33 /*
34 ic______^___ arrow height + margin
35 ic msg
36 |
37 | widget
38 |
39 |____________ bottom margin
40
41 */
42
cd_decorator_set_frame_size_tooltip(CairoDialog * pDialog)43 void cd_decorator_set_frame_size_tooltip (CairoDialog *pDialog)
44 {
45 int iMargin = .5 * myDialogsParam.iLineWidth + (1. - sqrt (2) / 2) * CD_RADIUS;
46 int iIconOffset = pDialog->iIconSize / 2;
47 pDialog->iRightMargin = iMargin + _CAIRO_DIALOG_TOOLTIP_MARGIN;
48 pDialog->iLeftMargin = iIconOffset + iMargin + _CAIRO_DIALOG_TOOLTIP_MARGIN;
49 pDialog->iTopMargin = iIconOffset + _CAIRO_DIALOG_TOOLTIP_MARGIN + myDialogsParam.iLineWidth;
50 pDialog->iBottomMargin = _CAIRO_DIALOG_TOOLTIP_MARGIN;
51 pDialog->iMinBottomGap = CD_ARROW_HEIGHT;
52 pDialog->iMinFrameWidth = _CAIRO_DIALOG_TOOLTIP_ARROW_WIDTH;
53 pDialog->fAlign = .5;
54 pDialog->container.fRatio = 0.;
55 pDialog->container.bUseReflect = FALSE;
56 pDialog->iIconOffsetX = iIconOffset;
57 pDialog->iIconOffsetY = pDialog->iTopMargin;
58 }
59
60
cd_decorator_draw_decorations_tooltip(cairo_t * pCairoContext,CairoDialog * pDialog)61 void cd_decorator_draw_decorations_tooltip (cairo_t *pCairoContext, CairoDialog *pDialog)
62 {
63 double fLineWidth = myDialogsParam.iLineWidth;
64 double fRadius = CD_RADIUS;
65 double fIconOffset = pDialog->iIconSize / 2; // myDialogsParam.iDialogIconSize/2
66
67 double fOffsetX = fRadius + fLineWidth / 2 + fIconOffset;
68 double fOffsetY = (pDialog->container.bDirectionUp ? fLineWidth / 2 : pDialog->container.iHeight - fLineWidth / 2) + (pDialog->container.bDirectionUp ? fIconOffset : /**-fIconOffset*/ -_CAIRO_DIALOG_TOOLTIP_MARGIN); // _CAIRO_DIALOG_TOOLTIP_MARGIN is to compensate for the slightly different placement of top dialogs
69 int sens = (pDialog->container.bDirectionUp ? 1 : -1);
70 int iWidth = pDialog->container.iWidth - fIconOffset;
71
72 int h = pDialog->iBubbleHeight + pDialog->iTopMargin + pDialog->iBottomMargin - (2 * fRadius + fLineWidth);
73 if (pDialog->container.bDirectionUp)
74 h -= fIconOffset;
75 else
76 h -= _CAIRO_DIALOG_TOOLTIP_MARGIN;
77
78 //On se déplace la ou il le faut
79 cairo_move_to (pCairoContext, fOffsetX, fOffsetY);
80
81 // Ligne du haut (Haut gauche -> Haut Droite)
82 cairo_rel_line_to (pCairoContext, iWidth - (2 * fRadius + fLineWidth), 0);
83
84 // Coin haut droit.
85 cairo_rel_curve_to (pCairoContext,
86 0, 0,
87 fRadius, 0,
88 fRadius, sens * fRadius);
89
90 // Ligne droite. (Haut droit -> Bas droit)
91 cairo_rel_line_to (pCairoContext, 0, sens * h);
92
93 // Coin bas droit.
94 cairo_rel_curve_to (pCairoContext,
95 0, 0,
96 0, sens * fRadius,
97 -fRadius, sens * fRadius);
98
99 // La pointe.
100 int iDeltaIconX = pDialog->container.iWindowPositionX + pDialog->container.iWidth - fRadius - fLineWidth/2 - pDialog->iAimedX;
101 cairo_rel_line_to (pCairoContext, - iDeltaIconX + _CAIRO_DIALOG_TOOLTIP_ARROW_WIDTH/2, 0);
102 cairo_rel_line_to (pCairoContext, - _CAIRO_DIALOG_TOOLTIP_ARROW_WIDTH/2, sens * CD_ARROW_HEIGHT);
103 cairo_rel_line_to (pCairoContext, - _CAIRO_DIALOG_TOOLTIP_ARROW_WIDTH/2, -sens * CD_ARROW_HEIGHT);
104 cairo_rel_line_to (pCairoContext, - iWidth + 2*fRadius + fLineWidth + iDeltaIconX + _CAIRO_DIALOG_TOOLTIP_ARROW_WIDTH/2, 0);
105
106 // Coin bas gauche.
107 cairo_rel_curve_to (pCairoContext,
108 0, 0,
109 -fRadius, 0,
110 -fRadius, -sens * fRadius);
111
112 // On remonte.
113 cairo_rel_line_to (pCairoContext, 0, - sens * h);
114
115 // Coin haut gauche.
116 cairo_rel_curve_to (pCairoContext,
117 0, 0,
118 0, -sens * fRadius,
119 fRadius, -sens * fRadius);
120 if (fRadius < 1)
121 cairo_close_path (pCairoContext);
122
123 // draw background
124 if (myDialogsParam.bUseDefaultColors)
125 gldi_style_colors_set_bg_color (pCairoContext);
126 else
127 gldi_color_set_cairo (pCairoContext, &myDialogsParam.fBgColor);
128 ///cairo_fill_preserve (pCairoContext);
129 cairo_save (pCairoContext);
130 cairo_clip_preserve (pCairoContext);
131 ///gldi_style_colors_paint_bg_color_with_alpha (pCairoContext, pDialog->container.iWidth, myDialogsParam.bUseDefaultColors ? -1. : myDialogsParam.fBgColor[3]);
132 cairo_paint (pCairoContext);
133 cairo_restore (pCairoContext);
134
135 // draw outline
136 if (myDialogsParam.bUseDefaultColors)
137 gldi_style_colors_set_line_color (pCairoContext);
138 else
139 gldi_color_set_cairo (pCairoContext, &myDialogsParam.fLineColor);
140 cairo_set_line_width (pCairoContext, fLineWidth);
141 cairo_stroke (pCairoContext);
142 }
143
144
_render_menu(GtkWidget * pMenu,cairo_t * pCairoContext)145 static void _render_menu (GtkWidget *pMenu, cairo_t *pCairoContext)
146 {
147 GldiMenuParams *pParams = g_object_get_data (G_OBJECT(pMenu), "gldi-params");
148 int iMarginPosition = -1;
149 int iAimedX = 0, iAimedY = 0;
150 int ah = CD_ARROW_HEIGHT;
151 if (pParams && pParams->pIcon) // main menu
152 {
153 iMarginPosition = pParams->iMarginPosition;
154 iAimedX = pParams->iAimedX;
155 iAimedY = pParams->iAimedY;
156 }
157 double fRadius = CD_RADIUS, fLineWidth = myDialogsParam.iLineWidth;
158
159 // draw the outline and set the clip
160 GtkAllocation alloc;
161 gtk_widget_get_allocation (pMenu, &alloc);
162
163 int w = alloc.width, h = alloc.height;
164 int x, y;
165 gdk_window_get_position (gtk_widget_get_window (gtk_widget_get_toplevel(pMenu)), &x, &y);
166 int _ah = ah - fLineWidth; // we want the tip of the arrow to reach the border, not the middle of the stroke
167 int aw = _CAIRO_DIALOG_TOOLTIP_ARROW_WIDTH/2;
168 int _aw = aw;
169 double dx, dy;
170
171 double fDockOffsetX = fRadius + fLineWidth/2;
172 double fDockOffsetY = fLineWidth/2;
173 double fFrameWidth, fFrameHeight;
174 fFrameWidth = w - 2*fRadius - fLineWidth;
175 fFrameHeight = h - fLineWidth;
176 switch (iMarginPosition)
177 {
178 case 0: // bottom
179 fFrameHeight -= ah;
180 break;
181 case 1: // top
182 fFrameHeight -= ah;
183 fDockOffsetY += ah;
184 break;
185 case 2: // right
186 fFrameWidth -= ah;
187 break;
188 case 3: // left
189 fFrameWidth -= ah;
190 fDockOffsetX += ah;
191 break;
192 default:
193 break;
194 }
195
196 cairo_move_to (pCairoContext, fDockOffsetX, fDockOffsetY);
197
198 if (iMarginPosition == 1) // top arrow
199 {
200 dx = MIN (w - fRadius - 2*aw, MAX (fRadius, iAimedX - x - aw));
201 cairo_line_to (pCairoContext, dx, fDockOffsetY);
202 cairo_line_to (pCairoContext, MIN (w, MAX (0, iAimedX - x)), fDockOffsetY - _ah);
203 cairo_line_to (pCairoContext, dx + 2*aw, fDockOffsetY);
204 cairo_line_to (pCairoContext, fFrameWidth, fDockOffsetY);
205 }
206 else
207 cairo_rel_line_to (pCairoContext, fFrameWidth, 0);
208
209 //\_________________ Coin haut droit.
210 cairo_arc (pCairoContext,
211 fDockOffsetX + fFrameWidth, fDockOffsetY + fRadius,
212 fRadius,
213 -G_PI/2, 0.);
214 if (iMarginPosition == 2) // right arrow
215 {
216 if (h < 2*aw + 2*fRadius)
217 _aw = (h - 2*fRadius) / 2;
218 dy = MIN (h - fRadius - 2*aw, MAX (fRadius, iAimedY - y - _aw));
219 cairo_line_to (pCairoContext, w - ah, dy);
220 cairo_line_to (pCairoContext, w - ah + _ah, MAX (0, iAimedY - y));
221 cairo_line_to (pCairoContext, w - ah, dy + 2*_aw);
222 cairo_line_to (pCairoContext, w - ah, h - fRadius);
223 }
224 else
225 cairo_rel_line_to (pCairoContext, 0, (fFrameHeight - fRadius * 2));
226
227 //\_________________ Coin bas droit.
228 cairo_arc (pCairoContext,
229 fDockOffsetX + fFrameWidth, fDockOffsetY + fFrameHeight - fRadius,
230 fRadius,
231 0., G_PI/2);
232
233 if (iMarginPosition == 0) // bottom arrow
234 {
235 dx = MIN (w - fRadius - 2*aw, MAX (fRadius, iAimedX - x - aw));
236 cairo_line_to (pCairoContext, dx + 2*aw, fDockOffsetY + fFrameHeight);
237 cairo_line_to (pCairoContext, MIN (w, MAX (0, iAimedX - x)), fDockOffsetY + fFrameHeight + _ah);
238 cairo_line_to (pCairoContext, dx, fDockOffsetY + fFrameHeight);
239 cairo_line_to (pCairoContext, fDockOffsetX, fDockOffsetY + fFrameHeight);
240 }
241 else
242 cairo_rel_line_to (pCairoContext, - fFrameWidth, 0);
243
244 //\_________________ Coin bas gauche.
245 cairo_arc (pCairoContext,
246 fDockOffsetX, fDockOffsetY + fFrameHeight - fRadius,
247 fRadius,
248 G_PI/2, G_PI);
249
250 if (iMarginPosition == 3) // left arrow
251 {
252 if (h < 2*aw + 2*fRadius)
253 _aw = (h - 2*fRadius) / 2;
254 dy = MIN (h - fRadius - 2*aw, MAX (fRadius, iAimedY - y - _aw));
255 cairo_line_to (pCairoContext, ah, dy);
256 cairo_line_to (pCairoContext, ah - _ah, MAX (0, iAimedY - y));
257 cairo_line_to (pCairoContext, ah, dy + 2*_aw);
258 cairo_line_to (pCairoContext, ah, fRadius);
259 }
260 else
261 cairo_rel_line_to (pCairoContext, 0, - fFrameHeight + fRadius * 2);
262 //\_________________ Coin haut gauche.
263 cairo_arc (pCairoContext,
264 fDockOffsetX, fDockOffsetY + fRadius,
265 fRadius,
266 G_PI, -G_PI/2);
267
268 // draw the background
269 if (myDialogsParam.bUseDefaultColors)
270 gldi_style_colors_set_bg_color_full (pCairoContext, FALSE);
271 else
272 gldi_color_set_cairo_rgb (pCairoContext, &myDialogsParam.fBgColor);
273 cairo_save (pCairoContext);
274 cairo_clip_preserve (pCairoContext);
275 gldi_style_colors_paint_bg_color_with_alpha (pCairoContext, alloc.width, myDialogsParam.bUseDefaultColors ? -1. : myDialogsParam.fBgColor.rgba.alpha);
276 cairo_restore (pCairoContext);
277
278 // draw outline
279 if (fLineWidth != 0) // draw the outline with same color as bg, but opaque
280 {
281 if (myDialogsParam.bUseDefaultColors)
282 gldi_style_colors_set_line_color (pCairoContext);
283 else
284 gldi_color_set_cairo (pCairoContext, &myDialogsParam.fLineColor);
285 cairo_set_line_width (pCairoContext, fLineWidth);
286 cairo_stroke_preserve (pCairoContext);
287 }
288
289 cairo_clip (pCairoContext); // clip
290 }
291
_setup_menu(GtkWidget * pMenu)292 static void _setup_menu (GtkWidget *pMenu)
293 {
294 GldiMenuParams *pParams = g_object_get_data (G_OBJECT(pMenu), "gldi-params");
295 pParams->iRadius = CD_RADIUS;
296 pParams->fAlign = CD_ALIGN;
297 pParams->iArrowHeight = CD_ARROW_HEIGHT;
298 }
299
cd_decorator_register_tooltip(void)300 void cd_decorator_register_tooltip (void)
301 {
302 CairoDialogDecorator *pDecorator = g_new (CairoDialogDecorator, 1);
303 pDecorator->set_size = cd_decorator_set_frame_size_tooltip;
304 pDecorator->render = cd_decorator_draw_decorations_tooltip;
305 pDecorator->render_opengl = NULL;
306 pDecorator->setup_menu = _setup_menu;
307 pDecorator->render_menu = _render_menu;
308 pDecorator->cDisplayedName = D_ (MY_APPLET_DECORATOR_TOOLTIP_NAME);
309 cairo_dock_register_dialog_decorator (MY_APPLET_DECORATOR_TOOLTIP_NAME, pDecorator);
310 }
311