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