1 /* Glide - a cairo based GTK+ engine
2  * Copyright (C) 2006 Andrew Johnson <acjgenius@earthlink.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Project contact: <gnome-themes-list@gnome.org>
19  *
20  */
21 
22 
23 #include "glide_gtk2_engine.h"
24 #include "glide_gtk2_support.h"
25 #include "glide_gtk2_drawing.h"
26 
27 static gdouble default_shades_table[] =
28 {
29 	0.66667,	/* DARKER	*/
30 	1.5,		/* LIGHTER	*/
31 	0.112,		/* REDMOND	*/
32 	0.8,		/* DARK		*/
33 	1.2		/* LIGHT	*/
34 };
35 
36 void
ge_blend_color(const CairoColor * color1,const CairoColor * color2,CairoColor * composite)37 ge_blend_color(const CairoColor *color1, const CairoColor *color2, CairoColor *composite)
38 {
39 	g_return_if_fail (color1 && color2 && composite);
40 
41 	composite->r = (color1->r + color2->r)/2;
42 	composite->g = (color1->g + color2->g)/2;
43 	composite->b = (color1->b + color2->b)/2;
44 	composite->a = (color1->a + color2->a)/2;
45 }
46 
47 typedef struct
48 {
49 	cairo_path_t *clip;
50 
51 	gint num_layers;
52 	cairo_pattern_t **layers;
53 } GlideFill;
54 
55 static void
glide_simple_border_gap_clip(cairo_t * canvas,gint x,gint y,gint width,gint height,GlideSide gap_side,gint gap_pos,gint gap_size)56 glide_simple_border_gap_clip(cairo_t *canvas,
57 				gint x,
58 				gint y,
59 				gint width,
60 				gint height,
61 
62 				GlideSide gap_side,
63 				gint gap_pos,
64 				gint gap_size)
65 {
66 	if (gap_side == GLIDE_SIDE_NONE)
67 	{
68 		return;
69 	}
70 
71 	cairo_set_line_width(canvas, 1.0);
72 
73 	switch (gap_side)
74 	{
75 		default:
76 		case GLIDE_SIDE_TOP:
77 			cairo_move_to(canvas, x, y);
78 			cairo_line_to(canvas, x, y + height);
79 			cairo_line_to(canvas, x + width, y + height);
80 			cairo_line_to(canvas, x + width, y);
81 			cairo_line_to(canvas, x + gap_pos + gap_size, y);
82 			cairo_line_to(canvas, x + gap_pos + gap_size, y + 3);
83 			cairo_line_to(canvas, x + gap_pos, y + 3);
84 			cairo_line_to(canvas, x + gap_pos, y);
85 			cairo_line_to(canvas, x, y);
86 		break;
87 
88 		case GLIDE_SIDE_LEFT:
89 			cairo_move_to(canvas, x, y);
90 			cairo_line_to(canvas, x + width, y);
91 			cairo_line_to(canvas, x + width, y + height);
92 			cairo_line_to(canvas, x, y + height);
93 			cairo_line_to(canvas, x, y + gap_pos + gap_size);
94 			cairo_line_to(canvas, x + 3, y + gap_pos + gap_size);
95 			cairo_line_to(canvas, x + 3, y + gap_pos);
96 			cairo_line_to(canvas, x, y + gap_pos);
97 			cairo_line_to(canvas, x, y);
98 		break;
99 
100 		case GLIDE_SIDE_BOTTOM:
101 			cairo_move_to(canvas, x + width, y + height);
102 			cairo_line_to(canvas, x + width, y);
103 			cairo_line_to(canvas, x, y);
104 			cairo_line_to(canvas, x, y + height);
105 			cairo_line_to(canvas, x + gap_pos, y + height);
106 			cairo_line_to(canvas, x + gap_pos, y + height - 3);
107 			cairo_line_to(canvas, x + gap_pos + gap_size, y + height - 3);
108 			cairo_line_to(canvas, x + gap_pos + gap_size, y + height);
109 			cairo_line_to(canvas, x + width, y + height);
110 		break;
111 
112 		case GLIDE_SIDE_RIGHT:
113 			cairo_line_to(canvas, x + width, y);
114 			cairo_line_to(canvas, x, y);
115 			cairo_line_to(canvas, x, y + height);
116 			cairo_line_to(canvas, x + width, y + height);
117 			cairo_line_to(canvas, x + width, y + gap_pos + gap_size);
118 			cairo_line_to(canvas, x + width - 3, y + gap_pos + gap_size);
119 			cairo_line_to(canvas, x + width - 3, y + gap_pos);
120 			cairo_line_to(canvas, x + width, y + gap_pos);
121 			cairo_line_to(canvas, x + width, y);
122 		break;
123 	}
124 
125 	cairo_clip(canvas);
126 }
127 
128 void
do_glide_draw_border_with_gap(cairo_t * canvas,CairoColor * base,GlideBevelStyle bevel_style,GlideBorderType border_type,gint x,gint y,gint width,gint height,GlideSide gap_side,gint gap_pos,gint gap_size)129 do_glide_draw_border_with_gap(cairo_t *canvas,
130 					CairoColor *base,
131 					GlideBevelStyle bevel_style,
132 					GlideBorderType border_type,
133 					gint x,
134 					gint y,
135 					gint width,
136 					gint height,
137 					GlideSide gap_side,
138 					gint gap_pos,
139 					gint gap_size)
140 {
141 	CairoColor color1, color2, color3, color4;
142 	gboolean inner_overlap = FALSE, outer_overlap = FALSE;
143 
144 	CairoColor darktone, lighttone, redmondtone;/*, icetone, coldtone,;*/
145 	CairoColor midtone;/*, black = {0, 0, 0, 1};*/
146 
147 	if ((border_type == GLIDE_BORDER_TYPE_NONE)
148 		|| (bevel_style == GLIDE_BEVEL_STYLE_NONE))
149 	{
150 		return;
151   	}
152 
153 	ge_shade_color(base, default_shades_table[0], &darktone);
154 
155 	if (bevel_style == GLIDE_BEVEL_STYLE_FLAT)
156 	{
157 		if (gap_size)
158 		{
159 			cairo_save(canvas);
160 
161 			glide_simple_border_gap_clip(canvas, x, y, width, height, gap_side, gap_pos, gap_size);
162 		}
163 
164 		ge_cairo_simple_border(canvas, &darktone, &darktone, x, y, width, height, (gap_side == GLIDE_SIDE_NONE) || ((gap_side == GLIDE_SIDE_BOTTOM) && (gap_size != width)) || ((gap_side == GLIDE_SIDE_RIGHT) && (gap_pos != 0)));
165 
166 		if (gap_size)
167 		{
168 			cairo_restore(canvas);
169 		}
170 
171 		return;
172 	}
173 
174 	ge_shade_color(base, default_shades_table[1], &lighttone);
175 
176 	switch (border_type)
177 	{
178 		case GLIDE_BORDER_TYPE_ENGRAVED:
179 			outer_overlap = TRUE;
180 			inner_overlap = TRUE;
181 
182 		case GLIDE_BORDER_TYPE_ETCHED:
183 		{
184 			if (bevel_style == GLIDE_BEVEL_STYLE_SMOOTHER)
185 			{
186 				ge_shade_color(base, default_shades_table[3], &darktone);
187 				ge_shade_color(base, default_shades_table[4], &lighttone);
188 			}
189 
190 			color1 = lighttone;
191 			color2 = darktone;
192 
193 			if (border_type == GLIDE_BORDER_TYPE_ENGRAVED)
194 			{
195 				CairoColor tmp;
196 
197 				tmp = color2;
198 				color2 = color1;
199 				color1 = tmp;
200 			}
201 
202 			if (gap_size)
203 			{
204 				cairo_save(canvas);
205 				glide_simple_border_gap_clip(canvas, x, y, width, height, gap_side, gap_pos, gap_size);
206 			}
207 
208 			ge_cairo_simple_border(canvas, &color2, &color1, x, y, width, height, outer_overlap);
209 
210 			if (gap_size)
211 			{
212 				cairo_restore(canvas);
213 				cairo_save(canvas);
214 				glide_simple_border_gap_clip(canvas, x, y, width, height, gap_side, gap_pos + 1, gap_size - 2);
215 			}
216 
217 			ge_cairo_simple_border(canvas, &color1, &color2, x+1, y+1, width-2, height-2, inner_overlap);
218 
219 			if (gap_size)
220 			{
221 				cairo_restore(canvas);
222 			}
223 		}
224 		break;
225 
226 		case GLIDE_BORDER_TYPE_IN:
227 		case GLIDE_BORDER_TYPE_OUT:
228 		{
229 			gboolean invert_in = TRUE;
230 
231 			if ((bevel_style == GLIDE_BEVEL_STYLE_THINICE))
232 			{
233 				color1 = darktone;
234 				color2 = lighttone;
235 
236 				if (border_type == GLIDE_BORDER_TYPE_OUT)
237 				{
238 					CairoColor tmp;
239 
240 					tmp = color2;
241 					color2 = color1;
242 					color1 = tmp;
243 				}
244 
245 				if (gap_size)
246 				{
247 					cairo_save(canvas);
248 					glide_simple_border_gap_clip(canvas, x, y, width, height, gap_side, gap_pos, gap_size);
249 				}
250 
251 				ge_cairo_simple_border(canvas, &color1, &color2, x, y, width, height, 						(gap_side == GLIDE_SIDE_NONE) || ((gap_side == GLIDE_SIDE_BOTTOM) && (gap_size != width)) || ((gap_side == GLIDE_SIDE_RIGHT) && (gap_pos != 0)));
252 				if (gap_size)
253 				{
254 					cairo_restore(canvas);
255 				}
256 			}
257 			else
258 			{
259 				switch (bevel_style)
260 				{
261 					case GLIDE_BEVEL_STYLE_REDMOND :
262 						ge_shade_color(base, default_shades_table[2], &redmondtone);
263 
264 						if (border_type == GLIDE_BORDER_TYPE_IN)
265 						{
266 							color1 = darktone;
267 							color2 = redmondtone;
268 							color3 = lighttone;
269 							color4 = *base;
270 						}
271 						else
272 						{
273 							color1 = lighttone;
274 							color2 = *base;
275 							color3 = redmondtone;
276 							color4 = darktone;
277 						}
278 
279 						outer_overlap = (gap_side == GLIDE_SIDE_NONE) || ((gap_side == GLIDE_SIDE_BOTTOM) && (gap_size != width)) || ((gap_side == GLIDE_SIDE_RIGHT) && (gap_pos != 0));
280 						inner_overlap = FALSE;
281 						invert_in = FALSE;
282 					break;
283 
284 					case GLIDE_BEVEL_STYLE_SMOOTHER :
285 						ge_shade_color(base, default_shades_table[3], &darktone);
286 						ge_shade_color(base, default_shades_table[4], &lighttone);
287 					case GLIDE_BEVEL_STYLE_SMOOTH :
288 					default :
289 						ge_blend_color(&darktone, &lighttone, &midtone);
290 
291 						if (border_type == GLIDE_BORDER_TYPE_IN)
292 						{
293 							color1 = midtone;
294 							color2 = darktone;
295 							color3 = lighttone;
296 							color4 = (bevel_style==GLIDE_BEVEL_STYLE_SMOOTHER)?*base:midtone;
297 
298 							outer_overlap = FALSE;
299 							inner_overlap = TRUE;
300 						}
301 						else
302 						{
303 							color1 = midtone;
304 							color2 = lighttone;
305 							color3 = darktone;
306 							color4 = (bevel_style==GLIDE_BEVEL_STYLE_SMOOTHER)?*base:midtone;
307 
308 							outer_overlap = TRUE;
309 							inner_overlap = (gap_side == GLIDE_SIDE_NONE) ||
310 										((gap_side == GLIDE_SIDE_BOTTOM) && (gap_size != width)) || ((gap_side == GLIDE_SIDE_RIGHT) && (gap_pos != 0));
311 						}
312 
313 						invert_in = FALSE;
314 					break;
315 				}
316 
317 				if ((invert_in) && (border_type == GLIDE_BORDER_TYPE_IN))
318 				{
319 					CairoColor tmp;
320 
321 					tmp = color3;
322 					color3 = color1;
323 					color1 = tmp;
324 
325 					tmp = color4;
326 					color4 = color2;
327 					color2 = tmp;
328 				}
329 
330 				if (gap_size)
331 				{
332 					cairo_save(canvas);
333 					glide_simple_border_gap_clip(canvas, x, y, width, height, gap_side, gap_pos, gap_size);
334 				}
335 
336 				ge_cairo_simple_border(canvas, &color1, &color3, x, y, width, height, outer_overlap);
337 
338 				if (gap_size)
339 				{
340 					cairo_restore(canvas);
341 					cairo_save(canvas);
342 					glide_simple_border_gap_clip(canvas, x, y, width, height, gap_side, gap_pos + 1, gap_size - 2);
343 				}
344 
345 				ge_cairo_simple_border(canvas, &color2, &color4, x+1, y+1, width-2, height-2, inner_overlap);
346 
347 				if (gap_size)
348 				{
349 					cairo_restore(canvas);
350 				}
351 			}
352 		}
353 		break;
354 
355 		default:
356 		break;
357 	}
358 }
359 
360 void
do_glide_draw_border(cairo_t * canvas,CairoColor * base,GlideBevelStyle bevel_style,GlideBorderType border_type,gint x,gint y,gint width,gint height)361 do_glide_draw_border(cairo_t *canvas,
362 			CairoColor *base,
363                         GlideBevelStyle bevel_style,
364                         GlideBorderType border_type,
365 			gint x,
366 			gint y,
367 			gint width,
368 			gint height)
369 
370 {
371 	do_glide_draw_border_with_gap(canvas, base, bevel_style, border_type,
372 					x, y, width, height, GLIDE_SIDE_NONE, 0, 0);
373 }
374 
375 /*#warning - do_glide_draw_option_check needs smarter sizing - perhaps draw check at base size, and scale/translate? */
376 static void
do_glide_draw_option_check(cairo_t * canvas,CairoColor * check_color,GlideCheckState check_state,gint center_x,gint center_y,gint radius)377 do_glide_draw_option_check(cairo_t *canvas,
378 				CairoColor *check_color,
379         	                GlideCheckState check_state,
380 				gint center_x,
381 				gint center_y,
382 				gint radius)
383 {
384 	cairo_save(canvas);
385 
386 	ge_cairo_set_color(canvas, check_color);
387 
388 	switch (check_state)
389 	{
390 		case GLIDE_CHECK_ON:
391 		{
392 			cairo_arc(canvas, center_x, center_y, radius*0.68, 0, 2 * G_PI);
393 			cairo_fill(canvas);
394 		}
395 		break;
396 
397 		case GLIDE_CHECK_INCONSISTENT:
398 		{
399 			gdouble line_width = radius;
400 
401 			cairo_set_line_cap(canvas, CAIRO_LINE_CAP_ROUND);
402 			cairo_set_line_width (canvas, line_width);
403 
404 			cairo_move_to(canvas, center_x - radius + line_width/2, center_y);
405 			cairo_line_to(canvas, center_x + radius - line_width/2, center_y);
406 
407 			cairo_stroke (canvas);
408 		}
409 		break;
410 
411 		case GLIDE_CHECK_OFF:
412 		default:
413 		{
414 		}
415 		break;
416 	}
417 
418 	cairo_restore(canvas);
419 }
420 
421 void
do_glide_draw_round_option(cairo_t * canvas,CairoColor * base,CairoColor * fill_color,CairoColor * check_color,GlideBevelStyle bevel_style,GlideBorderType border_type,GlideCheckState check_state,gint x,gint y,gint width,gint height)422 do_glide_draw_round_option(cairo_t *canvas,
423 				CairoColor *base,
424 				CairoColor *fill_color,
425 				CairoColor *check_color,
426         	                GlideBevelStyle bevel_style,
427                 	        GlideBorderType border_type,
428 				GlideCheckState check_state,
429 				gint x,
430 				gint y,
431 				gint width,
432 				gint height)
433 {
434 	CairoColor outer_color1, outer_color2, inner_color1, inner_color2;
435 
436 	CairoColor darktone, lighttone, redmondtone;/*, icetone, coldtone;*/
437 	CairoColor midtone;/*, black = {0, 0, 0, 1};*/
438 
439 	gint center_x = x + floor(width/2);
440 	gint center_y = y + floor(height/2);
441 	gint radius = floor(MIN(width,height)/2) + 1.5;
442 
443 
444 	cairo_save(canvas);
445 
446 	if ((border_type == GLIDE_BORDER_TYPE_NONE)
447 		|| (bevel_style == GLIDE_BEVEL_STYLE_NONE))
448 	{
449 		ge_cairo_set_color(canvas, fill_color);
450 		cairo_arc(canvas, center_x, center_y, radius, 0, 2 * G_PI);
451 		cairo_fill(canvas);
452 
453 		do_glide_draw_option_check(canvas, check_color, check_state, center_x, center_y, radius - 1);
454 
455 		cairo_restore(canvas);
456 
457 		return;
458   	}
459 
460 	ge_shade_color(base, default_shades_table[0], &darktone);
461 
462 	if (bevel_style == GLIDE_BEVEL_STYLE_FLAT)
463 	{
464 		do_glide_draw_simple_circle (canvas, &darktone, &darktone,
465 						center_x, center_y, radius);
466 
467 		ge_cairo_set_color(canvas, fill_color);
468 		cairo_arc(canvas, center_x, center_y, radius - 1, 0, 2 * G_PI);
469 		cairo_fill(canvas);
470 
471 		do_glide_draw_option_check(canvas, check_color, check_state, center_x, center_y, radius - 2);
472 
473 		cairo_restore(canvas);
474 		return;
475 	}
476 
477 	ge_shade_color(base, default_shades_table[1], &lighttone);
478 	cairo_set_line_width (canvas, 1);
479 
480 	switch (border_type)
481 	{
482 		case GLIDE_BORDER_TYPE_ENGRAVED:
483 		case GLIDE_BORDER_TYPE_ETCHED:
484 		{
485 			if (border_type == GLIDE_BORDER_TYPE_ENGRAVED)
486 			{
487 				outer_color1 = lighttone;
488 				outer_color2 = darktone;
489 
490 				inner_color1 = darktone;
491 				inner_color2 = lighttone;
492 			}
493 			else
494 			{
495 				outer_color1 = darktone;
496 				outer_color2 = lighttone;
497 
498 				inner_color1 = lighttone;
499 				inner_color2 = darktone;
500 			}
501 
502 			do_glide_draw_simple_circle (canvas, &outer_color1, &outer_color2,
503 								center_x, center_y, radius);
504 			do_glide_draw_simple_circle (canvas, &inner_color1, &inner_color2,
505 								center_x, center_y, radius - 1);
506 
507 			ge_cairo_set_color(canvas, fill_color);
508 			cairo_arc(canvas, center_x, center_y, radius - 2, 0, 2 * G_PI);
509 			cairo_fill(canvas);
510 
511 			do_glide_draw_option_check(canvas, check_color, check_state, center_x, center_y, radius - 3);
512 		}
513 		break;
514 
515 		case GLIDE_BORDER_TYPE_IN:
516 		case GLIDE_BORDER_TYPE_OUT:
517 		{
518 			gboolean invert_in = TRUE;
519 
520 			if ((bevel_style == GLIDE_BEVEL_STYLE_THINICE))
521 			{
522 				outer_color1 = darktone;
523 				outer_color2 = lighttone;
524 
525 				if (border_type == GLIDE_BORDER_TYPE_OUT)
526 				{
527 					CairoColor tmp;
528 
529 					tmp = outer_color1;
530 					outer_color1 = outer_color2;
531 					outer_color2 = tmp;
532 				}
533 
534 				do_glide_draw_simple_circle (canvas, &outer_color1, &outer_color2,
535 								center_x, center_y, radius);
536 
537 				ge_cairo_set_color(canvas, fill_color);
538 				cairo_arc(canvas, center_x, center_y, radius - 1, 0, 2 * G_PI);
539 				cairo_fill(canvas);
540 
541 				do_glide_draw_option_check(canvas, check_color, check_state, center_x, center_y, radius - 2);
542 			}
543 			else
544 			{
545 				switch (bevel_style)
546 				{
547 					case GLIDE_BEVEL_STYLE_REDMOND :
548 						ge_shade_color(base, default_shades_table[2], &redmondtone);
549 
550 						if (border_type == GLIDE_BORDER_TYPE_IN)
551 						{
552 							outer_color1 = darktone;
553 							inner_color1 = redmondtone;
554 							outer_color2 = lighttone;
555 							inner_color2 = *base;
556 						}
557 						else
558 						{
559 							outer_color1 = lighttone;
560 							inner_color1 = *base;
561 							outer_color2 = redmondtone;
562 							inner_color2 = darktone;
563 						}
564 
565 						invert_in = FALSE;
566 					break;
567 
568 					case GLIDE_BEVEL_STYLE_SMOOTHER :
569 						ge_shade_color(base, default_shades_table[3], &darktone);
570 						ge_shade_color(base, default_shades_table[4], &lighttone);
571 					case GLIDE_BEVEL_STYLE_SMOOTH :
572 					default :
573 						ge_blend_color(&darktone, &lighttone, &midtone);
574 
575 						if (border_type == GLIDE_BORDER_TYPE_IN)
576 						{
577 							outer_color1 = midtone;
578 							inner_color1 = darktone;
579 							outer_color2 = lighttone;
580 							inner_color2 = (bevel_style==GLIDE_BEVEL_STYLE_SMOOTHER)?*base:midtone;
581 						}
582 						else
583 						{
584 							outer_color1 = midtone;
585 							inner_color1 = lighttone;
586 							outer_color2 = darktone;
587 							inner_color2 = (bevel_style==GLIDE_BEVEL_STYLE_SMOOTHER)?*base:midtone;
588 						}
589 
590 						invert_in = FALSE;
591 					break;
592 				}
593 
594 				if ((invert_in) && (border_type == GLIDE_BORDER_TYPE_IN))
595 				{
596 					CairoColor tmp;
597 
598 					tmp = outer_color2;
599 					outer_color2 = outer_color1;
600 					outer_color1 = tmp;
601 
602 					tmp = inner_color2;
603 					inner_color2 = inner_color1;
604 					inner_color1 = tmp;
605 				}
606 
607 				do_glide_draw_simple_circle (canvas, &outer_color1, &outer_color2,
608 								center_x, center_y, radius);
609 				do_glide_draw_simple_circle (canvas, &inner_color1, &inner_color2,
610 								center_x, center_y, radius - 1);
611 
612 				ge_cairo_set_color(canvas, fill_color);
613 				cairo_arc(canvas, center_x, center_y, radius - 2, 0, 2 * G_PI);
614 				cairo_fill(canvas);
615 
616 				do_glide_draw_option_check(canvas, check_color, check_state, center_x, center_y, radius - 3);
617 			}
618 		}
619 		break;
620 
621 		default:
622 		break;
623 	}
624 
625 	cairo_restore(canvas);
626 }
627 
628 /***********************************************
629  * do_glide_draw_check -
630  *
631  *   A simple routine to draw a redmond style
632  *   check mark using the passed Color.
633  *
634  *   It originated in Smooth-Engine.
635  ***********************************************/
636 void
do_glide_draw_check(cairo_t * canvas,CairoColor * color,gint x,gint y,gint width,gint height)637 do_glide_draw_check (cairo_t *canvas,
638                        CairoColor * color,
639                        gint x,
640                        gint y,
641                        gint width,
642                        gint height)
643 {
644 /*
645 
646 EVEN -
647 
648     0   1   2   3   4   5   6   7   8   9
649   +---+---+---+---+---+---+---+---+---+---+
650 0 |   |   |   |   |   |   |   |   |   |   |
651   +---+---+---+---+---+---+---+---+---+---+
652 1 |   |   |   |   |   |   |   |   | x |   |
653   +---+---+---+---+---+---+---+---+---+---+
654 2 |   |   |   |   |   |   |   | x | x |   |
655   +---+---+---+---+---+---+---+---+---+---+
656 3 |   |   |   |   |   |   | x | x | x |   |
657   +---+---+---+---+---+---+---+---+---+---+
658 4 |   | x |   |   |   | x | x | x |   |   |
659   +---+---+---+---+---+---+---+---+---+---+
660 5 |   | x | x |   | x | x | x |   |   |   |
661   +---+---+---+---+---+---+---+---+---+---+
662 6 |   | x | x | x | x | x |   |   |   |   |
663   +---+---+---+---+---+---+---+---+---+---+
664 7 |   |   | x | x | x |   |   |   |   |   |
665   +---+---+---+---+---+---+---+---+---+---+
666 8 |   |   |   | x |   |   |   |   |   |   |
667   +---+---+---+---+---+---+---+---+---+---+
668 9 |   |   |   |   |   |   |   |   |   |   |
669   +---+---+---+---+---+---+---+---+---+---+
670 
671 ODD -
672 
673     0   1   2   3   4   5   6   7   8
674   +---+---+---+---+---+---+---+---+---+
675 0 |   |   |   |   |   |   |   |   |   |
676   +---+---+---+---+---+---+---+---+---+
677 1 |   |   |   |   |   |   |   | x |   |
678   +---+---+---+---+---+---+---+---+---+
679 2 |   |   |   |   |   |   | x | x |   |
680   +---+---+---+---+---+---+---+---+---+
681 3 |   | x |   |   |   | x | x | x |   |
682   +---+---+---+---+---+---+---+---+---+
683 4 |   | x | x |   | x | x | x |   |   |
684   +---+---+---+---+---+---+---+---+---+
685 5 |   | x | x | x | x | x |   |   |   |
686   +---+---+---+---+---+---+---+---+---+
687 6 |   |   | x | x | x |   |   |   |   |
688   +---+---+---+---+---+---+---+---+---+
689 7 |   |   |   | x |   |   |   |   |   |
690   +---+---+---+---+---+---+---+---+---+
691 8 |   |   |   |   |   |   |   |   |   |
692   +---+---+---+---+---+---+---+---+---+
693 
694 */
695   gint odd = 0;
696   gdouble left, top;
697   gint scale, factor;
698 
699   scale = MIN(width, height);
700 
701   factor = 10;
702 
703   if ((odd = (scale % 2)))
704   {
705     factor -= 1;
706   }
707 
708   if (scale <= (factor + 2))
709     scale = factor;
710 
711   left = x + floor((width - scale) / 2) + 0.5;
712   top = y + floor((height - scale) / 2) + 0.5;
713 
714   cairo_save(canvas);
715 
716   ge_cairo_set_color(canvas, color);
717   cairo_set_line_width(canvas, 0.5);
718 
719   cairo_move_to(canvas, left + floor((1*scale)/factor), top + floor(((4-odd)*scale)/factor)); /*(1,4-odd)*/
720   cairo_line_to(canvas, left + floor((1*scale)/factor), top + floor(((6-odd)*scale)/factor)); /*(1,6-odd)*/
721   cairo_line_to(canvas, left + floor((3*scale)/factor), top + floor(((8-odd)*scale)/factor)); /*(3,8-odd)*/
722   cairo_line_to(canvas, left + floor(((8-odd)*scale)/factor), top + floor((3*scale)/factor)); /*(8-odd,3)*/
723   cairo_line_to(canvas, left + floor(((8-odd)*scale)/factor), top + floor((1*scale)/factor)); /*(8-odd,1)*/
724   cairo_line_to(canvas, left + floor((3*scale)/factor), top + floor(((6-odd)*scale)/factor)); /*(3,6-odd)*/
725   cairo_line_to(canvas, left + floor((1*scale)/factor), top + floor(((4-odd)*scale)/factor)); /*(1,4-odd)*/
726 
727   cairo_fill_preserve(canvas);
728   cairo_stroke(canvas);
729 
730   cairo_restore(canvas);
731 }
732 
733 void
do_glide_draw_simple_circle(cairo_t * canvas,CairoColor * tl,CairoColor * br,gint center_x,gint center_y,gint radius)734 do_glide_draw_simple_circle (cairo_t *canvas,
735                      	  		CairoColor * tl,
736                        			CairoColor * br,
737 					gint center_x,
738 					gint center_y,
739 					gint radius)
740 {
741       cairo_new_path (canvas);
742 
743       cairo_move_to(canvas, center_x + (radius + 2), center_y + (radius + 2));
744       cairo_line_to(canvas, center_x + (radius + 2)*sin(G_PI/4.0), center_y - (radius + 2)*cos(G_PI/4.0));
745       cairo_line_to(canvas, center_x - (radius + 2)*sin(G_PI/4.0), center_y + (radius + 2)*cos(G_PI/4.0));
746       cairo_line_to(canvas, center_x + (radius + 2), center_y + (radius + 2));
747 
748       cairo_close_path (canvas);
749 
750       cairo_save(canvas);
751 
752       cairo_clip (canvas);
753 
754       ge_cairo_set_color(canvas, br);
755       cairo_arc(canvas, center_x, center_y, radius, 0,  2*G_PI);
756       cairo_fill(canvas);
757 
758       cairo_restore(canvas);
759 
760       cairo_save(canvas);
761       cairo_new_path (canvas);
762 
763       cairo_move_to(canvas, center_x - (radius + 2), center_y - (radius + 2));
764       cairo_line_to(canvas, center_x + (radius + 2)*sin(G_PI/4.0), center_y - (radius + 2)*cos(G_PI/4.0));
765       cairo_line_to(canvas, center_x - (radius + 2)*sin(G_PI/4.0), center_y + (radius + 2)*cos(G_PI/4.0));
766       cairo_line_to(canvas, center_x - (radius + 2), center_y - (radius + 2));
767 
768       cairo_close_path (canvas);
769 
770       cairo_clip (canvas);
771 
772       ge_cairo_set_color(canvas, tl);
773       cairo_arc(canvas, center_x, center_y, radius, 0, 2*G_PI);
774       cairo_fill(canvas);
775 
776       cairo_restore(canvas);
777 }
778 
779 /***********************************************
780  * do_glide_draw_arrow -
781  *
782  *   A simple routine to draw a redmond style
783  *   arrow using the passed GC.
784  *
785  *   Taken in part from smooth, it was based on
786  *   XFCE's & CleanIce draw arrow routines,
787  *   both which  were based on ThinIce's.
788  ***********************************************/
789 void
do_glide_draw_arrow(cairo_t * canvas,CairoColor * color,GtkArrowType arrow_type,gboolean fill,gint x,gint y,gint width,gint height)790 do_glide_draw_arrow (cairo_t *canvas,
791                CairoColor * color,
792                GtkArrowType arrow_type,
793                gboolean fill,
794                gint x,
795                gint y,
796                gint width,
797                gint height)
798 {
799 	gint aw = width, ah = height;
800  	GdkPoint points[3];
801 
802 	switch (arrow_type)
803 	{
804 		case GTK_ARROW_UP:
805 		case GTK_ARROW_DOWN:
806 		{
807 			gdouble tmp=((aw+1)/2) - ((height%2)?1:0);
808 
809 			if (tmp > ah)
810 			{
811 				aw = 2*ah - 1 - ((height%2)?1:0);
812 				ah = (aw+1)/2;
813 			}
814 			else
815 			{
816 				ah = (gint) tmp;
817 				aw = 2*ah - 1;
818 			}
819 
820 			if ((aw < 5) || (ah < 3))
821 			{
822 				aw = 5;
823 				ah = 3;
824 			}
825 
826 			x += (width - aw) / 2 ;
827 			y += (height - ah) / 2;
828 			width = aw;
829 			height = ah;
830 
831 			width += width % 2 - 1;
832 
833 			points[0].x = x;
834 			points[1].x = x + width - 1;
835 			points[2].x = x + ((height - 1) - (height - (1 + width / 2)));
836 
837 			points[0].y = points[1].y = y;
838 			points[2].y = y + height - 1;
839 
840 			if (arrow_type == GTK_ARROW_UP)
841 			{
842 				gint flip = points[1].y;
843 
844 				points[0].y = points[1].y = points[2].y;
845 				points[2].y = flip;
846 			}
847 		}
848 		break;
849 
850 		case GTK_ARROW_LEFT:
851 		case GTK_ARROW_RIGHT:
852 		{
853 			gdouble tmp=((ah+1)/2) - ((width%2)?1:0);
854 
855 			if (tmp > aw)
856 			{
857 				ah = 2*aw - 1 - ((width%2)?1:0);
858 				aw = (ah+1)/2;
859 			}
860 			else
861 			{
862 				aw = (gint) tmp;
863 				ah = 2*aw - 1;
864 			}
865 
866 			if ((ah < 5) || (aw < 3))
867 			{
868 				ah = 5;
869 				aw = 3;
870 			}
871 
872 			x += (width - aw) / 2 ;
873 			y += (height - ah) / 2;
874 			width = aw;
875 			height = ah;
876 
877 			height += height % 2 - 1;
878 
879 			points[0].y = y;
880 			points[1].y = y + height - 1;
881 			points[2].y = y + ((width - 1) - (width - (1 + height / 2)));
882 
883 			points[0].x = points[1].x = x;
884 			points[2].x = x + width - 1;
885 
886 			if (arrow_type == GTK_ARROW_LEFT)
887 			{
888 				gint flip = points[0].x;
889 
890 				points[0].x = points[1].x = points[2].x;
891 				points[2].x = flip;
892 			}
893 		}
894 		break;
895 
896 		default:
897 		{
898 			return;
899 		}
900 	}
901 
902 	cairo_save(canvas);
903 
904 	ge_cairo_set_color(canvas, color);
905 	cairo_set_line_width (canvas, 0.5);
906 
907 	cairo_move_to(canvas, points[0].x + 0.5, points[0].y + 0.5);
908 	cairo_line_to(canvas, points[1].x + 0.5, points[1].y + 0.5);
909 	cairo_line_to(canvas, points[2].x + 0.5, points[2].y + 0.5);
910 	cairo_line_to(canvas, points[0].x + 0.5, points[0].y + 0.5);
911 
912 	if (fill)
913 	{
914 		cairo_stroke_preserve(canvas);
915 
916 		cairo_fill(canvas);
917 	}
918 	else
919 	{
920 		cairo_stroke(canvas);
921 	}
922 
923 	cairo_restore(canvas);
924 }
925 
926 /***********************************************
927  * do_glide_draw_line -
928  *
929  *   A simple routine to draw a redmond style
930  *   spacer line.
931  ***********************************************/
932 void
do_glide_draw_line(cairo_t * canvas,CairoColor * dark,CairoColor * light,GdkRectangle * area,gint start,gint end,gint base,gboolean horizontal)933 do_glide_draw_line(cairo_t *canvas,
934              CairoColor * dark,
935              CairoColor * light,
936              GdkRectangle * area,
937              gint start,
938              gint end,
939              gint base,
940              gboolean horizontal)
941 {
942   cairo_set_line_width (canvas, 1);
943 
944   if (horizontal)
945     {
946       ge_cairo_set_color(canvas, dark);
947       cairo_move_to(canvas, start + 1.5, base + 0.5);
948       cairo_line_to(canvas, end - 1.5, base + 0.5);
949       cairo_stroke(canvas);
950 
951       ge_cairo_set_color(canvas, light);
952       cairo_move_to(canvas, start + 1.5, base + 1.5);
953       cairo_line_to(canvas, end - 1.5, base + 1.5);
954       cairo_stroke(canvas);
955     }
956   else
957     {
958       ge_cairo_set_color(canvas, dark);
959       cairo_move_to(canvas, base + 0.5, start + 1.5);
960       cairo_line_to(canvas, base + 0.5, end - 1.5);
961       cairo_stroke(canvas);
962 
963       ge_cairo_set_color(canvas, light);
964       cairo_move_to(canvas, base + 1.5, start + 1.5);
965       cairo_line_to(canvas, base + 1.5, end - 1.5);
966       cairo_stroke(canvas);
967     }
968 }
969 
970 static void
do_glide_draw_dot(cairo_t * canvas,CairoColor * light,CairoColor * dark,CairoColor * mid,gint x,gint y)971 do_glide_draw_dot (cairo_t *canvas,
972 			CairoColor * light,
973 			CairoColor * dark,
974 			CairoColor * mid,
975 			gint x,
976 			gint y)
977 {
978 	ge_cairo_set_color(canvas, dark);
979 	cairo_rectangle (canvas, x - 1, y - 1, 1, 1);
980 	cairo_rectangle (canvas, x - 0, y - 1, 1, 1);
981 	cairo_rectangle (canvas, x - 1, y - 0, 1, 1);
982 	cairo_fill(canvas);
983 
984 	ge_cairo_set_color(canvas, light);
985 	cairo_rectangle (canvas, x + 1, y + 1, 1, 1);
986 	cairo_rectangle (canvas, x + 0, y + 1, 1, 1);
987 	cairo_rectangle (canvas, x + 1, y + 0, 1, 1);
988 	cairo_fill(canvas);
989 
990 	ge_cairo_set_color(canvas, mid);
991 	cairo_rectangle (canvas, x + 1, y - 1, 1, 1);
992 	cairo_rectangle (canvas, x - 1, y + 1, 1, 1);
993 	cairo_fill(canvas);
994 }
995 
996 void
do_glide_draw_grip(cairo_t * canvas,CairoColor * light,CairoColor * dark,gint x,gint y,gint width,gint height,gboolean vertical)997 do_glide_draw_grip (cairo_t *canvas,
998 			CairoColor * light,
999 			CairoColor * dark,
1000 			gint x,
1001 			gint y,
1002 			gint width,
1003 			gint height,
1004 			gboolean vertical)
1005 {
1006 	gint modx=0, mody=0;
1007 	CairoColor mid;
1008 
1009 	if (vertical)
1010 		mody = 5;
1011 	else
1012 		modx = 5;
1013 
1014 	cairo_save(canvas);
1015 
1016 	cairo_set_line_width (canvas, 0.5);
1017 	cairo_set_antialias(canvas, CAIRO_ANTIALIAS_NONE);
1018 
1019 	ge_blend_color(dark, light, &mid);
1020 
1021 	do_glide_draw_dot (canvas,
1022 		 light, dark, &mid,
1023 		 x + width / 2 - modx + 1,
1024 		 y + height / 2 - mody);
1025 	do_glide_draw_dot (canvas,
1026 		 light, dark, &mid,
1027 		 x + width / 2 + 1,
1028 		 y + height / 2);
1029 	do_glide_draw_dot (canvas,
1030 		 light, dark, &mid,
1031 		 x + width / 2 + modx + 1,
1032 		 y + height / 2 + mody);
1033 
1034 	cairo_restore(canvas);
1035 }
1036 
1037 /***********************************************/
1038 /* MenuShell/MenuBar Item Prelight Workaround  */
1039 /***********************************************/
1040 
1041 /***********************************************
1042  * gtk_menu_shell_cleanup_signals -
1043  *
1044  *   Cleanup/remove Menu Shell signals
1045  ***********************************************/
1046 static void
glide_gtk2_engine_hack_menu_shell_cleanup(GtkWidget * widget)1047 glide_gtk2_engine_hack_menu_shell_cleanup(GtkWidget *widget)
1048 {
1049   if (GE_IS_MENU_BAR(widget))
1050     {
1051       gint id = 0;
1052 
1053       id = (gint)g_object_steal_data (G_OBJECT(widget), "GLIDE_MENU_SHELL_MOTION_ID");
1054       g_signal_handler_disconnect(G_OBJECT(widget), id);
1055 
1056       id = (gint)g_object_steal_data (G_OBJECT(widget), "GLIDE_MENU_SHELL_LEAVE_ID");
1057       g_signal_handler_disconnect(G_OBJECT(widget), id);
1058 
1059       id = (gint)g_object_steal_data (G_OBJECT(widget), "GLIDE_MENU_SHELL_DESTROY_ID");
1060       g_signal_handler_disconnect(G_OBJECT(widget), id);
1061 
1062       id = (gint)g_object_steal_data (G_OBJECT(widget), "GLIDE_MENU_SHELL_STYLE_SET_ID");
1063       g_signal_handler_disconnect(G_OBJECT(widget), id);
1064 
1065       g_object_steal_data (G_OBJECT(widget), "GLIDE_MENU_SHELL_HACK_SET");
1066     }
1067 }
1068 
1069 /***********************************************
1070  * glide_gtk2_engine_hack_menu_shell_style_set -
1071  *
1072  *   Style set signal to ensure menushell signals
1073  *   get cleaned up if the theme changes
1074  ***********************************************/
1075 static gboolean
glide_gtk2_engine_hack_menu_shell_style_set(GtkWidget * widget,GtkStyle * previous_style,gpointer user_data)1076 glide_gtk2_engine_hack_menu_shell_style_set(GtkWidget *widget,
1077                          GtkStyle *previous_style,
1078                          gpointer user_data)
1079 {
1080   glide_gtk2_engine_hack_menu_shell_cleanup(widget);
1081 
1082   return FALSE;
1083 }
1084 
1085 /***********************************************
1086  * gtk_menu_shell_destroy -
1087  *
1088  *   Destroy signal to ensure menushell signals
1089  *   get cleaned if it is destroyed
1090  ***********************************************/
1091 static gboolean
glide_gtk2_engine_hack_menu_shell_destroy(GtkWidget * widget,GdkEvent * event,gpointer user_data)1092 glide_gtk2_engine_hack_menu_shell_destroy(GtkWidget *widget,
1093                        GdkEvent *event,
1094                        gpointer user_data)
1095 {
1096  glide_gtk2_engine_hack_menu_shell_cleanup (widget);
1097 
1098   return FALSE;
1099 }
1100 
1101 /***********************************************
1102  * glide_gtk2_engine_hack_menu_shell_motion -
1103  *
1104  *   Motion signal to ensure menushell items
1105  *   prelight state changes on mouse move.
1106  ***********************************************/
1107 static gboolean
glide_gtk2_engine_hack_menu_shell_motion(GtkWidget * widget,GdkEventMotion * event,gpointer user_data)1108 glide_gtk2_engine_hack_menu_shell_motion(GtkWidget *widget,
1109                       GdkEventMotion *event,
1110                       gpointer user_data)
1111 {
1112   if (GE_IS_MENU_SHELL(widget))
1113     {
1114       gint pointer_x, pointer_y;
1115       GdkModifierType pointer_mask;
1116       GList *children = NULL, *child = NULL;
1117 
1118       gdk_window_get_pointer(widget->window, &pointer_x, &pointer_y, &pointer_mask);
1119 
1120       if (GE_IS_CONTAINER(widget))
1121         {
1122           children = gtk_container_get_children(GTK_CONTAINER(widget));
1123 
1124           for (child = g_list_first(children); child; child = g_list_next(child))
1125             {
1126 	      if ((child->data) && GE_IS_WIDGET(child->data) &&
1127                   (GTK_WIDGET_STATE(GTK_WIDGET(child->data)) != GTK_STATE_INSENSITIVE))
1128 	        {
1129 	          if ((pointer_x >= GTK_WIDGET(child->data)->allocation.x) &&
1130 	              (pointer_y >= GTK_WIDGET(child->data)->allocation.y) &&
1131 	              (pointer_x < (GTK_WIDGET(child->data)->allocation.x +
1132 	                              GTK_WIDGET(child->data)->allocation.width)) &&
1133 	              (pointer_y < (GTK_WIDGET(child->data)->allocation.y +
1134 	                              GTK_WIDGET(child->data)->allocation.height)))
1135 	            {
1136                       gtk_widget_set_state (GTK_WIDGET(child->data), GTK_STATE_PRELIGHT);
1137 	            }
1138 	          else
1139                     {
1140                       gtk_widget_set_state (GTK_WIDGET(child->data), GTK_STATE_NORMAL);
1141                     }
1142                  }
1143              }
1144 
1145            if (children)
1146              g_list_free(children);
1147         }
1148     }
1149 
1150   return FALSE;
1151 }
1152 
1153 /***********************************************
1154  * gtk_menu_shell_leave -
1155  *
1156  *   Leave signal to ensure menushell items
1157  *   normal state on mouse leave.
1158  ***********************************************/
1159 static gboolean
glide_gtk2_engine_hack_menu_shell_leave(GtkWidget * widget,GdkEventCrossing * event,gpointer user_data)1160 glide_gtk2_engine_hack_menu_shell_leave(GtkWidget *widget,
1161                       GdkEventCrossing *event,
1162                       gpointer user_data)
1163 {
1164   if (GE_IS_MENU_SHELL(widget))
1165     {
1166       GList *children = NULL, *child = NULL;
1167 
1168       if (GE_IS_CONTAINER(widget))
1169         {
1170           children = gtk_container_get_children(GTK_CONTAINER(widget));
1171 
1172           for (child = g_list_first(children); child; child = g_list_next(child))
1173             {
1174 	      if ((child->data) && GE_IS_MENU_ITEM(child->data) &&
1175                   (GTK_WIDGET_STATE(GTK_WIDGET(child->data)) != GTK_STATE_INSENSITIVE))
1176 	        {
1177                   if ((!GE_IS_MENU(GTK_MENU_ITEM(child->data)->submenu)) ||
1178                       (!(GTK_WIDGET_REALIZED(GTK_MENU_ITEM(child->data)->submenu) &&
1179                          GTK_WIDGET_VISIBLE(GTK_MENU_ITEM(child->data)->submenu) &&
1180                          GTK_WIDGET_REALIZED(GTK_MENU(GTK_MENU_ITEM(child->data)->submenu)->toplevel) &&
1181                          GTK_WIDGET_VISIBLE(GTK_MENU(GTK_MENU_ITEM(child->data)->submenu)->toplevel))))
1182 	          {
1183                     gtk_widget_set_state (GTK_WIDGET(child->data), GTK_STATE_NORMAL);
1184                   }
1185                 }
1186             }
1187 
1188           if (children)
1189   	    g_list_free(children);
1190         }
1191     }
1192 
1193   return FALSE;
1194 }
1195 
1196 /***********************************************
1197  * gtk_menu_shell_setup_signals -
1198  *
1199  *   Setup Menu Shell with signals to ensure
1200  *   prelight works on items
1201  ***********************************************/
1202 void
glide_gtk2_engine_hack_menu_shell_setup(GtkWidget * widget)1203 glide_gtk2_engine_hack_menu_shell_setup(GtkWidget *widget)
1204 {
1205   if (GE_IS_MENU_BAR(widget))
1206     {
1207       gint id = 0;
1208 
1209       if (!g_object_get_data(G_OBJECT(widget), "GLIDE_MENU_SHELL_HACK_SET"))
1210       {
1211         id = g_signal_connect(G_OBJECT(widget), "motion-notify-event",
1212                                              (GCallback)glide_gtk2_engine_hack_menu_shell_motion,
1213                                              NULL);
1214 
1215         g_object_set_data(G_OBJECT(widget), "GLIDE_MENU_SHELL_MOTION_ID", (gpointer)id);
1216 
1217         id = g_signal_connect(G_OBJECT(widget), "leave-notify-event",
1218                                              (GCallback)glide_gtk2_engine_hack_menu_shell_leave,
1219                                              NULL);
1220         g_object_set_data(G_OBJECT(widget), "GLIDE_MENU_SHELL_LEAVE_ID", (gpointer)id);
1221 
1222         id = g_signal_connect(G_OBJECT(widget), "destroy-event",
1223                                              (GCallback)glide_gtk2_engine_hack_menu_shell_destroy,
1224                                              NULL);
1225         g_object_set_data(G_OBJECT(widget), "GLIDE_MENU_SHELL_DESTROY_ID", (gpointer)id);
1226 
1227         g_object_set_data(G_OBJECT(widget), "GLIDE_MENU_SHELL_HACK_SET", (gpointer)1);
1228 
1229         id = g_signal_connect(G_OBJECT(widget), "style-set",
1230                                              (GCallback)glide_gtk2_engine_hack_menu_shell_style_set,
1231                                              NULL);
1232         g_object_set_data(G_OBJECT(widget), "GLIDE_MENU_SHELL_STYLE_SET_ID", (gpointer)id);
1233       }
1234     }
1235 }
1236