Accept 1927294: Patch from Gerald Young to allow intuitive editing of the BLinePoint...
[synfig.git] / synfig-studio / trunk / src / gtkmm / renderer_ducks.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file renderer_ducks.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "renderer_ducks.h"
34 #include "workarea.h"
35 #include "duckmatic.h"
36 #include <ETL/bezier>
37 #include <ETL/misc>
38 #include "widget_color.h"
39 #include <synfig/distance.h>
40 #include <synfig/valuenode_blinecalcvertex.h>
41 #include <synfig/valuenode_bline.h>
42 #include "app.h"
43
44 #include "general.h"
45
46 #endif
47
48 /* === U S I N G =========================================================== */
49
50 using namespace std;
51 using namespace etl;
52 using namespace synfig;
53 using namespace studio;
54
55 /* === M A C R O S ========================================================= */
56
57 /* === G L O B A L S ======================================================= */
58
59 /* === P R O C E D U R E S ================================================= */
60
61 /* === M E T H O D S ======================================================= */
62
63 bool
64 restrict_blinevertex_duck(etl::handle<Duck> duck, WorkArea& w_area, synfig::Point *point)
65 {
66         synfig::Point sub_trans_origin(duck->get_sub_trans_origin());
67         etl::handle<Duck> origin_duck = duck->get_origin_duck();
68         bool origin_changed = false;
69         if(origin_duck)
70                 origin_changed = restrict_blinevertex_duck(origin_duck, w_area, &sub_trans_origin);
71
72         if( ValueNode_BLineCalcVertex::Handle bline_vertex =
73                 ValueNode_BLineCalcVertex::Handle::cast_dynamic(duck->get_value_desc().get_value_node())
74         )
75         {
76                 synfig::Point closest_point = duck->get_point();
77                 synfig::Real radius = 0.0;
78                 synfig::find_closest_point( 
79                         ( *bline_vertex->get_link(bline_vertex->get_link_index_from_name("bline")) )( w_area.get_time() ),
80                         duck->get_point(),
81                         radius,
82                         ( *bline_vertex->get_link(bline_vertex->get_link_index_from_name("loop")) )( w_area.get_time() ).get(bool()),
83                         &closest_point
84                 );
85
86                 if(closest_point != duck->get_point())
87                 {
88                         *point = closest_point * duck->get_scalar() + sub_trans_origin;
89                         return true;
90                 }
91         }
92         
93         if(origin_changed)
94         {
95                 *point = duck->get_point() * duck->get_scalar() + sub_trans_origin;
96                 return true;
97         }
98         
99         return false;
100 }
101
102 Renderer_Ducks::~Renderer_Ducks()
103 {
104 }
105
106 /*
107 bool
108 Renderer_Ducks::get_enabled_vfunc()const
109 {
110         return get_work_area()->grid_status();
111 }
112 */
113
114 struct ScreenDuck
115 {
116         synfig::Point pos;
117         Gdk::Color color;
118         bool selected;
119         bool hover;
120         Real width;
121
122         ScreenDuck():width(0) { }
123 };
124
125 void
126 Renderer_Ducks::render_vfunc(
127         const Glib::RefPtr<Gdk::Drawable>& drawable,
128         const Gdk::Rectangle& /*expose_area*/
129 )
130 {
131         assert(get_work_area());
132         if(!get_work_area())
133                 return;
134
135         const synfig::Vector focus_point(get_work_area()->get_focus_point());
136
137
138         int drawable_w,drawable_h;
139         drawable->get_size(drawable_w,drawable_h);
140
141         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(drawable));
142
143
144         const synfig::Vector::value_type window_startx(get_work_area()->get_window_tl()[0]);
145         const synfig::Vector::value_type window_starty(get_work_area()->get_window_tl()[1]);
146
147         const float pw(get_pw()),ph(get_ph());
148
149         const std::list<etl::handle<Duckmatic::Bezier> >& bezier_list(get_work_area()->bezier_list());
150         const bool solid_lines(get_work_area()->solid_lines);
151
152         const std::list<handle<Duckmatic::Stroke> >& stroke_list(get_work_area()->stroke_list());
153
154         Glib::RefPtr<Pango::Layout> layout(Pango::Layout::create(get_work_area()->get_pango_context()));
155
156         // Render the strokes
157         for(std::list<handle<Duckmatic::Stroke> >::const_iterator iter=stroke_list.begin();iter!=stroke_list.end();++iter)
158         {
159                 Point window_start(window_startx,window_starty);
160                 vector<Gdk::Point> points;
161                 std::list<synfig::Point>::iterator iter2;
162                 Point holder;
163
164                 for(iter2=(*iter)->stroke_data->begin();iter2!=(*iter)->stroke_data->end();++iter2)
165                 {
166                         holder=*iter2-window_start;
167                         holder[0]/=pw;holder[1]/=ph;
168                         points.push_back(Gdk::Point(round_to_int(holder[0]),round_to_int(holder[1])));
169                 }
170
171                 gc->set_rgb_fg_color(colorconv_synfig2gdk((*iter)->color));
172                 gc->set_function(Gdk::COPY);
173                 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
174
175                 // Draw the stroke
176                 drawable->draw_lines(gc, Glib::ArrayHandle<Gdk::Point>(points));
177         }
178
179
180
181         // Render the beziers
182         for(std::list<handle<Duckmatic::Bezier> >::const_iterator iter=bezier_list.begin();iter!=bezier_list.end();++iter)
183         {
184                 Point sub_trans_p1((*iter)->p1->get_sub_trans_point());
185                 Point sub_trans_p2((*iter)->p2->get_sub_trans_point());
186                 Point sub_trans_c1((*iter)->c1->get_sub_trans_point());
187                 Point sub_trans_c2((*iter)->c2->get_sub_trans_point());
188
189                 WorkArea* w_area = get_work_area();
190                 restrict_blinevertex_duck((*iter)->p1, *w_area, &sub_trans_p1);
191                 restrict_blinevertex_duck((*iter)->p2, *w_area, &sub_trans_p2);
192                 restrict_blinevertex_duck((*iter)->c1, *w_area, &sub_trans_c1);
193                 restrict_blinevertex_duck((*iter)->c2, *w_area, &sub_trans_c2);
194                 
195                 Point window_start(window_startx,window_starty);
196                 Point p1((*iter)->p1->get_transform_stack().perform(sub_trans_p1)-window_start);
197                 Point p2((*iter)->p2->get_transform_stack().perform(sub_trans_p2)-window_start);
198                 Point c1((*iter)->c1->get_transform_stack().perform(sub_trans_c1)-window_start);
199                 Point c2((*iter)->c2->get_transform_stack().perform(sub_trans_c2)-window_start);
200                 p1[0]/=pw;p1[1]/=ph;
201                 p2[0]/=pw;p2[1]/=ph;
202                 c1[0]/=pw;c1[1]/=ph;
203                 c2[0]/=pw;c2[1]/=ph;
204                 bezier<Point> curve(p1,c1,c2,p2);
205                 vector<Gdk::Point> points;
206
207                 float f;
208                 Point pt;
209                 for(f=0;f<1.0;f+=1.0/17.0)
210                 {
211                         pt=curve(f);
212                         points.push_back(Gdk::Point(round_to_int(pt[0]),round_to_int(pt[1])));
213                 }
214                 points.push_back(Gdk::Point(round_to_int(p2[0]),round_to_int(p2[1])));
215
216                 // Draw the curve
217 /*              if(solid_lines)
218                 {
219                         gc->set_rgb_fg_color(DUCK_COLOR_BEZIER_1);
220                         gc->set_function(Gdk::COPY);
221                         gc->set_line_attributes(3,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
222                         drawable->draw_lines(gc, Glib::ArrayHandle<Gdk::Point>(points));
223                         gc->set_rgb_fg_color(DUCK_COLOR_BEZIER_2);
224                         gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
225                         drawable->draw_lines(gc, Glib::ArrayHandle<Gdk::Point>(points));
226                 }
227                 else
228 */
229                 {
230 //                      gc->set_rgb_fg_color(Gdk::Color("#ffffff"));
231 //                      gc->set_function(Gdk::INVERT);
232 //                      gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
233 //                      drawable->draw_lines(gc, Glib::ArrayHandle<Gdk::Point>(points));
234                         gc->set_rgb_fg_color(DUCK_COLOR_BEZIER_1);
235                         gc->set_function(Gdk::COPY);
236                         gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
237                         drawable->draw_lines(gc, Glib::ArrayHandle<Gdk::Point>(points));
238                         gc->set_rgb_fg_color(DUCK_COLOR_BEZIER_2);
239                         gc->set_line_attributes(1,Gdk::LINE_ON_OFF_DASH,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
240                         drawable->draw_lines(gc, Glib::ArrayHandle<Gdk::Point>(points));
241
242                 }
243         }
244
245
246         const DuckList duck_list(get_work_area()->get_duck_list());
247         //Gtk::StateType state = Gtk::STATE_ACTIVE;
248         Gtk::ShadowType shadow=Gtk::SHADOW_OUT;
249         std::list<ScreenDuck> screen_duck_list;
250         const float radius((abs(pw)+abs(ph))*4);
251
252         etl::handle<Duck> hover_duck(get_work_area()->find_duck(get_work_area()->get_cursor_pos(),radius, get_work_area()->get_type_mask()));
253
254         // Render the ducks
255         for(std::list<handle<Duck> >::const_iterator iter=duck_list.begin();iter!=duck_list.end();++iter)
256         {
257
258                 // If this type of duck has been masked, then skip it
259                 if((*iter)->get_type() && (!(get_work_area()->get_type_mask() & (*iter)->get_type())))
260                         continue;
261
262 //              Real x,y;
263         //      Gdk::Rectangle area;
264                 Point sub_trans_point((*iter)->get_sub_trans_point());
265                 Point sub_trans_origin((*iter)->get_sub_trans_origin());
266                 etl::handle<Duck> origin_duck = (*iter)->get_origin_duck();
267
268                 bool has_connect(false);
269                 if((*iter)->get_tangent() || (*iter)->get_type()&Duck::TYPE_ANGLE)
270                 {
271                         has_connect=true;
272                 }
273                 if((*iter)->get_connect_duck())
274                 {
275                         has_connect=true;
276                         sub_trans_origin = (*iter)->get_connect_duck()->get_sub_trans_point();
277                         origin_duck = (*iter)->get_connect_duck();
278                 }
279
280
281                 if (App::restrict_radius_ducks &&
282                         (*iter)->is_radius())
283                 {
284                         if (sub_trans_point[0] < sub_trans_origin[0])
285                                 sub_trans_point[0] = sub_trans_origin[0];
286                         if (sub_trans_point[1] < sub_trans_origin[1])
287                                 sub_trans_point[1] = sub_trans_origin[1];
288                 }
289
290                 WorkArea* w_area = get_work_area();
291                 restrict_blinevertex_duck((*iter), *w_area, &sub_trans_point);
292                 if(origin_duck)
293                         restrict_blinevertex_duck(origin_duck, *w_area, &sub_trans_origin);
294
295                 Point point((*iter)->get_transform_stack().perform(sub_trans_point));
296                 Point origin((*iter)->get_transform_stack().perform(sub_trans_origin));
297
298                 point[0]=(point[0]-window_startx)/pw;
299                 point[1]=(point[1]-window_starty)/ph;
300
301                 origin[0]=(origin[0]-window_startx)/pw;
302                 origin[1]=(origin[1]-window_starty)/ph;
303
304                 bool selected(get_work_area()->duck_is_selected(*iter));
305                 bool hover(*iter==hover_duck || (*iter)->get_hover());
306
307                 shadow = selected?Gtk::SHADOW_IN:Gtk::SHADOW_OUT;
308
309                 if(get_work_area()->get_selected_value_node())
310                 {
311                         synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
312                         if(value_desc.is_valid() && value_desc.is_value_node() && get_work_area()->get_selected_value_node()==value_desc.get_value_node())
313                         {
314                                 gc->set_function(Gdk::COPY);
315                                 gc->set_rgb_fg_color(DUCK_COLOR_SELECTED);
316                                 //gc->set_line_attributes(1,Gdk::LINE_ON_OFF_DASH,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
317                                 gc->set_line_attributes(2,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
318
319                                 drawable->draw_rectangle(gc,false,
320                                         round_to_int(point[0]-5),
321                                         round_to_int(point[1]-5),
322                                         10,
323                                         10
324                                 );
325                         }
326
327                 }
328
329                 if((*iter)->get_box_duck())
330                 {
331                         Point boxpoint((*iter)->get_box_duck()->get_trans_point());
332                         boxpoint[0]=(boxpoint[0]-window_startx)/pw;
333                         boxpoint[1]=(boxpoint[1]-window_starty)/ph;
334                         Point tl(min(point[0],boxpoint[0]),min(point[1],boxpoint[1]));
335
336                         gc->set_function(Gdk::COPY);
337                         gc->set_rgb_fg_color(DUCK_COLOR_BOX_1);
338                         gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
339                         drawable->draw_rectangle(gc,false,
340                                 round_to_int(tl[0]),
341                                 round_to_int(tl[1]),
342                                 round_to_int(abs(boxpoint[0]-point[0])),
343                                 round_to_int(abs(boxpoint[1]-point[1]))
344                         );
345                         gc->set_function(Gdk::COPY);
346                         gc->set_rgb_fg_color(DUCK_COLOR_BOX_2);
347                         gc->set_line_attributes(1,Gdk::LINE_ON_OFF_DASH,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
348                         drawable->draw_rectangle(gc,false,
349                                 round_to_int(tl[0]),
350                                 round_to_int(tl[1]),
351                                 round_to_int(abs(boxpoint[0]-point[0])),
352                                 round_to_int(abs(boxpoint[1]-point[1]))
353                         );
354                 }
355
356                 ScreenDuck screen_duck;
357                 screen_duck.pos=point;
358                 screen_duck.selected=selected;
359                 screen_duck.hover=hover;
360
361                 if(!(*iter)->get_editable())
362                         screen_duck.color=(DUCK_COLOR_NOT_EDITABLE);
363                 else if((*iter)->get_tangent())
364                         screen_duck.color=((*iter)->get_scalar()<0 ? DUCK_COLOR_TANGENT_1 : DUCK_COLOR_TANGENT_2);
365                 else if((*iter)->get_type()&Duck::TYPE_VERTEX)
366                         screen_duck.color=DUCK_COLOR_VERTEX;
367                 else if((*iter)->get_type()&Duck::TYPE_RADIUS)
368                         screen_duck.color=DUCK_COLOR_RADIUS;
369                 else if((*iter)->get_type()&Duck::TYPE_WIDTH)
370                         screen_duck.color=DUCK_COLOR_WIDTH;
371                 else if((*iter)->get_type()&Duck::TYPE_ANGLE)
372                         screen_duck.color=(DUCK_COLOR_ANGLE);
373                 else
374                         screen_duck.color=DUCK_COLOR_OTHER;
375
376                 screen_duck_list.push_front(screen_duck);
377
378                 if(has_connect)
379                 {
380                         if(solid_lines)
381                         {
382                                 gc->set_line_attributes(3,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
383                                 gc->set_rgb_fg_color(DUCK_COLOR_CONNECT_OUTSIDE);
384                                 gc->set_function(Gdk::COPY);
385                                 drawable->draw_line(gc, (int)origin[0],(int)origin[1],(int)(point[0]),(int)(point[1]));
386                                 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
387                                 gc->set_rgb_fg_color(DUCK_COLOR_CONNECT_INSIDE);
388                                 drawable->draw_line(gc, (int)origin[0],(int)origin[1],(int)(point[0]),(int)(point[1]));
389                         }
390                         else
391                         {
392 //                              gc->set_rgb_fg_color(Gdk::Color("#ffffff"));
393 //                              gc->set_function(Gdk::INVERT);
394 //                              drawable->draw_line(gc, (int)origin[0],(int)origin[1],(int)(point[0]),(int)(point[1]));
395                                 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
396                                 gc->set_rgb_fg_color(DUCK_COLOR_CONNECT_OUTSIDE);
397                                 gc->set_function(Gdk::COPY);
398                                 drawable->draw_line(gc, (int)origin[0],(int)origin[1],(int)(point[0]),(int)(point[1]));
399                                 gc->set_line_attributes(1,Gdk::LINE_ON_OFF_DASH,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
400                                 gc->set_rgb_fg_color(DUCK_COLOR_CONNECT_INSIDE);
401                                 drawable->draw_line(gc, (int)origin[0],(int)origin[1],(int)(point[0]),(int)(point[1]));
402                         }
403                 }
404
405                 if((*iter)->is_radius())
406                 {
407                         const Real mag((point-origin).mag());
408                         const int d(round_to_int(mag*2));
409                         const int x(round_to_int(origin[0]-mag));
410                         const int y(round_to_int(origin[1]-mag));
411
412                         if(solid_lines)
413                         {
414                                 gc->set_rgb_fg_color(Gdk::Color("#000000"));
415                                 gc->set_function(Gdk::COPY);
416                                 gc->set_line_attributes(3,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
417                                 drawable->draw_arc(
418                                         gc,
419                                         false,
420                                         x,
421                                         y,
422                                         d,
423                                         d,
424                                         0,
425                                         360*64
426                                 );
427                                 gc->set_rgb_fg_color(Gdk::Color("#afafaf"));
428                         }
429                         else
430                         {
431                                 gc->set_rgb_fg_color(Gdk::Color("#ffffff"));
432                                 gc->set_function(Gdk::INVERT);
433                         }
434                         gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
435
436                         drawable->draw_arc(
437                                 gc,
438                                 false,
439                                 x,
440                                 y,
441                                 d,
442                                 d,
443                                 0,
444                                 360*64
445                         );
446
447                         if(hover)
448                         {
449                                 Real mag;
450                                 if (App::restrict_radius_ducks)
451                                 {
452                                         Point sub_trans_point((*iter)->get_sub_trans_point());
453                                         Point sub_trans_origin((*iter)->get_sub_trans_origin());
454
455                                         if (sub_trans_point[0] < sub_trans_origin[0])
456                                                 sub_trans_point[0] = sub_trans_origin[0];
457                                         if (sub_trans_point[1] < sub_trans_origin[1])
458                                                 sub_trans_point[1] = sub_trans_origin[1];
459
460                                         Point point((*iter)->get_transform_stack().perform(sub_trans_point));
461                                         Point origin((*iter)->get_transform_stack().perform(sub_trans_origin));
462
463                                         mag = (point-origin).mag();
464                                 }
465                                 else
466                                         mag = ((*iter)->get_trans_point()-(*iter)->get_trans_origin()).mag();
467
468                                 Distance real_mag(mag, Distance::SYSTEM_UNITS);
469                                 real_mag.convert(App::distance_system,get_work_area()->get_rend_desc());
470                                 layout->set_text(real_mag.get_string());
471
472                                 gc->set_rgb_fg_color(DUCK_COLOR_WIDTH_TEXT_1);
473                                 drawable->draw_layout(
474                                         gc,
475                                         round_to_int(point[0])+1+6,
476                                         round_to_int(point[1])+1-8,
477                                         layout
478                                 );
479                                 gc->set_rgb_fg_color(DUCK_COLOR_WIDTH_TEXT_2);
480                                 drawable->draw_layout(
481                                         gc,
482                                         round_to_int(point[0])+6,
483                                         round_to_int(point[1])-8,
484                                         layout
485                                 );
486                         }
487
488                 }
489
490         }
491
492
493         for(;screen_duck_list.size();screen_duck_list.pop_front())
494         {
495                 int radius=4;
496                 int outline=1;
497                 Gdk::Color color(screen_duck_list.front().color);
498
499                 if(!screen_duck_list.front().selected)
500                 {
501                         color.set_red(color.get_red()*2/3);
502                         color.set_green(color.get_green()*2/3);
503                         color.set_blue(color.get_blue()*2/3);
504                 }
505
506                 if(screen_duck_list.front().hover && !screen_duck_list.back().hover && screen_duck_list.size()>1)
507                 {
508                         screen_duck_list.push_back(screen_duck_list.front());
509                         continue;
510                 }
511
512                 if(screen_duck_list.front().hover)
513                 {
514                         radius+=2;
515                         outline++;
516                 }
517
518                 gc->set_function(Gdk::COPY);
519                 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
520                 gc->set_rgb_fg_color(DUCK_COLOR_OUTLINE);
521                 drawable->draw_arc(
522                         gc,
523                         true,
524                         round_to_int(screen_duck_list.front().pos[0]-radius),
525                         round_to_int(screen_duck_list.front().pos[1]-radius),
526                         radius*2,
527                         radius*2,
528                         0,
529                         360*64
530                 );
531
532
533                 gc->set_rgb_fg_color(color);
534
535                 drawable->draw_arc(
536                         gc,
537                         true,
538                         round_to_int(screen_duck_list.front().pos[0]-radius+outline),
539                         round_to_int(screen_duck_list.front().pos[1]-radius+outline),
540                         radius*2-outline*2,
541                         radius*2-outline*2,
542                         0,
543                         360*64
544                 );
545         }
546 }