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