No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* Nautilus - Canvas item for floating selection.
4 *
5 * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
6 * Copyright (C) 2011 Red Hat Inc.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
23 * Authors: Federico Mena <federico@nuclecu.unam.mx>
24 * Cosimo Cecchi <cosimoc@redhat.com>
25 */
26
27 #include <config.h>
28
29 #include "nautilus-selection-canvas-item.h"
30
31 #include <math.h>
32
33 enum {
34 PROP_X1 = 1,
35 PROP_Y1,
36 PROP_X2,
37 PROP_Y2,
38 PROP_FILL_COLOR_RGBA,
39 PROP_OUTLINE_COLOR_RGBA,
40 PROP_OUTLINE_STIPPLING,
41 PROP_WIDTH_PIXELS,
42 NUM_PROPERTIES
43 };
44
45 static GParamSpec *properties[NUM_PROPERTIES] = { NULL };
46
47 typedef struct {
48 /*< public >*/
49 int x0, y0, x1, y1;
50 } Rect;
51
52 struct _NautilusSelectionCanvasItemDetails {
53 Rect last_update_rect;
54 Rect last_outline_update_rect;
55 int last_outline_update_width;
56
57 double x1, y1, x2, y2; /* Corners of item */
58 double width; /* Outline width */
59
60 GdkRGBA fill_color;
61 GdkRGBA outline_color;
62
63 gboolean outline_stippling;
64
65 /* Configuration flags */
66
67 unsigned int fill_set : 1; /* Is fill color set? */
68 unsigned int outline_set : 1; /* Is outline color set? */
69
70 double fade_out_fill_alpha;
71 double fade_out_outline_alpha;
72
73 double fade_out_fill_delta;
74 double fade_out_outline_delta;
75
76 guint fade_out_handler_id;
77 };
78
79 G_DEFINE_TYPE (NautilusSelectionCanvasItem, nautilus_selection_canvas_item, EEL_TYPE_CANVAS_ITEM);
80
81 #define DASH_ON 0.8
82 #define DASH_OFF 1.7
83 static void
84 nautilus_selection_canvas_item_draw (EelCanvasItem *item,
85 cairo_t *cr,
86 cairo_region_t *region)
87 {
88 NautilusSelectionCanvasItem *self;
89 double x1, y1, x2, y2;
90 int cx1, cy1, cx2, cy2;
91 double i2w_dx, i2w_dy;
92
93 self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
94
95 /* Get canvas pixel coordinates */
96 i2w_dx = 0.0;
97 i2w_dy = 0.0;
98 eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
99
100 x1 = self->priv->x1 + i2w_dx;
101 y1 = self->priv->y1 + i2w_dy;
102 x2 = self->priv->x2 + i2w_dx;
103 y2 = self->priv->y2 + i2w_dy;
104
105 eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
106 eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
107
108 if (cx2 <= cx1 || cy2 <= cy1 ) {
109 return;
110 }
111
112 cairo_save (cr);
113
114 if (self->priv->fill_set) {
115 GdkRGBA actual_fill;
116
117 actual_fill = self->priv->fill_color;
118
119 if (self->priv->fade_out_handler_id != 0) {
120 actual_fill.alpha = self->priv->fade_out_fill_alpha;
121 }
122
123 gdk_cairo_set_source_rgba (cr, &actual_fill);
124 cairo_rectangle (cr,
125 cx1, cy1,
126 cx2 - cx1 + 1,
127 cy2 - cy1 + 1);
128 cairo_fill (cr);
129 }
130
131 if (self->priv->outline_set) {
132 GdkRGBA actual_outline;
133
134 actual_outline = self->priv->outline_color;
135
136 if (self->priv->fade_out_handler_id != 0) {
137 actual_outline.alpha = self->priv->fade_out_outline_alpha;
138 }
139
140 gdk_cairo_set_source_rgba (cr, &actual_outline);
141 cairo_set_line_width (cr, (int) self->priv->width);
142
143 if (self->priv->outline_stippling) {
144 double dash[2] = { DASH_ON, DASH_OFF };
145
146 cairo_set_dash (cr, dash, G_N_ELEMENTS (dash), 0);
147 }
148
149 cairo_rectangle (cr,
150 cx1 + 0.5, cy1 + 0.5,
151 cx2 - cx1,
152 cy2 - cy1);
153 cairo_stroke (cr);
154 }
155
156 cairo_restore (cr);
157 }
158
159 static double
160 nautilus_selection_canvas_item_point (EelCanvasItem *item,
161 double x,
162 double y,
163 int cx,
164 int cy,
165 EelCanvasItem **actual_item)
166 {
167 NautilusSelectionCanvasItem *self;
168 double x1, y1, x2, y2;
169 double hwidth;
170 double dx, dy;
171 double tmp;
172
173 self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
174 *actual_item = item;
175
176 /* Find the bounds for the rectangle plus its outline width */
177
178 x1 = self->priv->x1;
179 y1 = self->priv->y1;
180 x2 = self->priv->x2;
181 y2 = self->priv->y2;
182
183 if (self->priv->outline_set) {
184 hwidth = (self->priv->width / item->canvas->pixels_per_unit) / 2.0;
185
186 x1 -= hwidth;
187 y1 -= hwidth;
188 x2 += hwidth;
189 y2 += hwidth;
190 } else
191 hwidth = 0.0;
192
193 /* Is point inside rectangle (which can be hollow if it has no fill set)? */
194
195 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
196 if (self->priv->fill_set || !self->priv->outline_set)
197 return 0.0;
198
199 dx = x - x1;
200 tmp = x2 - x;
201 if (tmp < dx)
202 dx = tmp;
203
204 dy = y - y1;
205 tmp = y2 - y;
206 if (tmp < dy)
207 dy = tmp;
208
209 if (dy < dx)
210 dx = dy;
211
212 dx -= 2.0 * hwidth;
213
214 if (dx < 0.0)
215 return 0.0;
216 else
217 return dx;
218 }
219
220 /* Point is outside rectangle */
221
222 if (x < x1)
223 dx = x1 - x;
224 else if (x > x2)
225 dx = x - x2;
226 else
227 dx = 0.0;
228
229 if (y < y1)
230 dy = y1 - y;
231 else if (y > y2)
232 dy = y - y2;
233 else
234 dy = 0.0;
235
236 return sqrt (dx * dx + dy * dy);
237 }
238
239 static void
240 request_redraw_borders (EelCanvas *canvas,
241 Rect *update_rect,
242 int width)
243 {
244 eel_canvas_request_redraw (canvas,
245 update_rect->x0, update_rect->y0,
246 update_rect->x1, update_rect->y0 + width);
247 eel_canvas_request_redraw (canvas,
248 update_rect->x0, update_rect->y1-width,
249 update_rect->x1, update_rect->y1);
250 eel_canvas_request_redraw (canvas,
251 update_rect->x0, update_rect->y0,
252 update_rect->x0+width, update_rect->y1);
253 eel_canvas_request_redraw (canvas,
254 update_rect->x1-width, update_rect->y0,
255 update_rect->x1, update_rect->y1);
256 }
257
258 static Rect make_rect (int x0, int y0, int x1, int y1);
259
260 static int
261 rect_empty (const Rect *src) {
262 return (src->x1 <= src->x0 || src->y1 <= src->y0);
263 }
264
265 static gboolean
266 rects_intersect (Rect r1, Rect r2)
267 {
268 if (r1.x0 >= r2.x1) {
269 return FALSE;
270 }
271 if (r2.x0 >= r1.x1) {
272 return FALSE;
273 }
274 if (r1.y0 >= r2.y1) {
275 return FALSE;
276 }
277 if (r2.y0 >= r1.y1) {
278 return FALSE;
279 }
280 return TRUE;
281 }
282
283 static void
284 diff_rects_guts (Rect ra, Rect rb, int *count, Rect result[4])
285 {
286 if (ra.x0 < rb.x0) {
287 result[(*count)++] = make_rect (ra.x0, ra.y0, rb.x0, ra.y1);
288 }
289 if (ra.y0 < rb.y0) {
290 result[(*count)++] = make_rect (ra.x0, ra.y0, ra.x1, rb.y0);
291 }
292 if (ra.x1 < rb.x1) {
293 result[(*count)++] = make_rect (ra.x1, rb.y0, rb.x1, rb.y1);
294 }
295 if (ra.y1 < rb.y1) {
296 result[(*count)++] = make_rect (rb.x0, ra.y1, rb.x1, rb.y1);
297 }
298 }
299
300 static void
301 diff_rects (Rect r1, Rect r2, int *count, Rect result[4])
302 {
303 g_assert (count != NULL);
304 g_assert (result != NULL);
305
306 *count = 0;
307
308 if (rects_intersect (r1, r2)) {
309 diff_rects_guts (r1, r2, count, result);
310 diff_rects_guts (r2, r1, count, result);
311 } else {
312 if (!rect_empty (&r1)) {
313 result[(*count)++] = r1;
314 }
315 if (!rect_empty (&r2)) {
316 result[(*count)++] = r2;
317 }
318 }
319 }
320
321 static Rect
322 make_rect (int x0, int y0, int x1, int y1)
323 {
324 Rect r;
325
326 r.x0 = x0;
327 r.y0 = y0;
328 r.x1 = x1;
329 r.y1 = y1;
330 return r;
331 }
332
333 static void
334 nautilus_selection_canvas_item_update (EelCanvasItem *item,
335 double i2w_dx,
336 double i2w_dy,
337 gint flags)
338 {
339 NautilusSelectionCanvasItem *self;
340 NautilusSelectionCanvasItemDetails *priv;
341 double x1, y1, x2, y2;
342 int cx1, cy1, cx2, cy2;
343 int repaint_rects_count, i;
344 int width_pixels;
345 int width_lt, width_rb;
346 Rect update_rect, repaint_rects[4];
347
348 if (EEL_CANVAS_ITEM_CLASS (nautilus_selection_canvas_item_parent_class)->update)
349 (* EEL_CANVAS_ITEM_CLASS (nautilus_selection_canvas_item_parent_class)->update) (item, i2w_dx, i2w_dy, flags);
350
351 self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
352 priv = self->priv;
353
354 x1 = priv->x1 + i2w_dx;
355 y1 = priv->y1 + i2w_dy;
356 x2 = priv->x2 + i2w_dx;
357 y2 = priv->y2 + i2w_dy;
358
359 eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
360 eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
361
362 update_rect = make_rect (cx1, cy1, cx2+1, cy2+1);
363 diff_rects (update_rect, priv->last_update_rect,
364 &repaint_rects_count, repaint_rects);
365 for (i = 0; i < repaint_rects_count; i++) {
366 eel_canvas_request_redraw (item->canvas,
367 repaint_rects[i].x0, repaint_rects[i].y0,
368 repaint_rects[i].x1, repaint_rects[i].y1);
369 }
370
371 priv->last_update_rect = update_rect;
372
373 if (priv->outline_set) {
374 /* Outline and bounding box */
375 width_pixels = (int) priv->width;
376 width_lt = width_pixels / 2;
377 width_rb = (width_pixels + 1) / 2;
378
379 cx1 -= width_lt;
380 cy1 -= width_lt;
381 cx2 += width_rb;
382 cy2 += width_rb;
383
384 update_rect = make_rect (cx1, cy1, cx2, cy2);
385 request_redraw_borders (item->canvas, &update_rect,
386 (width_lt + width_rb));
387 request_redraw_borders (item->canvas, &priv->last_outline_update_rect,
388 priv->last_outline_update_width);
389 priv->last_outline_update_rect = update_rect;
390 priv->last_outline_update_width = width_lt + width_rb;
391
392 item->x1 = cx1;
393 item->y1 = cy1;
394 item->x2 = cx2+1;
395 item->y2 = cy2+1;
396 } else {
397 item->x1 = cx1;
398 item->y1 = cy1;
399 item->x2 = cx2+1;
400 item->y2 = cy2+1;
401 }
402 }
403
404 static void
405 nautilus_selection_canvas_item_translate (EelCanvasItem *item,
406 double dx,
407 double dy)
408 {
409 NautilusSelectionCanvasItem *self;
410
411 self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
412
413 self->priv->x1 += dx;
414 self->priv->y1 += dy;
415 self->priv->x2 += dx;
416 self->priv->y2 += dy;
417 }
418
419 static void
420 nautilus_selection_canvas_item_bounds (EelCanvasItem *item,
421 double *x1,
422 double *y1,
423 double *x2,
424 double *y2)
425 {
426 NautilusSelectionCanvasItem *self;
427 double hwidth;
428
429 self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
430
431 hwidth = (self->priv->width / item->canvas->pixels_per_unit) / 2.0;
432
433 *x1 = self->priv->x1 - hwidth;
434 *y1 = self->priv->y1 - hwidth;
435 *x2 = self->priv->x2 + hwidth;
436 *y2 = self->priv->y2 + hwidth;
437 }
438
439 #define FADE_OUT_STEPS 5
440 #define FADE_OUT_SPEED 30
441
442 static gboolean
443 fade_and_request_redraw (gpointer user_data)
444 {
445 NautilusSelectionCanvasItem *self = user_data;
446
447 if (self->priv->fade_out_fill_alpha <= 0 ||
448 self->priv->fade_out_outline_alpha <= 0) {
449 self->priv->fade_out_handler_id = 0;
450 eel_canvas_item_destroy (EEL_CANVAS_ITEM (self));
451
452 return FALSE;
453 }
454
455 self->priv->fade_out_fill_alpha -= self->priv->fade_out_fill_delta;
456 self->priv->fade_out_outline_alpha -= self->priv->fade_out_outline_delta;
457
458 eel_canvas_item_request_redraw (EEL_CANVAS_ITEM (self));
459
460 return TRUE;
461 }
462
463 void
464 nautilus_selection_canvas_item_fade_out (NautilusSelectionCanvasItem *self,
465 guint transition_time)
466 {
467 self->priv->fade_out_fill_alpha = self->priv->fill_color.alpha;
468 self->priv->fade_out_outline_alpha = self->priv->outline_color.alpha;
469
470 self->priv->fade_out_fill_delta = self->priv->fade_out_fill_alpha / FADE_OUT_STEPS;
471 self->priv->fade_out_outline_delta = self->priv->fade_out_outline_alpha / FADE_OUT_STEPS;
472
473 self->priv->fade_out_handler_id =
474 g_timeout_add ((guint) (transition_time / FADE_OUT_STEPS),
475 fade_and_request_redraw, self);
476 }
477
478 static void
479 nautilus_selection_canvas_item_dispose (GObject *obj)
480 {
481 NautilusSelectionCanvasItem *self = NAUTILUS_SELECTION_CANVAS_ITEM (obj);
482
483 if (self->priv->fade_out_handler_id != 0) {
484 g_source_remove (self->priv->fade_out_handler_id);
485 self->priv->fade_out_handler_id = 0;
486 }
487
488 G_OBJECT_CLASS (nautilus_selection_canvas_item_parent_class)->dispose (obj);
489 }
490
491 static void
492 do_set_fill (NautilusSelectionCanvasItem *self,
493 gboolean fill_set)
494 {
495 if (self->priv->fill_set != fill_set) {
496 self->priv->fill_set = fill_set;
497 eel_canvas_item_request_update (EEL_CANVAS_ITEM (self));
498 }
499 }
500
501 static void
502 do_set_outline (NautilusSelectionCanvasItem *self,
503 gboolean outline_set)
504 {
505 if (self->priv->outline_set != outline_set) {
506 self->priv->outline_set = outline_set;
507 eel_canvas_item_request_update (EEL_CANVAS_ITEM (self));
508 }
509 }
510
511 static void
512 nautilus_selection_canvas_item_set_property (GObject *object,
513 guint param_id,
514 const GValue *value,
515 GParamSpec *pspec)
516 {
517 EelCanvasItem *item;
518 NautilusSelectionCanvasItem *self;
519
520 self = NAUTILUS_SELECTION_CANVAS_ITEM (object);
521 item = EEL_CANVAS_ITEM (object);
522
523 switch (param_id) {
524 case PROP_X1:
525 self->priv->x1 = g_value_get_double (value);
526
527 eel_canvas_item_request_update (item);
528 break;
529
530 case PROP_Y1:
531 self->priv->y1 = g_value_get_double (value);
532
533 eel_canvas_item_request_update (item);
534 break;
535
536 case PROP_X2:
537 self->priv->x2 = g_value_get_double (value);
538
539 eel_canvas_item_request_update (item);
540 break;
541
542 case PROP_Y2:
543 self->priv->y2 = g_value_get_double (value);
544
545 eel_canvas_item_request_update (item);
546 break;
547
548 case PROP_FILL_COLOR_RGBA: {
549 GdkRGBA *color;
550
551 color = g_value_get_boxed (value);
552
553 do_set_fill (self, color != NULL);
554
555 if (color != NULL) {
556 self->priv->fill_color = *color;
557 }
558
559 eel_canvas_item_request_redraw (item);
560 break;
561 }
562
563 case PROP_OUTLINE_COLOR_RGBA: {
564 GdkRGBA *color;
565
566 color = g_value_get_boxed (value);
567
568 do_set_outline (self, color != NULL);
569
570 if (color != NULL) {
571 self->priv->outline_color = *color;
572 }
573
574 eel_canvas_item_request_redraw (item);
575 break;
576 }
577
578 case PROP_OUTLINE_STIPPLING:
579 self->priv->outline_stippling = g_value_get_boolean (value);
580
581 eel_canvas_item_request_redraw (item);
582 break;
583
584 case PROP_WIDTH_PIXELS:
585 self->priv->width = g_value_get_uint (value);
586
587 eel_canvas_item_request_update (item);
588 break;
589
590 default:
591 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
592 break;
593 }
594 }
595
596 static void
597 nautilus_selection_canvas_item_get_property (GObject *object,
598 guint param_id,
599 GValue *value,
600 GParamSpec *pspec)
601 {
602 NautilusSelectionCanvasItem *self;
603
604 self = NAUTILUS_SELECTION_CANVAS_ITEM (object);
605
606 switch (param_id) {
607 case PROP_X1:
608 g_value_set_double (value, self->priv->x1);
609 break;
610
611 case PROP_Y1:
612 g_value_set_double (value, self->priv->y1);
613 break;
614
615 case PROP_X2:
616 g_value_set_double (value, self->priv->x2);
617 break;
618
619 case PROP_Y2:
620 g_value_set_double (value, self->priv->y2);
621 break;
622
623 case PROP_FILL_COLOR_RGBA:
624 g_value_set_boxed (value, &self->priv->fill_color);
625 break;
626
627 case PROP_OUTLINE_COLOR_RGBA:
628 g_value_set_boxed (value, &self->priv->outline_color);
629 break;
630
631 case PROP_OUTLINE_STIPPLING:
632 g_value_set_boolean (value, self->priv->outline_stippling);
633 break;
634 case PROP_WIDTH_PIXELS:
635 g_value_set_uint (value, self->priv->width);
636 break;
637 default:
638 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
639 break;
640 }
641 }
642
643 static void
644 nautilus_selection_canvas_item_class_init (NautilusSelectionCanvasItemClass *klass)
645 {
646 EelCanvasItemClass *item_class;
647 GObjectClass *gobject_class;
648
649 gobject_class = G_OBJECT_CLASS (klass);
650 item_class = EEL_CANVAS_ITEM_CLASS (klass);
651
652 gobject_class->set_property = nautilus_selection_canvas_item_set_property;
653 gobject_class->get_property = nautilus_selection_canvas_item_get_property;
654 gobject_class->dispose = nautilus_selection_canvas_item_dispose;
655
656 item_class->draw = nautilus_selection_canvas_item_draw;
657 item_class->point = nautilus_selection_canvas_item_point;
658 item_class->update = nautilus_selection_canvas_item_update;
659 item_class->bounds = nautilus_selection_canvas_item_bounds;
660 item_class->translate = nautilus_selection_canvas_item_translate;
661
662 properties[PROP_X1] =
663 g_param_spec_double ("x1", NULL, NULL,
664 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
665 G_PARAM_READWRITE);
666 properties[PROP_Y1] =
667 g_param_spec_double ("y1", NULL, NULL,
668 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
669 G_PARAM_READWRITE);
670 properties[PROP_X2] =
671 g_param_spec_double ("x2", NULL, NULL,
672 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
673 G_PARAM_READWRITE);
674 properties[PROP_Y2] =
675 g_param_spec_double ("y2", NULL, NULL,
676 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
677 G_PARAM_READWRITE);
678 properties[PROP_FILL_COLOR_RGBA] =
679 g_param_spec_boxed ("fill-color-rgba", NULL, NULL,
680 GDK_TYPE_RGBA,
681 G_PARAM_READWRITE);
682 properties[PROP_OUTLINE_COLOR_RGBA] =
683 g_param_spec_boxed ("outline-color-rgba", NULL, NULL,
684 GDK_TYPE_RGBA,
685 G_PARAM_READWRITE);
686 properties[PROP_OUTLINE_STIPPLING] =
687 g_param_spec_boolean ("outline-stippling", NULL, NULL,
688 FALSE, G_PARAM_READWRITE);
689 properties[PROP_WIDTH_PIXELS] =
690 g_param_spec_uint ("width-pixels", NULL, NULL,
691 0, G_MAXUINT, 0,
692 G_PARAM_READWRITE);
693
694 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
695 g_type_class_add_private (klass, sizeof (NautilusSelectionCanvasItemDetails));
696 }
697
698 static void
699 nautilus_selection_canvas_item_init (NautilusSelectionCanvasItem *self)
700 {
701 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NAUTILUS_TYPE_SELECTION_CANVAS_ITEM,
702 NautilusSelectionCanvasItemDetails);
703 }