Applied patch from IL'dar AKHmetgaleev aka AkhIL to allow different qualities of...
[synfig.git] / synfig-studio / trunk / src / gtkmm / workarea.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file workarea.cpp
3 **      \brief Template Header
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright 2006 Yue Shi Lai
10 **      Copyright (c) 2007 Chris Moore
11 **
12 **      This package is free software; you can redistribute it and/or
13 **      modify it under the terms of the GNU General Public License as
14 **      published by the Free Software Foundation; either version 2 of
15 **      the License, or (at your option) any later version.
16 **
17 **      This package is distributed in the hope that it will be useful,
18 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **      General Public License for more details.
21 **      \endlegal
22 */
23 /* ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #include <sigc++/adaptors/hide.h>
35
36 #include "workarea.h"
37 #include "canvasview.h"
38 #include "app.h"
39 #include <gtkmm/window.h>
40 #include <gtkmm/image.h>
41 #include <gtkmm/drawingarea.h>
42 #include <gtkmm/ruler.h>
43 #include <gtkmm/arrow.h>
44 #include <gtkmm/image.h>
45 #include <gtkmm/scrollbar.h>
46 #include <cmath>
47 #include <sigc++/retype_return.h>
48 #include <sigc++/retype.h>
49 #include <sigc++/hide.h>
50 #include <ETL/misc>
51
52 #include <synfig/target_scanline.h>
53 #include <synfig/target_tile.h>
54 #include <synfig/surface.h>
55 #include <synfig/valuenode_composite.h>
56 #include <synfigapp/canvasinterface.h>
57 #include "event_mouse.h"
58 #include "event_layerclick.h"
59 #include "widget_color.h"
60 #include <synfig/distance.h>
61 #include "workarearenderer.h"
62
63 #include "renderer_canvas.h"
64 #include "renderer_grid.h"
65 #include "renderer_guides.h"
66 #include "renderer_timecode.h"
67 #include "renderer_ducks.h"
68 #include "renderer_dragbox.h"
69 #include "renderer_bbox.h"
70 #include "asyncrenderer.h"
71 #include <gtkmm/frame.h>
72
73 #include <synfig/mutex.h>
74
75 #include "general.h"
76
77 #endif
78
79 /* === U S I N G =========================================================== */
80
81 using namespace std;
82 using namespace etl;
83 using namespace synfig;
84 using namespace studio;
85
86 /* === M A C R O S ========================================================= */
87
88 #define RULER_FIX               15
89
90 #ifndef stratof
91 #define stratof(X) (atof((X).c_str()))
92 #define stratoi(X) (atoi((X).c_str()))
93 #endif
94
95
96 /* === G L O B A L S ======================================================= */
97
98 /* === C L A S S E S ======================================================= */
99
100 class studio::WorkAreaTarget : public synfig::Target_Tile
101 {
102 public:
103         WorkArea *workarea;
104         bool low_res;
105         int w,h;
106         int real_tile_w,real_tile_h;
107         //std::vector<Glib::RefPtr<Gdk::Pixbuf> >::iterator tile_iter;
108
109         int twindow_start, twindow_width, twindow_height, twindow_pad;
110         int refresh_id;
111
112         bool onionskin;
113         bool onion_first_tile;
114         int onion_layers;
115
116         std::list<synfig::Time> onion_skin_queue;
117
118         synfig::Mutex mutex;
119
120         void set_onion_skin(bool x)
121         {
122                 onionskin=x;
123
124                 Time time(rend_desc().get_time_start());
125
126                 onion_skin_queue.push_back(time);
127                 //onion_skin_queue.push_back(time-1);
128                 //onion_skin_queue.push_back(time+1);
129
130                 try
131                 {
132                         onion_skin_queue.push_back(
133                                 get_canvas()->keyframe_list().find_prev(
134                                         time
135                                 )->get_time()
136                         );
137                 }
138                 catch(...)
139                 {  }
140
141                 try
142                 {
143                         onion_skin_queue.push_back(
144                                 get_canvas()->keyframe_list().find_next(
145                                         time
146                                 )->get_time()
147                         );
148                 }
149                 catch(...)
150                 {  }
151
152                 onion_layers=onion_skin_queue.size();
153
154                 onion_first_tile=false;
155         }
156 public:
157
158         WorkAreaTarget(WorkArea *workarea,int w, int h):
159                 workarea(workarea),
160                 low_res(workarea->get_low_resolution_flag()),
161                 w(w),
162                 h(h),
163                 real_tile_w(workarea->tile_w),
164                 real_tile_h(workarea->tile_h),
165                 refresh_id(workarea->refreshes),
166                 onionskin(false),
167                 onion_layers(0)
168         {
169                 //set_remove_alpha();
170                 //set_avoid_time_sync();
171                 set_clipping(true);
172                 if(low_res)
173                 {
174
175                         int div = 1 << workarea->get_lowrespixel();
176                         set_tile_w(workarea->tile_w/div);
177                         set_tile_h(workarea->tile_h/div);
178                 }
179                 else
180                 {
181                         set_tile_w(workarea->tile_w);
182                         set_tile_h(workarea->tile_h);
183                 }
184                 set_canvas(workarea->get_canvas());
185                 set_quality(workarea->get_quality());
186         }
187
188         ~WorkAreaTarget()
189         {
190                 workarea->queue_draw();
191         }
192
193         virtual bool set_rend_desc(synfig::RendDesc *newdesc)
194         {
195                 assert(workarea);
196                 newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
197                 if(low_res) {
198                         int div = 1 << workarea->get_lowrespixel();
199                         newdesc->set_wh(w/div,h/div);
200                 }
201                 else
202                         newdesc->set_wh(w,h);
203
204                 if(
205                                 workarea->get_w()!=w
206                         ||      workarea->get_h()!=h
207                 ) workarea->set_wh(w,h,4);
208
209                 workarea->full_frame=false;
210
211                 desc=*newdesc;
212                 return true;
213         }
214
215         virtual int total_tiles()const
216         {
217                 int tw(rend_desc().get_w()/get_tile_w());
218                 int th(rend_desc().get_h()/get_tile_h());
219                 if(rend_desc().get_w()%get_tile_w()!=0)tw++;
220                 if(rend_desc().get_h()%get_tile_h()!=0)th++;
221                 return tw*th;
222         }
223
224         virtual int next_frame(Time& time)
225         {
226                 synfig::Mutex::Lock lock(mutex);
227
228                 if(!onionskin)
229                         return synfig::Target_Tile::next_frame(time);
230
231                 onion_first_tile=(onion_layers==(signed)onion_skin_queue.size());
232
233                 if(!onion_skin_queue.empty())
234                 {
235                         time=onion_skin_queue.front();
236                         onion_skin_queue.pop_front();
237                 }
238                 else
239                         return 0;
240
241                 return onion_skin_queue.size()+1;
242         }
243
244         virtual int next_tile(int& x, int& y)
245         {
246                 synfig::Mutex::Lock lock(mutex);
247                 //if(workarea->tile_queue.empty()) return 0;
248
249                 //int curr_tile(workarea->tile_queue.front());
250                 //workarea->tile_queue.pop_front();
251                 int curr_tile(workarea->next_unrendered_tile(refresh_id-onion_skin_queue.size()));
252                 if(curr_tile<0)
253                         return 0;
254
255                 // Width of the image(in tiles)
256                 int tw(rend_desc().get_w()/get_tile_w());
257                 if(rend_desc().get_w()%get_tile_w()!=0)tw++;
258
259                 y=(curr_tile/tw)*get_tile_w();
260                 x=(curr_tile%tw)*get_tile_h();
261
262                 // Mark this tile as "up-to-date"
263                 if(onionskin)
264                         workarea->tile_book[curr_tile].second=refresh_id-onion_skin_queue.size();
265                 else
266                         workarea->tile_book[curr_tile].second=refresh_id;
267
268                 return total_tiles()-curr_tile+1;
269         }
270
271
272         virtual bool start_frame(synfig::ProgressCallback */*cb*/)
273         {
274                 synfig::Mutex::Lock lock(mutex);
275
276                 int tw(rend_desc().get_w()/get_tile_w());
277                 if(rend_desc().get_w()%get_tile_w()!=0)tw++;
278                 int th(rend_desc().get_h()/get_tile_h());
279                 if(rend_desc().get_h()%get_tile_h()!=0)th++;
280
281                 twindow_start=0;
282                 twindow_width=tw;
283                 twindow_height=th;
284                 twindow_pad=0;
285
286                 workarea->tile_book.resize(total_tiles());
287                 //tile_iter=workarea->tile_book.begin()+twindow_start;
288                 return true;
289         }
290
291         static void free_buff(const guint8 *x) { free(const_cast<guint8*>(x)); }
292
293         virtual bool add_tile(const synfig::Surface &surface, int x, int y)
294         {
295                 synfig::Mutex::Lock lock(mutex);
296                 assert(surface);
297
298                 PixelFormat pf(PF_RGB);
299
300                 const int total_bytes(get_tile_w()*get_tile_h()*synfig::channels(pf));
301
302                 unsigned char *buffer((unsigned char*)malloc(total_bytes));
303
304                 if(!surface || !buffer)
305                         return false;
306                 {
307                         unsigned char *dest(buffer);
308                         const Color *src(surface[0]);
309                         int w(get_tile_w());
310                         int h(get_tile_h());
311                         int x(surface.get_w()*surface.get_h());
312                         //if(low_res) { w/=2,h/=2; }
313                         Color dark(0.6,0.6,0.6);
314                         Color lite(0.8,0.8,0.8);
315                         for(int i=0;i<x;i++)
316                                 dest=Color2PixelFormat(
317                                         Color::blend(
318                                                 (*(src++)),
319                                                 ((i/surface.get_w())*8/h+(i%surface.get_w())*8/w)&1?dark:lite,
320                                                 1.0f
321                                         ).clamped(),
322                                         pf,dest,App::gamma
323                                 );
324                 }
325
326                 x/=get_tile_w();
327                 y/=get_tile_h();
328                 int tw(rend_desc().get_w()/get_tile_w());
329                 if(rend_desc().get_w()%get_tile_w()!=0)tw++;
330                 unsigned int index=y*tw+x;
331
332                 // Sanity check
333                 if(index>workarea->tile_book.size())
334                         return false;
335
336                 Glib::RefPtr<Gdk::Pixbuf> pixbuf;
337
338                 pixbuf=Gdk::Pixbuf::create_from_data(
339                         buffer, // pointer to the data
340                         Gdk::COLORSPACE_RGB, // the colorspace
341                         ((pf&PF_A)==PF_A), // has alpha?
342                         8, // bits per sample
343                         surface.get_w(),        // width
344                         surface.get_h(),        // height
345                         surface.get_w()*synfig::channels(pf), // stride (pitch)
346                         sigc::ptr_fun(&WorkAreaTarget::free_buff)
347                 );
348
349                 if(low_res)
350                 {
351                         // We need to scale up
352
353                         int div = 1 << workarea->get_lowrespixel();
354                         pixbuf=pixbuf->scale_simple(
355                                 surface.get_w()*div,
356                                 surface.get_h()*div,
357                                 Gdk::INTERP_NEAREST
358                         );
359                 }
360
361                 if(!onionskin || onion_first_tile || !workarea->tile_book[index].first)
362                 {
363                         workarea->tile_book[index].first=pixbuf;
364                 }
365                 else
366                 {
367                         pixbuf->composite(
368                                 workarea->tile_book[index].first, // Dest
369                                 0,//int dest_x
370                                 0,//int dest_y
371                                 pixbuf->get_width(), // dest width
372                                 pixbuf->get_height(), // dest_height,
373                                 0, // double offset_x
374                                 0, // double offset_y
375                                 1, // double scale_x
376                                 1, // double scale_y
377                                 Gdk::INTERP_NEAREST, // interp
378                                 255/(onion_layers-onion_skin_queue.size()+1) //int overall_alpha
379                         );
380                 }
381
382                 //if(index%2)
383                         workarea->queue_draw();
384                 assert(workarea->tile_book[index].first);
385                 return true;
386         }
387
388         virtual void end_frame()
389         {
390                 //workarea->queue_draw();
391         }
392 };
393
394
395 class studio::WorkAreaTarget_Full : public synfig::Target_Scanline
396 {
397 public:
398         WorkArea *workarea;
399         bool low_res;
400         int w,h;
401         int real_tile_w,real_tile_h;
402         //std::vector<Glib::RefPtr<Gdk::Pixbuf> >::iterator tile_iter;
403
404         int twindow_start, twindow_width, twindow_height, twindow_pad;
405         int refresh_id;
406
407         bool onionskin;
408         bool onion_first_tile;
409         int onion_layers;
410
411         Surface surface;
412
413         std::list<synfig::Time> onion_skin_queue;
414
415         void set_onion_skin(bool x)
416         {
417                 onionskin=x;
418
419                 Time time(rend_desc().get_time_start());
420
421                 onion_skin_queue.push_back(time);
422                 //onion_skin_queue.push_back(time-1);
423                 //onion_skin_queue.push_back(time+1);
424                 if(!onionskin)
425                         return;
426
427                 try
428                 {
429                         onion_skin_queue.push_back(
430                                 get_canvas()->keyframe_list().find_prev(
431                                         time
432                                 )->get_time()
433                         );
434                 }
435                 catch(...)
436                 {  }
437
438                 try
439                 {
440                         onion_skin_queue.push_back(
441                                 get_canvas()->keyframe_list().find_next(
442                                         time
443                                 )->get_time()
444                         );
445                 }
446                 catch(...)
447                 {  }
448
449                 onion_layers=onion_skin_queue.size();
450
451                 onion_first_tile=false;
452         }
453 public:
454
455         WorkAreaTarget_Full(WorkArea *workarea,int w, int h):
456                 workarea(workarea),
457                 low_res(workarea->get_low_resolution_flag()),
458                 w(w),
459                 h(h),
460                 refresh_id(workarea->refreshes),
461                 onionskin(false),
462                 onion_layers(0)
463         {
464                 set_canvas(workarea->get_canvas());
465                 set_quality(workarea->get_quality());
466         }
467
468         ~WorkAreaTarget_Full()
469         {
470         }
471
472         virtual bool set_rend_desc(synfig::RendDesc *newdesc)
473         {
474                 assert(workarea);
475                 newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
476                 if(low_res)
477                         newdesc->set_wh(w/2,h/2);
478                 else
479                         newdesc->set_wh(w,h);
480
481                 if(
482                                 workarea->get_w()!=w
483                         ||      workarea->get_h()!=h
484                 ) workarea->set_wh(w,h,4);
485
486                 surface.set_wh(newdesc->get_w(),newdesc->get_h());
487
488                 desc=*newdesc;
489                 workarea->full_frame=true;
490                 workarea->tile_book.resize(1);
491                 return true;
492         }
493
494         virtual int next_frame(Time& time)
495         {
496                 // Mark this tile as "up-to-date"
497                 if(onionskin)
498                         workarea->tile_book[0].second=refresh_id-onion_skin_queue.size();
499                 else
500                         workarea->tile_book[0].second=refresh_id;
501
502                 if(!onionskin)
503                         return synfig::Target_Scanline::next_frame(time);
504
505                 onion_first_tile=(onion_layers==(signed)onion_skin_queue.size());
506
507                 if(!onion_skin_queue.empty())
508                 {
509                         time=onion_skin_queue.front();
510                         onion_skin_queue.pop_front();
511                 }
512                 else
513                         return 0;
514                 return onion_skin_queue.size()+1;
515         }
516
517
518         virtual bool start_frame(synfig::ProgressCallback */*cb*/)
519         {
520                 return true;
521         }
522
523         virtual Color * start_scanline(int scanline)
524         {
525                 return surface[scanline];
526         }
527
528         virtual bool end_scanline()
529         {
530                 return true;
531         }
532
533         static void free_buff(const guint8 *x) { free(const_cast<guint8*>(x)); }
534
535         virtual void end_frame()
536         {
537                 assert(surface);
538
539                 PixelFormat pf(PF_RGB);
540
541                 const int total_bytes(surface.get_w()*surface.get_h()*synfig::channels(pf));
542
543                 unsigned char *buffer((unsigned char*)malloc(total_bytes));
544
545                 if(!surface || !buffer)
546                         return;
547                 {
548                         unsigned char *dest(buffer);
549                         const Color *src(surface[0]);
550                         int w(surface.get_w());
551                         //int h(surface.get_h());
552                         int x(surface.get_w()*surface.get_h());
553                         //if(low_res) { w/=2,h/=2; }
554                         Color dark(0.6,0.6,0.6);
555                         Color lite(0.8,0.8,0.8);
556                         int tw=workarea->tile_w;
557                         int th=workarea->tile_h;
558                         if(low_res)
559                         {
560                                 tw/=2;
561                                 th/=2;
562                         }
563                         for(int i=0;i<x;i++)
564                                 dest=Color2PixelFormat(
565                                         Color::blend(
566                                                 (*(src++)),
567                                                 ((i/w)*8/th+(i%w)*8/tw)&1?dark:lite,
568                                                 1.0f
569                                         ).clamped(),
570                                         pf,
571                                         dest,
572                                         App::gamma
573                                 );
574                 }
575
576                 Glib::RefPtr<Gdk::Pixbuf> pixbuf;
577
578                 pixbuf=Gdk::Pixbuf::create_from_data(
579                         buffer, // pointer to the data
580                         Gdk::COLORSPACE_RGB, // the colorspace
581                         ((pf&PF_A)==PF_A), // has alpha?
582                         8, // bits per sample
583                         surface.get_w(),        // width
584                         surface.get_h(),        // height
585                         surface.get_w()*synfig::channels(pf), // stride (pitch)
586                         sigc::ptr_fun(&WorkAreaTarget::free_buff)
587                 );
588
589                 if(low_res)
590                 {
591                         // We need to scale up
592                         pixbuf=pixbuf->scale_simple(
593                                 surface.get_w()*2,
594                                 surface.get_h()*2,
595                                 Gdk::INTERP_NEAREST
596                         );
597                 }
598
599                 int index=0;
600
601                 if(!onionskin || onion_first_tile || !workarea->tile_book[index].first)
602                 {
603                         workarea->tile_book[index].first=pixbuf;
604                 }
605                 else
606                 {
607                         pixbuf->composite(
608                                 workarea->tile_book[index].first, // Dest
609                                 0,//int dest_x
610                                 0,//int dest_y
611                                 pixbuf->get_width(), // dest width
612                                 pixbuf->get_height(), // dest_height,
613                                 0, // double offset_x
614                                 0, // double offset_y
615                                 1, // double scale_x
616                                 1, // double scale_y
617                                 Gdk::INTERP_NEAREST, // interp
618                                 255/(onion_layers-onion_skin_queue.size()+1) //int overall_alpha
619                         );
620                 }
621
622                 workarea->queue_draw();
623                 assert(workarea->tile_book[index].first);
624         }
625 };
626
627
628
629
630 /* === M E T H O D S ======================================================= */
631
632
633 WorkArea::WorkArea(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface):
634         Gtk::Table(4+RULER_FIX, 3, false),
635         canvas_interface(canvas_interface),
636         canvas(canvas_interface->get_canvas()),
637         scrollx_adjustment(0,-4,4,0.01,0.1),
638         scrolly_adjustment(0,-4,4,0.01,0.1),
639         w(TILE_SIZE),
640         h(TILE_SIZE),
641         last_event_time(0),
642         progresscallback(0),
643         dragging(DRAG_NONE),
644         show_grid(false),
645         tile_w(TILE_SIZE),
646         tile_h(TILE_SIZE),
647         timecode_width(0),
648         timecode_height(0)
649 {
650         show_guides=true;
651         curr_input_device=0;
652         full_frame=false;
653         allow_duck_clicks=true;
654         allow_layer_clicks=true;
655         render_idle_func_id=0;
656         zoom=prev_zoom=1.0;
657         quality=10;
658         lowrespixel=1;
659         rendering=false;
660         canceled_=false;
661         low_resolution=true;
662         pw=0.001;
663         ph=0.001;
664         last_focus_point=Point(0,0);
665         onion_skin=false;
666         queued=false;
667         dirty_trap_enabled=false;
668         solid_lines=true;
669
670         dirty_trap_queued=0;
671
672         meta_data_lock=false;
673
674         insert_renderer(new Renderer_Canvas,    000);
675         insert_renderer(new Renderer_Grid,              100);
676         insert_renderer(new Renderer_Guides,    200);
677         insert_renderer(new Renderer_Ducks,             300);
678         insert_renderer(new Renderer_BBox,              399);
679         insert_renderer(new Renderer_Dragbox,   400);
680         insert_renderer(new Renderer_Timecode,  500);
681
682         signal_duck_selection_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::queue_draw));
683         signal_strokes_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::queue_draw));
684         signal_grid_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::queue_draw));
685         signal_grid_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::save_meta_data));
686         signal_sketch_saved().connect(sigc::mem_fun(*this,&studio::WorkArea::save_meta_data));
687
688         // Not that it really makes a difference... (setting this to zero, that is)
689         refreshes=0;
690
691         drawing_area=manage(new class Gtk::DrawingArea());
692         drawing_area->show();
693         drawing_area->set_extension_events(Gdk::EXTENSION_EVENTS_ALL);
694
695         drawing_frame=manage(new Gtk::Frame);
696         drawing_frame->add(*drawing_area);
697         //drawing_frame->set_shadow_type(Gtk::SHADOW_NONE);
698         //drawing_frame->property_border_width()=5;
699         //drawing_frame->modify_fg(Gtk::STATE_NORMAL,Gdk::Color("#00ffff"));
700         //drawing_frame->modify_base(Gtk::STATE_NORMAL,Gdk::Color("#ff00ff"));
701         /*drawing_frame->modify_fg(Gtk::STATE_ACTIVE,Gdk::Color("#00ffff"));
702         drawing_frame->modify_base(Gtk::STATE_ACTIVE,Gdk::Color("#ff00ff"));
703         drawing_frame->modify_bg(Gtk::STATE_ACTIVE,Gdk::Color("#00ff00"));
704         drawing_frame->modify_fg(Gtk::STATE_INSENSITIVE,Gdk::Color("#00ffff"));
705         drawing_frame->modify_base(Gtk::STATE_INSENSITIVE,Gdk::Color("#ff00ff"));
706         drawing_frame->modify_bg(Gtk::STATE_INSENSITIVE,Gdk::Color("#00ff00"));
707         drawing_frame->modify_fg(Gtk::STATE_SELECTED,Gdk::Color("#00ffff"));
708         drawing_frame->modify_base(Gtk::STATE_SELECTED,Gdk::Color("#ff00ff"));
709         drawing_frame->modify_bg(Gtk::STATE_SELECTED,Gdk::Color("#00ff00"));
710         */
711         //drawing_frame->set_state(Gtk::STATE_NORMAL);
712
713         drawing_frame->show();
714
715         attach(*drawing_frame, 1, 3+RULER_FIX, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
716
717         Gtk::IconSize iconsize=Gtk::IconSize::from_name("synfig-small_icon");
718
719
720         // Create the vertical and horizontal rulers
721         vruler = manage(new class Gtk::VRuler());
722         hruler = manage(new class Gtk::HRuler());
723         vruler->set_metric(Gtk::PIXELS);
724         hruler->set_metric(Gtk::PIXELS);
725         vruler->show();
726         hruler->show();
727         attach(*vruler, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
728         attach(*hruler, 1, 3+RULER_FIX, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
729         hruler->signal_event().connect(sigc::mem_fun(*this, &WorkArea::on_hruler_event));
730         vruler->signal_event().connect(sigc::mem_fun(*this, &WorkArea::on_vruler_event));
731         hruler->add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON2_MOTION_MASK |Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
732         vruler->add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON2_MOTION_MASK |Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
733
734         // Create the menu button
735         menubutton=manage(new class Gtk::Button());
736         Gtk::Arrow *arrow1 = manage(new class Gtk::Arrow(Gtk::ARROW_RIGHT, Gtk::SHADOW_OUT));
737         menubutton->add(*arrow1);
738         menubutton->show_all();
739         menubutton->signal_pressed().connect(sigc::mem_fun(*this, &WorkArea::popup_menu));
740         attach(*menubutton, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
741
742
743
744         Gtk::VScrollbar *vscrollbar1 = manage(new class Gtk::VScrollbar(*get_scrolly_adjustment()));
745         Gtk::HScrollbar *hscrollbar1 = manage(new class Gtk::HScrollbar(*get_scrollx_adjustment()));
746         vscrollbar1->show();
747         hscrollbar1->show();
748         attach(*vscrollbar1, 3+RULER_FIX, 4+RULER_FIX, 1, 2, Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
749         attach(*hscrollbar1, 2+RULER_FIX, 3+RULER_FIX, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0);
750
751         ZoomDial *zoomdial=manage(new class ZoomDial(iconsize));
752         zoomdial->signal_zoom_in().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_in));
753         zoomdial->signal_zoom_out().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_out));
754         zoomdial->signal_zoom_fit().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_fit));
755         zoomdial->signal_zoom_norm().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_norm));
756         zoomdial->show();
757         attach(*zoomdial, 0, 2+RULER_FIX, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
758
759         drawing_area->add_events(Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
760         add_events(Gdk::KEY_PRESS_MASK);
761         drawing_area->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
762         drawing_area->add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON2_MOTION_MASK |Gdk::POINTER_MOTION_MASK);
763
764         // ----------------- Attach signals
765
766         drawing_area->signal_expose_event().connect(sigc::mem_fun(*this, &WorkArea::refresh));
767         drawing_area->signal_event().connect(sigc::mem_fun(*this, &WorkArea::on_drawing_area_event));
768         drawing_area->signal_size_allocate().connect(sigc::hide(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info)));
769
770
771
772         canvas_interface->signal_rend_desc_changed().connect(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info));
773         // When either of the scrolling adjustments change, then redraw.
774         get_scrollx_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::queue_scroll));
775         get_scrolly_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::queue_scroll));
776         get_scrollx_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info));
777         get_scrolly_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info));
778
779         get_canvas()->signal_meta_data_changed("grid_size").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
780         get_canvas()->signal_meta_data_changed("grid_snap").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
781         get_canvas()->signal_meta_data_changed("grid_show").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
782         get_canvas()->signal_meta_data_changed("guide_show").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
783         get_canvas()->signal_meta_data_changed("guide_x").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
784         get_canvas()->signal_meta_data_changed("guide_y").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
785         get_canvas()->signal_meta_data_changed("onion_skin").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
786         get_canvas()->signal_meta_data_changed("guide_snap").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
787         get_canvas()->signal_meta_data_changed("sketch").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
788         get_canvas()->signal_meta_data_changed("solid_lines").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
789
790         queued=false;
791         meta_data_lock=false;
792         set_focus_point(Point(0,0));
793
794
795         load_meta_data();
796         // Load sketch
797         {
798                 String data(canvas->get_meta_data("sketch"));
799                 if(!data.empty())
800                 {
801                         if(!load_sketch(data))
802                                 load_sketch(dirname(canvas->get_file_name())+ETL_DIRECTORY_SEPARATOR+basename(data));
803                 }
804         }
805
806         hruler->property_max_size()=double(10.0);
807         vruler->property_max_size()=double(10.0);
808
809         drawing_area->set_flags(drawing_area->get_flags()|Gtk::CAN_FOCUS);
810 }
811
812 WorkArea::~WorkArea()
813 {
814 //      delete [] buffer;
815
816         // don't leave the render function queued if we are about to vanish;
817         // that causes crashes
818         if(render_idle_func_id)
819                 render_idle_func_id=0;
820 }
821
822 bool
823 WorkArea::get_updating()const
824 {
825         return App::single_threaded && async_renderer && async_renderer->updating;
826 }
827
828 void
829 WorkArea::stop_updating(bool cancel)
830 {
831         async_renderer->stop();
832         if (cancel) canceled_=true;
833 }
834
835 void
836 WorkArea::save_meta_data()
837 {
838         if(meta_data_lock)
839                 return;
840         meta_data_lock=true;
841
842         Vector s(get_grid_size());
843         canvas_interface->set_meta_data("grid_size",strprintf("%f %f",s[0],s[1]));
844         canvas_interface->set_meta_data("grid_snap",get_grid_snap()?"1":"0");
845         canvas_interface->set_meta_data("guide_snap",get_guide_snap()?"1":"0");
846         canvas_interface->set_meta_data("guide_show",get_show_guides()?"1":"0");
847         canvas_interface->set_meta_data("grid_show",show_grid?"1":"0");
848         canvas_interface->set_meta_data("onion_skin",onion_skin?"1":"0");
849         {
850                 String data;
851                 GuideList::const_iterator iter;
852                 for(iter=get_guide_list_x().begin();iter!=get_guide_list_x().end();++iter)
853                 {
854                         if(!data.empty())
855                                 data+=' ';
856                         data+=strprintf("%f",*iter);
857                 }
858                 if(!data.empty())
859                         canvas_interface->set_meta_data("guide_x",data);
860
861                 data.clear();
862                 for(iter=get_guide_list_y().begin();iter!=get_guide_list_y().end();++iter)
863                 {
864                         if(!data.empty())
865                                 data+=' ';
866                         data+=strprintf("%f",*iter);
867                 }
868                 if(!data.empty())
869                         canvas_interface->set_meta_data("guide_y",data);
870         }
871
872         if(get_sketch_filename().size())
873         {
874                 if(dirname(canvas->get_file_name())==dirname(get_sketch_filename()))
875                         canvas_interface->set_meta_data("sketch",basename(get_sketch_filename()));
876                 else
877                         canvas_interface->set_meta_data("sketch",get_sketch_filename());
878         }
879
880         meta_data_lock=false;
881 }
882
883 void
884 WorkArea::load_meta_data()
885 {
886         if(meta_data_lock)
887                 return;
888         meta_data_lock=true;
889
890         String data;
891
892         data=canvas->get_meta_data("grid_size");
893         if(!data.empty())
894         {
895                 float gx(get_grid_size()[0]),gy(get_grid_size()[1]);
896
897                 String::iterator iter(find(data.begin(),data.end(),' '));
898                 String tmp(data.begin(),iter);
899
900                 if(!tmp.empty())
901                         gx=stratof(tmp);
902                 else
903                         synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"grid_size\", which was \"%s\"",data.c_str());
904
905                 if(iter==data.end())
906                         tmp.clear();
907                 else
908                         tmp=String(iter+1,data.end());
909
910                 if(!tmp.empty())
911                         gy=stratof(tmp);
912                 else
913                         synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"grid_size\", which was \"%s\"",data.c_str());
914
915                 set_grid_size(Vector(gx,gy));
916         }
917
918         data=canvas->get_meta_data("grid_show");
919         if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
920                 show_grid=true;
921         if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
922                 show_grid=false;
923
924         data=canvas->get_meta_data("solid_lines");
925         if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
926                 solid_lines=true;
927         if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
928                 solid_lines=false;
929
930         data=canvas->get_meta_data("guide_show");
931         if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
932                 show_guides=true;
933         if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
934                 show_guides=false;
935
936         data=canvas->get_meta_data("grid_snap");
937         if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
938                 set_grid_snap(true);
939         if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
940                 set_grid_snap(false);
941
942         data=canvas->get_meta_data("guide_snap");
943         if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
944                 set_guide_snap(true);
945         if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
946                 set_guide_snap(false);
947
948         data=canvas->get_meta_data("onion_skin");
949         if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
950                 set_onion_skin(true);
951         if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
952                 set_onion_skin(false);
953
954         data=canvas->get_meta_data("guide_x");
955         get_guide_list_x().clear();
956         while(!data.empty())
957         {
958                 String::iterator iter(find(data.begin(),data.end(),' '));
959                 String guide(data.begin(),iter);
960
961                 if(!guide.empty())
962                         get_guide_list_x().push_back(stratof(guide));
963
964                 if(iter==data.end())
965                         data.clear();
966                 else
967                         data=String(iter+1,data.end());
968         }
969         //sort(get_guide_list_x());
970
971         data=canvas->get_meta_data("guide_y");
972         get_guide_list_y().clear();
973         while(!data.empty())
974         {
975                 String::iterator iter(find(data.begin(),data.end(),' '));
976                 String guide(data.begin(),iter);
977
978                 if(!guide.empty())
979                         get_guide_list_y().push_back(stratof(guide));
980
981                 if(iter==data.end())
982                         data.clear();
983                 else
984                         data=String(iter+1,data.end());
985         }
986         //sort(get_guide_list_y());
987
988         meta_data_lock=false;
989         queue_draw();
990 }
991
992 void
993 WorkArea::set_onion_skin(bool x)
994 {
995         if(onion_skin==x)
996                 return;
997         onion_skin=x;
998         save_meta_data();
999         queue_render_preview();
1000         signal_onion_skin_changed()();
1001 }
1002
1003 bool
1004 WorkArea::get_onion_skin()const
1005 {
1006         return onion_skin;
1007 }
1008
1009 void
1010 WorkArea::enable_grid()
1011 {
1012         show_grid=true;
1013         save_meta_data();
1014         queue_draw();
1015 }
1016
1017 void
1018 WorkArea::disable_grid()
1019 {
1020         show_grid=false;
1021         save_meta_data();
1022         queue_draw();
1023 }
1024
1025 void
1026 WorkArea::set_show_guides(bool x)
1027 {
1028         show_guides=x;
1029         save_meta_data();
1030         queue_draw();
1031 }
1032
1033 void
1034 WorkArea::toggle_grid()
1035 {
1036         show_grid=!show_grid;
1037         save_meta_data();
1038         queue_draw();
1039 }
1040
1041 void
1042 WorkArea::set_low_resolution_flag(bool x)
1043 {
1044         if(x!=low_resolution)
1045         {
1046                 low_resolution=x;
1047                 queue_render_preview();
1048         }
1049 }
1050
1051 void
1052 WorkArea::toggle_low_resolution_flag()
1053 {
1054         set_low_resolution_flag(!get_low_resolution_flag());
1055 }
1056
1057 void
1058 WorkArea::popup_menu()
1059 {
1060         signal_popup_menu()();
1061 }
1062
1063 void
1064 WorkArea::set_grid_size(const synfig::Vector &s)
1065 {
1066         Duckmatic::set_grid_size(s);
1067         save_meta_data();
1068         queue_draw();
1069 }
1070
1071 void
1072 WorkArea::set_focus_point(const synfig::Point &point)
1073 {
1074         // These next three lines try to ensure that we place the
1075         // focus on a pixel boundary
1076         /*Point adjusted(point[0]/abs(get_pw()),point[1]/abs(get_ph()));
1077         adjusted[0]=(abs(adjusted[0]-floor(adjusted[0]))<0.5)?floor(adjusted[0])*abs(get_pw()):ceil(adjusted[0])*abs(get_ph());
1078         adjusted[1]=(abs(adjusted[1]-floor(adjusted[1]))<0.5)?floor(adjusted[1])*abs(get_ph()):ceil(adjusted[1])*abs(get_ph());
1079         */
1080         const synfig::Point& adjusted(point);
1081
1082         synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1083         Real x_factor=(rend_desc.get_br()[0]-rend_desc.get_tl()[0]>0)?-1:1;
1084         Real y_factor=(rend_desc.get_br()[1]-rend_desc.get_tl()[1]>0)?-1:1;
1085
1086         get_scrollx_adjustment()->set_value(adjusted[0]*x_factor);
1087         get_scrolly_adjustment()->set_value(adjusted[1]*y_factor);
1088 }
1089
1090 synfig::Point
1091 WorkArea::get_focus_point()const
1092 {
1093         synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1094         Real x_factor=(rend_desc.get_br()[0]-rend_desc.get_tl()[0]>0)?-1:1;
1095         Real y_factor=(rend_desc.get_br()[1]-rend_desc.get_tl()[1]>0)?-1:1;
1096
1097         return synfig::Point(get_scrollx_adjustment()->get_value()*x_factor, get_scrolly_adjustment()->get_value()*y_factor);
1098 }
1099
1100 bool
1101 WorkArea::set_wh(int W, int H,int CHAN)
1102 {
1103         // If our size is already set, don't set it again
1104         if(W==w && H==h && CHAN==bpp)
1105         {
1106                 return true;
1107         }
1108         if(W<=0 || H<=0 || CHAN<=0)
1109                 return false;
1110
1111         assert(W>0);
1112         assert(H>0);
1113         assert(CHAN>0);
1114
1115         // Set all of the parameters
1116         w=W;
1117         h=H;
1118         bpp=CHAN;
1119
1120         refresh_dimension_info();
1121
1122         tile_book.clear();
1123
1124         return true;
1125 }
1126
1127 bool
1128 WorkArea::on_key_press_event(GdkEventKey* event)
1129 {
1130         if(get_selected_ducks().empty())
1131                 return false;
1132
1133         Real multiplier(1.0);
1134
1135         if(Gdk::ModifierType(event->state)&GDK_SHIFT_MASK)
1136                 multiplier=10.0;
1137
1138         Vector nudge;
1139         switch(event->keyval)
1140         {
1141                 case GDK_Left:
1142                         nudge=Vector(-pw,0);
1143                         break;
1144                 case GDK_Right:
1145                         nudge=Vector(pw,0);
1146                         break;
1147                 case GDK_Up:
1148                         nudge=Vector(0,-ph);
1149                         break;
1150                 case GDK_Down:
1151                         nudge=Vector(0,ph);
1152                         break;
1153                 default:
1154                         return false;
1155                         break;
1156         }
1157
1158         synfigapp::Action::PassiveGrouper grouper(instance.get(),"Nudge");
1159
1160         // Grid snap does not apply to nudging
1161         bool grid_snap_holder(get_grid_snap());
1162         bool guide_snap_holder(get_guide_snap());
1163         set_grid_snap(false);
1164
1165         try {
1166                 start_duck_drag(get_selected_duck()->get_trans_point());
1167                 translate_selected_ducks(get_selected_duck()->get_trans_point()+nudge*multiplier);
1168                 end_duck_drag();
1169         }
1170         catch(String)
1171         {
1172                 canvas_view->duck_refresh_flag=true;
1173                 canvas_view->queue_rebuild_ducks();
1174         }
1175
1176         set_grid_snap(grid_snap_holder);
1177         set_guide_snap(guide_snap_holder);
1178
1179         return true;
1180 }
1181
1182 bool
1183 WorkArea::on_drawing_area_event(GdkEvent *event)
1184 {
1185         synfig::Point mouse_pos;
1186     float bezier_click_pos;
1187         const float radius((abs(pw)+abs(ph))*4);
1188         int button_pressed(0);
1189         float pressure(0);
1190         bool is_mouse(false);
1191         Gdk::ModifierType modifier(Gdk::ModifierType(0));
1192
1193         drawing_area->grab_focus();
1194
1195         // Handle input stuff
1196         if(
1197                 event->any.type==GDK_MOTION_NOTIFY ||
1198                 event->any.type==GDK_BUTTON_PRESS ||
1199                 event->any.type==GDK_2BUTTON_PRESS ||
1200                 event->any.type==GDK_3BUTTON_PRESS ||
1201                 event->any.type==GDK_BUTTON_RELEASE
1202         )
1203         {
1204                 GdkDevice *device;
1205                 if(event->any.type==GDK_MOTION_NOTIFY)
1206                 {
1207                         device=event->motion.device;
1208                         modifier=Gdk::ModifierType(event->motion.state);
1209                 }
1210                 else
1211                 {
1212                         device=event->button.device;
1213                         modifier=Gdk::ModifierType(event->button.state);
1214                 }
1215
1216                 // Make sure we recognize the device
1217                 if(curr_input_device)
1218                 {
1219                         if(curr_input_device!=device)
1220                         {
1221                                 assert(device);
1222                                 curr_input_device=device;
1223                                 signal_input_device_changed()(curr_input_device);
1224                         }
1225                 }
1226                 else if(device)
1227                 {
1228                         curr_input_device=device;
1229                         signal_input_device_changed()(curr_input_device);
1230                 }
1231
1232                 assert(curr_input_device);
1233
1234                 // Calculate the position of the
1235                 // input device in canvas coordinates
1236                 // and the buttons
1237                 if(!event->button.axes)
1238                 {
1239                         mouse_pos=synfig::Point(screen_to_comp_coords(synfig::Point(event->button.x,event->button.y)));
1240                         button_pressed=event->button.button;
1241                         pressure=1.0f;
1242                         is_mouse=true;
1243                         if(isnan(event->button.x) || isnan(event->button.y))
1244                                 return false;
1245                 }
1246                 else
1247                 {
1248                         double x(event->button.axes[0]);
1249                         double y(event->button.axes[1]);
1250                         if(isnan(x) || isnan(y))
1251                                 return false;
1252
1253                         pressure=event->button.axes[2];
1254                         //synfig::info("pressure=%f",pressure);
1255                         pressure-=0.04f;
1256                         pressure/=1.0f-0.04f;
1257
1258
1259                         assert(!isnan(pressure));
1260
1261                         mouse_pos=synfig::Point(screen_to_comp_coords(synfig::Point(x,y)));
1262
1263                         button_pressed=event->button.button;
1264
1265                         if(button_pressed==1 && pressure<0 && (event->any.type!=GDK_BUTTON_RELEASE && event->any.type!=GDK_BUTTON_PRESS))
1266                                 button_pressed=0;
1267                         if(pressure<0)
1268                                 pressure=0;
1269
1270                         //if(event->any.type==GDK_BUTTON_PRESS && button_pressed)
1271                         //      synfig::info("Button pressed on input device = %d",event->button.button);
1272
1273                         //if(event->button.axes[2]>0.1)
1274                         //      button_pressed=1;
1275                         //else
1276                         //      button_pressed=0;
1277                 }
1278         }
1279         // GDK mouse scrolling events
1280         else if(event->any.type==GDK_SCROLL)
1281         {
1282                 // GDK information needed to properly interpret mouse
1283                 // scrolling events are: scroll.state, scroll.x/scroll.y, and
1284                 // scroll.direction. The value of scroll.direction will be
1285                 // obtained later.
1286
1287                 modifier=Gdk::ModifierType(event->scroll.state);
1288                 mouse_pos=synfig::Point(screen_to_comp_coords(synfig::Point(event->scroll.x,event->scroll.y)));
1289         }
1290
1291         // Handle the renderables
1292         {
1293                 std::set<etl::handle<WorkAreaRenderer> >::iterator iter;
1294                 for(iter=renderer_set_.begin();iter!=renderer_set_.end();++iter)
1295                 {
1296                         if((*iter)->get_enabled())
1297                                 if((*iter)->event_vfunc(event))
1298                                 {
1299                                         // Event handled. Return true.
1300                                         return true;
1301                                 }
1302                 }
1303         }
1304
1305         // Event hasn't been handled, pass it down
1306         switch(event->type)
1307     {
1308         case GDK_BUTTON_PRESS:
1309                 {
1310                 switch(button_pressed)
1311                 {
1312                 case 1: // Attempt to click on a duck
1313                 {
1314                         etl::handle<Duck> duck;
1315                         dragging=DRAG_NONE;
1316
1317                         if(allow_duck_clicks)
1318                         {
1319                                 duck=find_duck(mouse_pos,radius);
1320
1321                                 if(duck)
1322                                 {
1323                                         // make a note of whether the duck we click on was selected or not
1324                                         if(duck_is_selected(duck))
1325                                                 clicked_duck=duck;
1326                                         else
1327                                         {
1328                                                 clicked_duck=0;
1329                                                 // if CTRL isn't pressed, clicking an unselected duck will unselect all other ducks
1330                                                 if(!(modifier&GDK_CONTROL_MASK))
1331                                                         clear_selected_ducks();
1332                                                 select_duck(duck);
1333                                         }
1334                                 }
1335                         }
1336                         //else
1337                         //      clear_selected_ducks();
1338
1339
1340
1341                         selected_bezier=find_bezier(mouse_pos,radius,&bezier_click_pos);
1342                         if(duck && duck->get_editable())
1343                         {
1344                                 //get_selected_duck()->signal_user_click(0)();
1345                                 //if(clicked_duck)clicked_duck->signal_user_click(0)();
1346
1347                                 // if the user is holding shift while clicking on a tangent duck, consider splitting the tangent
1348                                 if (event->motion.state&GDK_SHIFT_MASK && duck->get_type() == Duck::TYPE_TANGENT)
1349                                 {
1350                                         synfigapp::ValueDesc value_desc = duck->get_value_desc();
1351
1352                                         // we have the tangent, but need the vertex - that's the parent
1353                                         if (value_desc.parent_is_value_node()) {
1354                                                 ValueNode_Composite::Handle parent_value_node = value_desc.get_parent_value_node();
1355
1356                                                 // if the tangent isn't split, then split it
1357                                                 if (!((*(parent_value_node->get_link("split")))(get_time()).get(bool())))
1358                                                 {
1359                                                         get_canvas_view()->canvas_interface()->
1360                                                                 change_value(synfigapp::ValueDesc(parent_value_node,
1361                                                                                                                                   parent_value_node->get_link_index_from_name("split")),
1362                                                                                          true);
1363                                                         // rebuild the ducks from scratch, so the tangents ducks aren't connected
1364                                                         get_canvas_view()->rebuild_ducks();
1365
1366                                                         // reprocess the mouse click
1367                                                         return on_drawing_area_event(event);
1368                                                 }
1369                                         } else {
1370                                                 // I don't know how to access the vertex from the tangent duck when originally drawing the bline in the bline tool
1371
1372                                                 // synfig::ValueNode::Handle vn = value_desc.get_value_node();
1373                                                 synfig::info("parent isn't value node?  shift-drag-tangent doesn't work in bline tool yet...");
1374                                         }
1375                                 }
1376
1377                                 dragging=DRAG_DUCK;
1378                                 drag_point=mouse_pos;
1379                                 //drawing_area->queue_draw();
1380                                 start_duck_drag(mouse_pos);
1381                                 get_canvas_view()->reset_cancel_status();
1382                                 return true;
1383                         }
1384 // I commented out this section because
1385 // it was causing issues when rotoscoping.
1386 // At the moment, we don't need it, so
1387 // this was the easiest way to fix the problem.
1388 /*
1389                         else
1390                         if(selected_bezier)
1391                         {
1392                                 selected_duck=0;
1393                                 selected_bezier->signal_user_click(0)(bezier_click_pos);
1394                         }
1395 */
1396                         else
1397                         {
1398                                 //clear_selected_ducks();
1399                                 selected_bezier=0;
1400                                 if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_LEFT,mouse_pos,pressure,modifier))==Smach::RESULT_OK)
1401                                 {
1402                                         // Check for a guide click
1403                                         GuideList::iterator iter;
1404
1405                                         iter=find_guide_x(mouse_pos,radius);
1406                                         if(iter==get_guide_list_x().end())
1407                                         {
1408                                                 curr_guide_is_x=false;
1409                                                 iter=find_guide_y(mouse_pos,radius);
1410                                         }
1411                                         else
1412                                                 curr_guide_is_x=true;
1413                                         if(iter!=get_guide_list_x().end() && iter!=get_guide_list_y().end())
1414                                         {
1415                                                 dragging=DRAG_GUIDE;
1416                                                 curr_guide=iter;
1417                                                 return true;
1418                                         }
1419
1420
1421                                         // All else fails, try making a selection box
1422                                         dragging=DRAG_BOX;
1423                                         curr_point=drag_point=mouse_pos;
1424                                         return true;
1425                                 }
1426                         }
1427                         break;
1428                 }
1429                 case 2: // Attempt to drag and move the window
1430                 {
1431                         etl::handle<Duck> duck=find_duck(mouse_pos,radius);
1432                         etl::handle<Bezier> bezier=find_bezier(mouse_pos,radius,&bezier_click_pos);
1433                         if(duck)
1434                                 duck->signal_user_click(1)();
1435                         else
1436                         if(bezier)
1437                                 bezier->signal_user_click(1)(bezier_click_pos);
1438
1439                         if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_MIDDLE,mouse_pos,pressure,modifier))==Smach::RESULT_OK)
1440                         if(is_mouse)
1441                         {
1442                                 dragging=DRAG_WINDOW;
1443                                 drag_point=mouse_pos;
1444                                 signal_user_click(1)(mouse_pos);
1445                         }
1446                         break;
1447                 }
1448                 case 3: // Attempt to either get info on a duck, or open the menu
1449                 {
1450                         etl::handle<Duck> duck=find_duck(mouse_pos,radius);
1451                         etl::handle<Bezier> bezier=find_bezier(mouse_pos,radius,&bezier_click_pos);
1452
1453                         Layer::Handle layer(get_canvas()->find_layer(mouse_pos));
1454                         if(duck)
1455                         {
1456                                 if(get_selected_ducks().size()<=1)
1457                                         duck->signal_user_click(2)();
1458                                 else
1459                                         canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MULTIPLE_DUCKS_CLICKED,BUTTON_RIGHT,mouse_pos,pressure,modifier));
1460                                 return true;
1461                         }
1462                         else if(bezier)
1463                         {
1464                                 bezier->signal_user_click(2)(bezier_click_pos);
1465                                 return true;
1466                         }
1467                         else if (layer)
1468                         {
1469                                 if(canvas_view->get_smach().process_event(EventLayerClick(layer,BUTTON_RIGHT,mouse_pos))==Smach::RESULT_OK)
1470                                         return false;
1471                                 return true;
1472                         }
1473                         else
1474                                 canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_RIGHT,mouse_pos,pressure,modifier));
1475                         /*
1476                         if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_RIGHT,mouse_pos,pressure,modifier))==Smach::RESULT_OK)
1477                         {
1478                                 //popup_menu();
1479                                 return true;
1480                         }
1481                         */
1482                         break;
1483                 }
1484                 case 4:
1485                         signal_user_click(3)(mouse_pos);
1486                         break;
1487                 case 5:
1488                         signal_user_click(4)(mouse_pos);
1489                         break;
1490                 default:
1491                         break;
1492                 }
1493                 }
1494                 break;
1495         case GDK_MOTION_NOTIFY:
1496                 curr_point=mouse_pos;
1497
1498                 if(event->motion.time-last_event_time<25)
1499                         return true;
1500                 else
1501                         last_event_time=event->motion.time;
1502
1503                 signal_cursor_moved_();
1504
1505                 // Guide/Duck highlights on hover
1506                 if(dragging==DRAG_NONE)
1507                 {
1508                         GuideList::iterator iter;
1509
1510                         iter=find_guide_x(mouse_pos,radius);
1511                         if(iter==get_guide_list_x().end())
1512                                 iter=find_guide_y(mouse_pos,radius);
1513
1514                         if(iter!=curr_guide)
1515                         {
1516                                 curr_guide=iter;
1517                                 drawing_area->queue_draw();
1518                         }
1519
1520                         etl::handle<Duck> duck;
1521                         duck=find_duck(mouse_pos,radius);
1522                         if(duck!=hover_duck)
1523                         {
1524                                 hover_duck=duck;
1525                                 drawing_area->queue_draw();
1526                         }
1527                 }
1528
1529
1530                 if(dragging==DRAG_DUCK)
1531                 {
1532                         if(canvas_view->get_cancel_status())
1533                         {
1534                                 dragging=DRAG_NONE;
1535                                 canvas_view->queue_rebuild_ducks();
1536                                 return true;
1537                         }
1538                         /*
1539                         Point point((mouse_pos-selected_duck->get_origin())/selected_duck->get_scalar());
1540                         if(get_grid_snap())
1541                         {
1542                                 point[0]=floor(point[0]/grid_size[0]+0.5)*grid_size[0];
1543                                 point[1]=floor(point[1]/grid_size[1]+0.5)*grid_size[1];
1544                         }
1545                         selected_duck->set_point(point);
1546                         */
1547
1548                         //Point p(mouse_pos);
1549
1550                         set_axis_lock(event->motion.state&GDK_SHIFT_MASK);
1551
1552                         translate_selected_ducks(mouse_pos);
1553
1554                         drawing_area->queue_draw();
1555                 }
1556
1557                 if(dragging==DRAG_BOX)
1558                 {
1559                         curr_point=mouse_pos;
1560                         drawing_area->queue_draw();
1561                 }
1562
1563                 if(dragging==DRAG_GUIDE)
1564                 {
1565                         if(curr_guide_is_x)
1566                                 *curr_guide=mouse_pos[0];
1567                         else
1568                                 *curr_guide=mouse_pos[1];
1569                         drawing_area->queue_draw();
1570                 }
1571
1572                 if(dragging!=DRAG_WINDOW)
1573                 {       // Update those triangle things on the rulers
1574                         const synfig::Point point(mouse_pos);
1575                         hruler->property_position()=Distance(point[0],Distance::SYSTEM_UNITS).get(App::distance_system,get_canvas()->rend_desc());
1576                         vruler->property_position()=Distance(point[1],Distance::SYSTEM_UNITS).get(App::distance_system,get_canvas()->rend_desc());
1577                 }
1578
1579                 if(dragging == DRAG_WINDOW)
1580                         set_focus_point(get_focus_point() + mouse_pos-drag_point);
1581                 else if (event->motion.state & GDK_BUTTON1_MASK &&
1582                                 canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DRAG, BUTTON_LEFT,
1583                                                                                                                                   mouse_pos,pressure,modifier)) == Smach::RESULT_ACCEPT)
1584                         return true;
1585                 else if (event->motion.state & GDK_BUTTON2_MASK &&
1586                                  canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DRAG, BUTTON_MIDDLE,
1587                                                                                                                                    mouse_pos, pressure, modifier)) == Smach::RESULT_ACCEPT)
1588                         return true;
1589                 else if (event->motion.state & GDK_BUTTON3_MASK &&
1590                                  canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DRAG, BUTTON_RIGHT,
1591                                                                                                                                    mouse_pos, pressure, modifier)) == Smach::RESULT_ACCEPT)
1592                         return true;
1593                 else if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_MOTION, BUTTON_NONE,
1594                                                                                                                                   mouse_pos, pressure,modifier)) == Smach::RESULT_ACCEPT)
1595                         return true;
1596
1597                 break;
1598
1599         case GDK_BUTTON_RELEASE:
1600         {
1601                 bool ret(false);
1602
1603                 if(dragging==DRAG_GUIDE)
1604                 {
1605                         dragging=DRAG_NONE;
1606                         save_meta_data();
1607                         return true;
1608                 }
1609                 else
1610                 if(dragging==DRAG_DUCK)
1611                 {
1612                         synfigapp::Action::PassiveGrouper grouper(instance.get(),"Move");
1613                         dragging=DRAG_NONE;
1614                         //translate_selected_ducks(mouse_pos);
1615                         set_axis_lock(false);
1616
1617                         try{
1618                         get_canvas_view()->duck_refresh_flag=false;
1619                         get_canvas_view()->duck_refresh_needed=false;
1620                         const bool drag_did_anything(end_duck_drag());
1621                         get_canvas_view()->duck_refresh_flag=true;
1622                         if(!drag_did_anything)
1623                         {
1624                                 // if we originally clicked on a selected duck ...
1625                                 if(clicked_duck)
1626                                 {
1627                                         // ... and CTRL is pressed, then just toggle the clicked duck
1628                                         //     otherwise make the clicked duck the only selected duck
1629                                         if(modifier&GDK_CONTROL_MASK)
1630                                                 unselect_duck(clicked_duck);
1631                                         else
1632                                         {
1633                                                 clear_selected_ducks();
1634                                                 select_duck(clicked_duck);
1635                                         }
1636                                         clicked_duck->signal_user_click(0)();
1637                                 }
1638                         }
1639                         else
1640                         {
1641                                 if(canvas_view->duck_refresh_needed)
1642                                         canvas_view->queue_rebuild_ducks();
1643                                 return true;
1644                         }
1645                         }catch(String)
1646                         {
1647                                 canvas_view->duck_refresh_flag=true;
1648                                 canvas_view->queue_rebuild_ducks();
1649                                 return true;
1650                         }
1651                         //queue_draw();
1652                         clicked_duck=0;
1653
1654                         ret=true;
1655                 }
1656
1657                 if(dragging==DRAG_BOX)
1658                 {
1659                         dragging=DRAG_NONE;
1660                         if((drag_point-mouse_pos).mag()>radius/2.0f)
1661                         {
1662                                 if(canvas_view->get_smach().process_event(EventBox(drag_point,mouse_pos,MouseButton(event->button.button),modifier))==Smach::RESULT_ACCEPT)
1663                                         return true;
1664
1665                                 // when dragging a box around some ducks:
1666                                 // SHIFT selects; CTRL toggles; SHIFT+CTRL unselects; <none> clears all then selects
1667                                 if(modifier&GDK_SHIFT_MASK)
1668                                         select_ducks_in_box(drag_point,mouse_pos);
1669
1670                                 if(modifier&GDK_CONTROL_MASK)
1671                                         toggle_select_ducks_in_box(drag_point,mouse_pos);
1672                                 else if(!(modifier&GDK_SHIFT_MASK))
1673                                 {
1674                                         clear_selected_ducks();
1675                                         select_ducks_in_box(drag_point,mouse_pos);
1676                                 }
1677                                 ret=true;
1678                         }
1679                         else
1680                         {
1681                                 if(allow_layer_clicks)
1682                                 {
1683                                         Layer::Handle layer(get_canvas()->find_layer(drag_point));
1684                                         //if(layer)
1685                                         {
1686                                                 if(canvas_view->get_smach().process_event(EventLayerClick(layer,BUTTON_LEFT,mouse_pos,modifier))==Smach::RESULT_OK)
1687                                                         signal_layer_selected_(layer);
1688                                                 ret=true;
1689                                         }
1690                                 }
1691                                 else
1692                                 {
1693                                         signal_user_click(0)(mouse_pos);
1694                                 }
1695                         }
1696                 }
1697
1698                 dragging=DRAG_NONE;
1699
1700                 if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_UP,MouseButton(event->button.button),mouse_pos,pressure,modifier))==Smach::RESULT_ACCEPT)
1701                         ret=true;
1702
1703                 return ret;
1704         }
1705                 break;
1706         case GDK_SCROLL:
1707         {
1708                 // Handle a mouse scrolling event like Xara Xtreme and
1709                 // Inkscape:
1710
1711                 // Scroll up/down: scroll up/down
1712                 // Shift + scroll up/down: scroll left/right
1713                 // Control + scroll up/down: zoom in/out
1714
1715                 if(modifier&GDK_CONTROL_MASK)
1716                 {
1717
1718                         // The zoom is performed while preserving the pointer
1719                         // position as a fixed point (similarly to Xara Xtreme and
1720                         // Inkscape).
1721
1722                         // The strategy used below is to scroll to the updated
1723                         // position, then zoom. This is easy to implement within
1724                         // the present architecture, but has the disadvantage of
1725                         // triggering multiple visible refreshes. Note: 1.25 is
1726                         // the hard wired ratio in zoom_in()/zoom_out(). The
1727                         // variable "drift" compensates additional inaccuracies in
1728                         // the zoom. There is also an additional minus sign for
1729                         // the inverted y coordinates.
1730
1731                         // FIXME: One might want to figure out where in the code
1732                         // this empirical drift is been introduced.
1733
1734                         const synfig::Point scroll_point(get_scrollx_adjustment()->get_value(),get_scrolly_adjustment()->get_value());
1735                         const double drift = 0.052;
1736
1737                         switch(event->scroll.direction)
1738                         {
1739                                 case GDK_SCROLL_UP:
1740                                 case GDK_SCROLL_RIGHT:
1741                                         get_scrollx_adjustment()->set_value(scroll_point[0]+(mouse_pos[0]-scroll_point[0])*(1.25-(1+drift)));
1742                                         get_scrolly_adjustment()->set_value(scroll_point[1]-(mouse_pos[1]+scroll_point[1])*(1.25-(1+drift)));
1743                                         zoom_in();
1744                                         break;
1745                                 case GDK_SCROLL_DOWN:
1746                                 case GDK_SCROLL_LEFT:
1747                                         get_scrollx_adjustment()->set_value(scroll_point[0]+(mouse_pos[0]-scroll_point[0])*(1/1.25-(1+drift)));
1748                                         get_scrolly_adjustment()->set_value(scroll_point[1]-(mouse_pos[1]+scroll_point[1])*(1/1.25-(1+drift)));
1749                                         zoom_out();
1750                                         break;
1751                                 default:
1752                                         break;
1753                         }
1754                 }
1755                 else if(modifier&GDK_SHIFT_MASK)
1756                 {
1757                         // Scroll in either direction by 20 pixels. Ideally, the
1758                         // amount of pixels per scrolling event should be
1759                         // configurable. Xara Xtreme currently uses an (hard
1760                         // wired) amount 20 pixel, Inkscape defaults to 40 pixels.
1761
1762                         const int scroll_pixel = 20;
1763
1764                         switch(event->scroll.direction)
1765                         {
1766                                 case GDK_SCROLL_UP:
1767                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()-scroll_pixel*pw);
1768                                         break;
1769                                 case GDK_SCROLL_DOWN:
1770                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()+scroll_pixel*pw);
1771                                         break;
1772                                 case GDK_SCROLL_LEFT:
1773                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()+scroll_pixel*ph);
1774                                         break;
1775                                 case GDK_SCROLL_RIGHT:
1776                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()-scroll_pixel*ph);
1777                                         break;
1778                                 default:
1779                                         break;
1780                         }
1781                 }
1782                 else
1783                 {
1784                         // Scroll in either direction by 20 pixels. Ideally, the
1785                         // amount of pixels per scrolling event should be
1786                         // configurable. Xara Xtreme currently uses an (hard
1787                         // wired) amount 20 pixel, Inkscape defaults to 40 pixels.
1788
1789                         const int scroll_pixel = 20;
1790
1791                         switch(event->scroll.direction)
1792                         {
1793                                 case GDK_SCROLL_UP:
1794                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()+scroll_pixel*ph);
1795                                         break;
1796                                 case GDK_SCROLL_DOWN:
1797                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()-scroll_pixel*ph);
1798                                         break;
1799                                 case GDK_SCROLL_LEFT:
1800                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()-scroll_pixel*pw);
1801                                         break;
1802                                 case GDK_SCROLL_RIGHT:
1803                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()+scroll_pixel*pw);
1804                                         break;
1805                                 default:
1806                                         break;
1807                         }
1808                 }
1809         }
1810                 break;
1811         default:
1812                 break;
1813         }
1814                 return false;
1815 }
1816
1817 bool
1818 WorkArea::on_hruler_event(GdkEvent */*event*/)
1819 {
1820 /*
1821         switch(event->type)
1822     {
1823         case GDK_BUTTON_PRESS:
1824                 if(dragging==DRAG_NONE)
1825                 {
1826                         dragging=DRAG_GUIDE;
1827                         curr_guide=get_guide_list_y().insert(get_guide_list_y().begin());
1828                         curr_guide_is_x=false;
1829                 }
1830                 return true;
1831                 break;
1832
1833         case GDK_MOTION_NOTIFY:
1834                 // Guide movement
1835                 if(dragging==DRAG_GUIDE && curr_guide_is_x==false)
1836                 {
1837                         double y,x;
1838                         if(event->button.axes)
1839                         {
1840                                 x=(event->button.axes[0]);
1841                                 y=(event->button.axes[1]);
1842                         }
1843                         else
1844                         {
1845                                 x=event->button.x;
1846                                 y=event->button.y;
1847                         }
1848
1849                         if(isnan(y) || isnan(x))
1850                                 return false;
1851
1852                         *curr_guide=synfig::Point(screen_to_comp_coords(synfig::Point(x,y)))[1];
1853
1854                         queue_draw();
1855                 }
1856                 return true;
1857                 break;
1858
1859         case GDK_BUTTON_RELEASE:
1860                 if(dragging==DRAG_GUIDE && curr_guide_is_x==false)
1861                 {
1862                         dragging=DRAG_NONE;
1863                         get_guide_list_y().erase(curr_guide);
1864                 }
1865                 break;
1866                 return true;
1867         default:
1868                 break;
1869         }
1870 */
1871         return false;
1872 }
1873
1874 bool
1875 WorkArea::on_vruler_event(GdkEvent */*event*/)
1876 {
1877 /*
1878         switch(event->type)
1879     {
1880         case GDK_BUTTON_PRESS:
1881                 if(dragging==DRAG_NONE)
1882                 {
1883                         dragging=DRAG_GUIDE;
1884                         curr_guide=get_guide_list_x().insert(get_guide_list_x().begin());
1885                         curr_guide_is_x=true;
1886                 }
1887                 return true;
1888                 break;
1889         case GDK_BUTTON_RELEASE:
1890                 if(dragging==DRAG_GUIDE && curr_guide_is_x==true)
1891                 {
1892                         dragging=DRAG_NONE;
1893                         get_guide_list_x().erase(curr_guide);
1894                 }
1895                 break;
1896                 return true;
1897         default:
1898                 break;
1899         }
1900 */
1901         return false;
1902 }
1903
1904
1905 void
1906 WorkArea::refresh_dimension_info()
1907 {
1908         synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1909
1910         canvaswidth=rend_desc.get_br()[0]-rend_desc.get_tl()[0];
1911         canvasheight=rend_desc.get_br()[1]-rend_desc.get_tl()[1];
1912
1913         pw=canvaswidth/w;
1914         ph=canvasheight/h;
1915
1916         scrollx_adjustment.set_page_increment(abs(get_grid_size()[0]));
1917         scrollx_adjustment.set_step_increment(abs(pw));
1918         scrollx_adjustment.set_lower(-abs(canvaswidth));
1919         scrollx_adjustment.set_upper(abs(canvaswidth));
1920         scrolly_adjustment.set_lower(-abs(canvasheight));
1921         scrolly_adjustment.set_upper(abs(canvasheight));
1922         scrolly_adjustment.set_step_increment(abs(ph));
1923         scrolly_adjustment.set_page_increment(abs(get_grid_size()[1]));
1924
1925
1926
1927         if(drawing_area->get_width()<=0 || drawing_area->get_height()<=0 || w==0 || h==0)
1928                 return;
1929
1930         const synfig::Point focus_point(get_focus_point());
1931         const synfig::Real x(focus_point[0]/pw+drawing_area->get_width()/2-w/2);
1932         const synfig::Real y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
1933
1934         window_tl[0]=rend_desc.get_tl()[0]-pw*x;
1935         window_br[0]=rend_desc.get_br()[0]+pw*(drawing_area->get_width()-x-w);
1936
1937         window_tl[1]=rend_desc.get_tl()[1]-ph*y;
1938         window_br[1]=rend_desc.get_br()[1]+ph*(drawing_area->get_height()-y-h);
1939
1940         hruler->property_lower()=Distance(window_tl[0],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1941         hruler->property_upper()=Distance(window_br[0],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1942         vruler->property_lower()=Distance(window_tl[1],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1943         vruler->property_upper()=Distance(window_br[1],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1944
1945         view_window_changed();
1946 }
1947
1948
1949 synfig::Point
1950 WorkArea::screen_to_comp_coords(synfig::Point pos)const
1951 {
1952         synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1953         //synfig::Vector::value_type canvaswidth=rend_desc.get_br()[0]-rend_desc.get_tl()[0];
1954         //synfig::Vector::value_type canvasheight=rend_desc.get_br()[1]-rend_desc.get_tl()[1];
1955         //synfig::Vector::value_type pw=canvaswidth/w;
1956         //synfig::Vector::value_type ph=canvasheight/h;
1957         Vector focus_point=get_focus_point();
1958         synfig::Vector::value_type x=focus_point[0]/pw+drawing_area->get_width()/2-w/2;
1959         synfig::Vector::value_type y=focus_point[1]/ph+drawing_area->get_height()/2-h/2;
1960
1961         return rend_desc.get_tl()-synfig::Point(pw*x,ph*y)+synfig::Point(pw*pos[0],ph*pos[1]);
1962 }
1963
1964 synfig::Point
1965 WorkArea::comp_to_screen_coords(synfig::Point /*pos*/)const
1966 {
1967         synfig::warning("WorkArea::comp_to_screen_coords: Not yet implemented");
1968         return synfig::Point();
1969 }
1970
1971 int
1972 WorkArea::next_unrendered_tile(int refreshes)const
1973 {
1974         //assert(!tile_book.empty());
1975         if(tile_book.empty())
1976                 return -1;
1977
1978         //const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1979
1980         const synfig::Vector focus_point(get_focus_point());
1981
1982         // Calculate the window coordinates of the top-left
1983         // corner of the canvas.
1984         const synfig::Vector::value_type
1985                 x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
1986                 y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
1987
1988
1989         int div = 1 << lowrespixel;
1990         const int width_in_tiles(w/tile_w+((low_resolution?((w/div)%(tile_w/div)):(w%tile_w))?1:0));
1991         const int height_in_tiles(h/tile_h+(h%tile_h?1:0));
1992
1993         int
1994                 u(0),v(0),
1995                 u1(int(-x/tile_w)),
1996                 v1(int(-y/tile_h)),
1997                 u2(int((-x+drawing_area->get_width())/tile_w+1)),
1998                 v2(int((-y+drawing_area->get_height())/tile_h+1));
1999
2000         if(u2>width_in_tiles)u2=width_in_tiles;
2001         if(v2>height_in_tiles)v2=height_in_tiles;
2002         if(u1<0)u1=0;
2003         if(v1<0)v1=0;
2004
2005         int last_good_tile(-1);
2006
2007         for(v=v1;v<v2;v++)
2008                 for(u=u1;u<u2;u++)
2009                 {
2010                         int index(v*width_in_tiles+u);
2011                         if(tile_book[index].second<refreshes)
2012                         {
2013                                 last_good_tile=index;
2014                                 if(rand()%8==0)
2015                                         return index;
2016                         }
2017                 }
2018         return last_good_tile;
2019 }
2020
2021 /*
2022 template <typename F, typename T=WorkAreaRenderer, typename R=typename F::result_type>
2023 class handle2ptr_t : public std::unary_function<typename etl::handle<T>,R>
2024 {
2025 private:
2026         F func;
2027 public:
2028         handle2ptr_t(const F &func):func(func) { };
2029
2030         R operator()(typename etl::handle<T> x) { return func(*x); }
2031 };
2032
2033 template <typename F>
2034 handle2ptr_t<F>
2035 handle2ptr(F func)
2036 {
2037         return handle2ptr_t<F>(func);
2038 }
2039         for_each(
2040                 renderer_set_.begin(),
2041                 renderer_set_.end(),
2042                 handle2ptr(
2043                         sigc::bind(
2044                                 sigc::bind(
2045                                         sigc::mem_fun(
2046                                                 &WorkAreaRenderer::render_vfunc
2047                                         ),
2048                                         Gdk::Rectangle(event->area)
2049                                 ),
2050                                 drawing_area->get_window()
2051                         )
2052                 )
2053         );
2054 */
2055
2056 bool
2057 WorkArea::refresh(GdkEventExpose*event)
2058 {
2059         assert(get_canvas());
2060
2061         drawing_area->get_window()->clear();
2062
2063         //const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
2064
2065         const synfig::Vector focus_point(get_focus_point());
2066
2067         // Update the old focus point
2068         last_focus_point=focus_point;
2069
2070         // Draw out the renderables
2071         {
2072                 std::set<etl::handle<WorkAreaRenderer> >::iterator iter;
2073                 for(iter=renderer_set_.begin();iter!=renderer_set_.end();++iter)
2074                 {
2075                         if((*iter)->get_enabled())
2076                                 (*iter)->render_vfunc(
2077                                         drawing_area->get_window(),
2078                                         Gdk::Rectangle(&event->area)
2079                                 );
2080                 }
2081         }
2082
2083         // Calculate the window coordinates of the top-left
2084         // corner of the canvas.
2085         //const synfig::Vector::value_type
2086         //      x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
2087         //      y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2088
2089         //const synfig::Vector::value_type window_startx(window_tl[0]);
2090         //const synfig::Vector::value_type window_endx(window_br[0]);
2091         //const synfig::Vector::value_type window_starty(window_tl[1]);
2092         //const synfig::Vector::value_type window_endy(window_br[1]);
2093
2094         Glib::RefPtr<Gdk::GC> gc=Gdk::GC::create(drawing_area->get_window());
2095
2096         // If we are in animate mode, draw a red border around the screen
2097         if(canvas_interface->get_mode()&synfigapp::MODE_ANIMATE)
2098         {
2099 // #define USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
2100 #ifdef USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
2101                 // This method of drawing the red border doesn't work on any
2102                 // Gtk theme which uses the crux-engine, hcengine, industrial,
2103                 // mist, or ubuntulooks engine, such as the default ubuntu
2104                 // 'Human' theme.
2105                 drawing_frame->modify_bg(Gtk::STATE_NORMAL,Gdk::Color("#FF0000"));
2106 #else
2107                 // So let's do it in a more primitive fashion.
2108                 gc->set_rgb_fg_color(Gdk::Color("#FF0000"));
2109                 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
2110                 drawing_area->get_window()->draw_rectangle(
2111                         gc,
2112                         false,  // Fill?
2113                         0,0,    // x,y
2114                         drawing_area->get_width()-1,drawing_area->get_height()-1); // w,h
2115 #endif
2116         }
2117 #ifdef USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
2118         else
2119                 drawing_frame->unset_bg(Gtk::STATE_NORMAL);
2120 #endif
2121
2122         previous_focus=get_focus_point();
2123
2124         return true;
2125 }
2126
2127 void
2128 WorkArea::done_rendering()
2129 {
2130 /*
2131         assert(buffer);
2132         assert(w>0);
2133         assert(h>0);
2134         pix_buf=Gdk::Pixbuf::create_from_data(
2135                 buffer, // pointer to the data
2136                 Gdk::COLORSPACE_RGB, // the colorspace
2137                 true, // has alpha?
2138                 8, // bits per sample
2139                 w,      // width
2140                 h,      // height
2141                 w*bpp); // stride (pitch)
2142         assert(pix_buf);
2143 */
2144 }
2145
2146
2147 void
2148 WorkArea::set_quality(int x)
2149 {
2150         if(x==quality)
2151                 return;
2152         quality=x;
2153         queue_render_preview();
2154 }
2155
2156 void
2157 WorkArea::set_lowrespixel(int x)
2158 {
2159         if(x==lowrespixel)
2160                 return;
2161         lowrespixel=x;
2162         queue_render_preview();
2163 }
2164
2165
2166
2167
2168 namespace studio
2169 {
2170 class WorkAreaProgress : public synfig::ProgressCallback
2171 {
2172         WorkArea *work_area;
2173         ProgressCallback *cb;
2174
2175 public:
2176
2177         WorkAreaProgress(WorkArea *work_area,ProgressCallback *cb):
2178                 work_area(work_area),cb(cb)
2179         {
2180                 assert(cb);
2181         }
2182
2183         virtual bool
2184         task(const std::string &str)
2185         {
2186                 if(work_area->dirty)
2187                         return false;
2188                 return cb->task(str);
2189         }
2190
2191         virtual bool
2192         error(const std::string &err)
2193         {
2194                 if(work_area->dirty)
2195                         return false;
2196                 return cb->error(err);
2197         }
2198
2199         virtual bool
2200         amount_complete(int current, int total)
2201         {
2202                 if(work_area->dirty)
2203                         return false;
2204                 return cb->amount_complete(current,total);
2205         }
2206 };
2207 }
2208
2209 bool
2210 studio::WorkArea::async_update_preview()
2211 {
2212         if (get_updating())
2213         {
2214                 stop_updating();
2215                 queue_render_preview();
2216                 return false;
2217         }
2218
2219         async_renderer=0;
2220
2221         queued=false;
2222         canceled_=false;
2223         get_canvas_view()->reset_cancel_status();
2224
2225         // This object will mark us as busy until
2226         // we are done.
2227         //studio::App::Busy busy;
2228
2229         //WorkAreaProgress callback(this,get_canvas_view()->get_ui_interface().get());
2230         //synfig::ProgressCallback *cb=&callback;
2231
2232         if(!is_visible())return false;
2233
2234         /*
2235         // If we are queued to render the scene at the next idle
2236         // go ahead and de-queue it.
2237         if(render_idle_func_id)
2238         {
2239                 g_source_remove(render_idle_func_id);
2240                 //queued=false;
2241                 render_idle_func_id=0;
2242         }
2243         */
2244
2245         dirty=false;
2246         get_canvas_view()->reset_cancel_status();
2247
2248         //bool ret=false;
2249         RendDesc desc=get_canvas()->rend_desc();
2250
2251         int w=(int)(desc.get_w()*zoom);
2252         int h=(int)(desc.get_h()*zoom);
2253
2254         // Setup the description parameters
2255         desc.set_antialias(1);
2256         desc.set_time(cur_time);
2257
2258         set_rend_desc(desc);
2259
2260         // Create the render target
2261         handle<Target> target;
2262
2263         // if we have lots of pixels to render and the tile renderer isn't disabled, use it
2264         int div = 1 << lowrespixel;
2265         if(w*h>(low_resolution?480*270:480*270/div) &&
2266            !getenv("SYNFIG_DISABLE_TILE_RENDER"))
2267         {
2268                 // do a tile render
2269                 handle<WorkAreaTarget> trgt(new class WorkAreaTarget(this,w,h));
2270
2271                 trgt->set_rend_desc(&desc);
2272                 trgt->set_onion_skin(get_onion_skin());
2273                 target=trgt;
2274         }
2275         else
2276         {
2277                 // do a scanline render
2278                 handle<WorkAreaTarget_Full> trgt(new class WorkAreaTarget_Full(this,w,h));
2279
2280                 trgt->set_rend_desc(&desc);
2281                 trgt->set_onion_skin(get_onion_skin());
2282                 target=trgt;
2283         }
2284
2285         // We can rest assured that our time has already
2286         // been set, so there is no need to have to
2287         // recalculate that over again.
2288         // UPDATE: This is kind of needless with
2289         // the way that time is handled now in SYNFIG.
2290         //target->set_avoid_time_sync(true);
2291         async_renderer=new AsyncRenderer(target);
2292         async_renderer->signal_finished().connect(
2293                 sigc::mem_fun(this,&WorkArea::async_update_finished)
2294         );
2295
2296         rendering=true;
2297         async_renderer->start();
2298
2299         synfig::ProgressCallback *cb=get_canvas_view()->get_ui_interface().get();
2300
2301         rendering=true;
2302         cb->task(_("Rendering..."));
2303         rendering=true;
2304
2305         return true;
2306 }
2307
2308 void
2309 studio::WorkArea::async_update_finished()
2310 {
2311         synfig::ProgressCallback *cb=get_canvas_view()->get_ui_interface().get();
2312
2313         rendering=false;
2314
2315         if(!async_renderer)
2316                 return;
2317
2318         // If we completed successfully, then
2319         // we aren't dirty anymore
2320         if(async_renderer->has_success())
2321         {
2322                 dirty=false;
2323                 //queued=false;
2324                 cb->task(_("Idle"));
2325         }
2326         else
2327         {
2328                 dirty=true;
2329                 cb->task(_("Render Failed"));
2330         }
2331         //get_canvas_view()->reset_cancel_status();
2332         done_rendering();
2333 }
2334
2335 bool
2336 studio::WorkArea::sync_update_preview()
2337 {
2338         //      const Time &time(cur_time);
2339
2340         canceled_=false;
2341         get_canvas_view()->reset_cancel_status();
2342
2343         async_renderer=0;
2344
2345 again:
2346         // This object will mark us as busy until
2347         // we are done.
2348         studio::App::Busy busy;
2349
2350         WorkAreaProgress callback(this,get_canvas_view()->get_ui_interface().get());
2351         synfig::ProgressCallback *cb=&callback;
2352
2353         // We don't want to render if we are already rendering
2354         if(rendering)
2355         {
2356                 dirty=true;
2357                 return false;
2358         }
2359
2360         if(!is_visible())return false;
2361         get_canvas()->set_time(get_time());
2362         get_canvas_view()->get_smach().process_event(EVENT_REFRESH_DUCKS);
2363         signal_rendering()();
2364
2365         // If we are queued to render the scene at the next idle
2366         // go ahead and de-queue it.
2367         if(render_idle_func_id)
2368         {
2369                 g_source_remove(render_idle_func_id);
2370                 //queued=false;
2371                 render_idle_func_id=0;
2372         }
2373         // Start rendering
2374         rendering=true;
2375
2376         dirty=false;
2377         get_canvas_view()->reset_cancel_status();
2378
2379         RendDesc desc=get_canvas()->rend_desc();
2380         //newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2381
2382         int w=(int)(desc.get_w()*zoom);
2383         int h=(int)(desc.get_h()*zoom);
2384
2385         // Setup the description parameters
2386         desc.set_antialias(1);
2387         desc.set_time(cur_time);
2388         //desc.set_wh(w,h);
2389
2390         set_rend_desc(desc);
2391
2392         // Create the render target
2393         handle<WorkAreaTarget> target(new class WorkAreaTarget(this,w,h));
2394
2395         target->set_rend_desc(&desc);
2396
2397         // We can rest assured that our time has already
2398         // been set, so there is no need to have to
2399         // recalculate that over again.
2400         target->set_avoid_time_sync(true);
2401
2402         if(cb)
2403                 cb->task(strprintf(_("Rendering canvas %s..."),get_canvas()->get_name().c_str()));
2404
2405         bool ret = target->render(cb);
2406
2407         if(!ret && !get_canvas_view()->get_cancel_status() && dirty)
2408         {
2409                 rendering=false;
2410                 //canceled_=true;
2411                 goto again;
2412         }
2413         if(get_canvas_view()->get_cancel_status())
2414                 canceled_=true;
2415
2416         if(cb)
2417         {
2418                 if(ret)
2419                         cb->task(_("Idle"));
2420                 else
2421                         cb->task(_("Render Failed"));
2422                 cb->amount_complete(0,1);
2423         }
2424
2425         // Refresh the work area to make sure that
2426         // it is being displayed correctly
2427         drawing_area->queue_draw();
2428
2429         // If we completed successfully, then
2430         // we aren't dirty anymore
2431         if(ret)
2432         {
2433                 dirty=false;
2434                 //queued=false;
2435         }
2436         else dirty=true;
2437         rendering=false;
2438         //get_canvas_view()->reset_cancel_status();
2439         done_rendering();
2440         return ret;
2441 }
2442
2443 void
2444 studio::WorkArea::async_render_preview(synfig::Time time)
2445 {
2446         cur_time=time;
2447         //tile_book.clear();
2448
2449         refreshes+=5;
2450         if(!is_visible())return;
2451
2452         get_canvas()->set_time(get_time());
2453         get_canvas_view()->get_smach().process_event(EVENT_REFRESH_DUCKS);
2454         signal_rendering()();
2455
2456         async_update_preview();
2457 }
2458 void
2459 WorkArea::async_render_preview()
2460 {
2461         return async_render_preview(get_canvas_view()->get_time());
2462 }
2463
2464 bool
2465 studio::WorkArea::sync_render_preview(synfig::Time time)
2466 {
2467         cur_time=time;
2468         //tile_book.clear();
2469         refreshes+=5;
2470         if(!is_visible())return false;
2471         return sync_update_preview();
2472 }
2473
2474 bool
2475 WorkArea::sync_render_preview()
2476 {
2477         return sync_render_preview(get_canvas_view()->get_time());
2478 }
2479
2480 void
2481 WorkArea::sync_render_preview_hook()
2482 {
2483         sync_render_preview(get_canvas_view()->get_time());
2484 }
2485
2486 void
2487 WorkArea::queue_scroll()
2488 {
2489 //      const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
2490
2491         const synfig::Point focus_point(get_focus_point());
2492
2493         const synfig::Real
2494                 new_x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
2495                 new_y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2496
2497         const synfig::Real
2498                 old_x(last_focus_point[0]/pw+drawing_area->get_width()/2-w/2),
2499                 old_y(last_focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2500
2501         // If the coordinates didn't change, we shouldn't queue a draw
2502         if(old_x==new_x && old_y==new_y)
2503                 return;
2504
2505         const int
2506                 dx(round_to_int(old_x)-round_to_int(new_x)),
2507                 dy(round_to_int(old_y)-round_to_int(new_y));
2508
2509         drawing_area->get_window()->scroll(-dx,-dy);
2510
2511         if (timecode_width && timecode_height)
2512         {
2513                 drawing_area->queue_draw_area(4,       4,    4+timecode_width,    4+timecode_height);
2514                 drawing_area->queue_draw_area(4-dx, 4-dy, 4-dx+timecode_width, 4-dy+timecode_height);
2515         }
2516
2517 #ifndef USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
2518         if(canvas_interface->get_mode()&synfigapp::MODE_ANIMATE)
2519         {
2520                 int maxx = drawing_area->get_width()-1;
2521                 int maxy = drawing_area->get_height()-1;
2522
2523                 if (dx > 0)
2524                 {
2525                         drawing_area->queue_draw_area(      0, 0,       1, maxy);
2526                         drawing_area->queue_draw_area(maxx-dx, 0, maxx-dx, maxy);
2527                 }
2528                 else if (dx < 0) 
2529                 {
2530                         drawing_area->queue_draw_area(   maxx, 0,    maxx, maxy);
2531                         drawing_area->queue_draw_area(    -dx, 0,     -dx, maxy);
2532                 }
2533                 if (dy > 0)
2534                 {
2535                         drawing_area->queue_draw_area(0,       0, maxx,       1);
2536                         drawing_area->queue_draw_area(0, maxy-dy, maxx, maxy-dy);
2537                 }
2538                 else if (dy < 0) 
2539                 {
2540                         drawing_area->queue_draw_area(0,    maxy, maxx,    maxy);
2541                         drawing_area->queue_draw_area(0,     -dy, maxx,     -dy);
2542                 }
2543         }
2544 #endif // USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
2545
2546         last_focus_point=focus_point;
2547 }
2548
2549 void
2550 studio::WorkArea::zoom_in()
2551 {
2552         set_zoom(zoom*1.25);
2553 }
2554
2555 void
2556 studio::WorkArea::zoom_out()
2557 {
2558         set_zoom(zoom/1.25);
2559 }
2560
2561 void
2562 studio::WorkArea::zoom_fit()
2563 {
2564         // This really doesn't zoom to fit. Bug.
2565         zoom_norm();
2566 }
2567
2568 void
2569 studio::WorkArea::zoom_norm()
2570 {
2571         if(zoom==1.0)
2572                 set_zoom(prev_zoom);
2573         else
2574         {
2575                 prev_zoom=zoom;
2576                 set_zoom(1.0f);
2577         }
2578 }
2579
2580 gboolean
2581 studio::WorkArea::__render_preview(gpointer data)
2582 {
2583         WorkArea *work_area(static_cast<WorkArea*>(data));
2584
2585         // there's no point anyone trying to cancel the timer now - it's gone off already
2586         work_area->render_idle_func_id = 0;
2587
2588         work_area->queued=false;
2589         work_area->async_render_preview(work_area->get_canvas_view()->get_time());
2590
2591         return 0;
2592 }
2593
2594 void
2595 studio::WorkArea::queue_render_preview()
2596 {
2597         //synfig::info("queue_render_preview(): called for %s", get_canvas_view()->get_time().get_string().c_str());
2598
2599         if(queued==true)
2600         {
2601                 return;
2602                 //synfig::info("queue_render_preview(): already queued, unqueuing");
2603 /*              if(render_idle_func_id)
2604                         g_source_remove(render_idle_func_id);
2605                 render_idle_func_id=0;
2606                 queued=false;
2607 */
2608                 //async_renderer=0;
2609         }
2610
2611         if(dirty_trap_enabled)
2612         {
2613                 dirty_trap_queued++;
2614                 return;
2615         }
2616
2617         int queue_time=50;
2618
2619         if(rendering)
2620                 queue_time+=250;
2621
2622
2623         if(queued==false)
2624         {
2625                 //synfig::info("queue_render_preview(): (re)queuing...");
2626                 //render_idle_func_id=g_idle_add_full(G_PRIORITY_DEFAULT,__render_preview,this,NULL);
2627                 render_idle_func_id=g_timeout_add_full(
2628                         G_PRIORITY_DEFAULT,     // priority - 
2629                         queue_time,                     // interval - the time between calls to the function, in milliseconds (1/1000ths of a second)
2630                         __render_preview,       // function - function to call
2631                         this,                           // data     - data to pass to function
2632                         NULL);                          // notify   - function to call when the idle is removed, or NULL
2633                 queued=true;
2634         }
2635 /*      else if(rendering)
2636         {
2637                 refreshes+=5;
2638                 dirty=true;
2639                 queue_draw();
2640         }
2641 */
2642 }
2643
2644 DirtyTrap::DirtyTrap(WorkArea *work_area):work_area(work_area)
2645 {
2646         work_area->dirty_trap_enabled=true;
2647
2648         work_area->dirty_trap_queued=0;
2649 }
2650
2651 DirtyTrap::~DirtyTrap()
2652 {
2653         work_area->dirty_trap_enabled=false;
2654         if(work_area->dirty_trap_queued)
2655                 work_area->queue_render_preview();
2656 }
2657
2658 void
2659 studio::WorkArea::queue_draw_preview()
2660 {
2661         drawing_area->queue_draw();
2662 }
2663
2664 void
2665 studio::WorkArea::set_cursor(const Gdk::Cursor& x)
2666 {
2667         drawing_area->get_window()->set_cursor(x);
2668 }
2669 void
2670 studio::WorkArea::set_cursor(Gdk::CursorType x)
2671 {
2672         drawing_area->get_window()->set_cursor(Gdk::Cursor(x));
2673 }
2674
2675 #include "iconcontroller.h"
2676
2677 void
2678 studio::WorkArea::refresh_cursor()
2679 {
2680 //      set_cursor(IconController::get_tool_cursor(canvas_view->get_smach().get_state_name(),drawing_area->get_window()));
2681 }
2682
2683 void
2684 studio::WorkArea::reset_cursor()
2685 {
2686         drawing_area->get_window()->set_cursor(Gdk::Cursor(Gdk::TOP_LEFT_ARROW));
2687 //      set_cursor(Gdk::TOP_LEFT_ARROW);
2688 }
2689
2690 void
2691 studio::WorkArea::set_zoom(float z)
2692 {
2693         z=max(1.0f/128.0f,min(128.0f,z));
2694         if(z==zoom)
2695                 return;
2696         zoom = z;
2697         refresh_dimension_info();
2698         /*if(async_renderer)
2699         {
2700                 async_renderer->stop();
2701                 async_renderer=0;
2702         }*/
2703         refreshes+=5;
2704         async_update_preview();
2705         //queue_render_preview();
2706 }
2707
2708 void
2709 WorkArea::set_selected_value_node(etl::loose_handle<synfig::ValueNode> x)
2710 {
2711         if(x!=selected_value_node_)
2712         {
2713                 selected_value_node_=x;
2714                 queue_draw();
2715         }
2716 }
2717
2718 void
2719 WorkArea::insert_renderer(const etl::handle<WorkAreaRenderer> &x)
2720 {
2721         renderer_set_.insert(x);
2722         x->set_work_area(this);
2723         queue_draw();
2724 }
2725
2726 void
2727 WorkArea::insert_renderer(const etl::handle<WorkAreaRenderer> &x, int priority)
2728 {
2729         x->set_priority(priority);
2730         insert_renderer(x);
2731 }
2732
2733 void
2734 WorkArea::erase_renderer(const etl::handle<WorkAreaRenderer> &x)
2735 {
2736         x->set_work_area(0);
2737         renderer_set_.erase(x);
2738         queue_draw();
2739 }
2740
2741 void
2742 WorkArea::resort_render_set()
2743 {
2744         std::set<etl::handle<WorkAreaRenderer> > tmp(
2745                 renderer_set_.begin(),
2746                 renderer_set_.end()
2747         );
2748         renderer_set_.swap(tmp);
2749         queue_draw();
2750 }