Add support for scrolling left and right in the canvas window. Ctrl+LeftScroll zooms...
[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                                 case GDK_SCROLL_RIGHT:
1693                                         get_scrollx_adjustment()->set_value(scroll_point[0]+(mouse_pos[0]-scroll_point[0])*(1.25-(1+drift)));
1694                                         get_scrolly_adjustment()->set_value(scroll_point[1]-(mouse_pos[1]+scroll_point[1])*(1.25-(1+drift)));
1695                                         zoom_in();
1696                                         break;
1697                                 case GDK_SCROLL_DOWN:
1698                                 case GDK_SCROLL_LEFT:
1699                                         get_scrollx_adjustment()->set_value(scroll_point[0]+(mouse_pos[0]-scroll_point[0])*(1/1.25-(1+drift)));
1700                                         get_scrolly_adjustment()->set_value(scroll_point[1]-(mouse_pos[1]+scroll_point[1])*(1/1.25-(1+drift)));
1701                                         zoom_out();
1702                                         break;
1703                                 default:
1704                                         break;
1705                         }
1706                 }
1707                 else if(modifier&GDK_SHIFT_MASK)
1708                 {
1709                         // Scroll in either direction by 20 pixels. Ideally, the
1710                         // amount of pixels per scrolling event should be
1711                         // configurable. Xara Xtreme currently uses an (hard
1712                         // wired) amount 20 pixel, Inkscape defaults to 40 pixels.
1713
1714                         const int scroll_pixel = 20;
1715
1716                         switch(event->scroll.direction)
1717                         {
1718                                 case GDK_SCROLL_UP:
1719                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()-scroll_pixel*pw);
1720                                         break;
1721                                 case GDK_SCROLL_DOWN:
1722                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()+scroll_pixel*pw);
1723                                         break;
1724                                 case GDK_SCROLL_LEFT:
1725                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()+scroll_pixel*ph);
1726                                         break;
1727                                 case GDK_SCROLL_RIGHT:
1728                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()-scroll_pixel*ph);
1729                                         break;
1730                                 default:
1731                                         break;
1732                         }
1733                 }
1734                 else
1735                 {
1736                         // Scroll in either direction by 20 pixels. Ideally, the
1737                         // amount of pixels per scrolling event should be
1738                         // configurable. Xara Xtreme currently uses an (hard
1739                         // wired) amount 20 pixel, Inkscape defaults to 40 pixels.
1740
1741                         const int scroll_pixel = 20;
1742
1743                         switch(event->scroll.direction)
1744                         {
1745                                 case GDK_SCROLL_UP:
1746                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()+scroll_pixel*ph);
1747                                         break;
1748                                 case GDK_SCROLL_DOWN:
1749                                         get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()-scroll_pixel*ph);
1750                                         break;
1751                                 case GDK_SCROLL_LEFT:
1752                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()-scroll_pixel*pw);
1753                                         break;
1754                                 case GDK_SCROLL_RIGHT:
1755                                         get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()+scroll_pixel*pw);
1756                                         break;
1757                                 default:
1758                                         break;
1759                         }
1760                 }
1761         }
1762                 break;
1763         default:
1764                 break;
1765         }
1766                 return false;
1767 }
1768
1769 bool
1770 WorkArea::on_hruler_event(GdkEvent *event)
1771 {
1772 /*
1773         switch(event->type)
1774     {
1775         case GDK_BUTTON_PRESS:
1776                 if(dragging==DRAG_NONE)
1777                 {
1778                         dragging=DRAG_GUIDE;
1779                         curr_guide=get_guide_list_y().insert(get_guide_list_y().begin());
1780                         curr_guide_is_x=false;
1781                 }
1782                 return true;
1783                 break;
1784
1785         case GDK_MOTION_NOTIFY:
1786                 // Guide movement
1787                 if(dragging==DRAG_GUIDE && curr_guide_is_x==false)
1788                 {
1789                         double y,x;
1790                         if(event->button.axes)
1791                         {
1792                                 x=(event->button.axes[0]);
1793                                 y=(event->button.axes[1]);
1794                         }
1795                         else
1796                         {
1797                                 x=event->button.x;
1798                                 y=event->button.y;
1799                         }
1800
1801                         if(isnan(y) || isnan(x))
1802                                 return false;
1803
1804                         *curr_guide=synfig::Point(screen_to_comp_coords(synfig::Point(x,y)))[1];
1805
1806                         queue_draw();
1807                 }
1808                 return true;
1809                 break;
1810
1811         case GDK_BUTTON_RELEASE:
1812                 if(dragging==DRAG_GUIDE && curr_guide_is_x==false)
1813                 {
1814                         dragging=DRAG_NONE;
1815                         get_guide_list_y().erase(curr_guide);
1816                 }
1817                 break;
1818                 return true;
1819         default:
1820                 break;
1821         }
1822 */
1823         return false;
1824 }
1825
1826 bool
1827 WorkArea::on_vruler_event(GdkEvent *event)
1828 {
1829 /*
1830         switch(event->type)
1831     {
1832         case GDK_BUTTON_PRESS:
1833                 DEBUGPOINT();
1834                 if(dragging==DRAG_NONE)
1835                 {
1836                         DEBUGPOINT();
1837                         dragging=DRAG_GUIDE;
1838                         curr_guide=get_guide_list_x().insert(get_guide_list_x().begin());
1839                         curr_guide_is_x=true;
1840                 }
1841                 return true;
1842                 break;
1843         case GDK_BUTTON_RELEASE:
1844                 DEBUGPOINT();
1845                 if(dragging==DRAG_GUIDE && curr_guide_is_x==true)
1846                 {
1847                         DEBUGPOINT();
1848                         dragging=DRAG_NONE;
1849                         get_guide_list_x().erase(curr_guide);
1850                 }
1851                 break;
1852                 return true;
1853         default:
1854                 break;
1855         }
1856 */
1857         return false;
1858 }
1859
1860
1861 void
1862 WorkArea::refresh_dimension_info()
1863 {
1864         synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1865
1866         canvaswidth=rend_desc.get_br()[0]-rend_desc.get_tl()[0];
1867         canvasheight=rend_desc.get_br()[1]-rend_desc.get_tl()[1];
1868
1869         pw=canvaswidth/w;
1870         ph=canvasheight/h;
1871
1872         scrollx_adjustment.set_page_increment(abs(get_grid_size()[0]));
1873         scrollx_adjustment.set_step_increment(abs(pw));
1874         scrollx_adjustment.set_lower(-abs(canvaswidth));
1875         scrollx_adjustment.set_upper(abs(canvaswidth));
1876         scrolly_adjustment.set_lower(-abs(canvasheight));
1877         scrolly_adjustment.set_upper(abs(canvasheight));
1878         scrolly_adjustment.set_step_increment(abs(ph));
1879         scrolly_adjustment.set_page_increment(abs(get_grid_size()[1]));
1880
1881
1882
1883         if(drawing_area->get_width()<=0 || drawing_area->get_height()<=0 || w==0 || h==0)
1884                 return;
1885
1886         const synfig::Point focus_point(get_focus_point());
1887         const synfig::Real x(focus_point[0]/pw+drawing_area->get_width()/2-w/2);
1888         const synfig::Real y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
1889
1890         window_tl[0]=rend_desc.get_tl()[0]-pw*x;
1891         window_br[0]=rend_desc.get_br()[0]+pw*(drawing_area->get_width()-x-w);
1892
1893         window_tl[1]=rend_desc.get_tl()[1]-ph*y;
1894         window_br[1]=rend_desc.get_br()[1]+ph*(drawing_area->get_height()-y-h);
1895
1896         hruler->property_lower()=Distance(window_tl[0],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1897         hruler->property_upper()=Distance(window_br[0],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1898         vruler->property_lower()=Distance(window_tl[1],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1899         vruler->property_upper()=Distance(window_br[1],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc);
1900
1901         view_window_changed();
1902 }
1903
1904
1905 synfig::Point
1906 WorkArea::screen_to_comp_coords(synfig::Point pos)const
1907 {
1908         synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1909         //synfig::Vector::value_type canvaswidth=rend_desc.get_br()[0]-rend_desc.get_tl()[0];
1910         //synfig::Vector::value_type canvasheight=rend_desc.get_br()[1]-rend_desc.get_tl()[1];
1911         //synfig::Vector::value_type pw=canvaswidth/w;
1912         //synfig::Vector::value_type ph=canvasheight/h;
1913         Vector focus_point=get_focus_point();
1914         synfig::Vector::value_type x=focus_point[0]/pw+drawing_area->get_width()/2-w/2;
1915         synfig::Vector::value_type y=focus_point[1]/ph+drawing_area->get_height()/2-h/2;
1916
1917         return rend_desc.get_tl()-synfig::Point(pw*x,ph*y)+synfig::Point(pw*pos[0],ph*pos[1]);
1918 }
1919
1920 synfig::Point
1921 WorkArea::comp_to_screen_coords(synfig::Point pos)const
1922 {
1923         synfig::warning("WorkArea::comp_to_screen_coords: Not yet implemented");
1924         return synfig::Point();
1925 }
1926
1927 int
1928 WorkArea::next_unrendered_tile(int refreshes)const
1929 {
1930         //assert(!tile_book.empty());
1931         if(tile_book.empty())
1932                 return -1;
1933
1934         //const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1935
1936         const synfig::Vector focus_point(get_focus_point());
1937
1938         // Calculate the window coordinates of the top-left
1939         // corner of the canvas.
1940         const synfig::Vector::value_type
1941                 x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
1942                 y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
1943
1944         const int width_in_tiles(w/tile_w+(w%tile_w?1:0));
1945         const int height_in_tiles(h/tile_h+(h%tile_h?1:0));
1946
1947         int
1948                 u(0),v(0),
1949                 u1(int(-x/tile_w)),
1950                 v1(int(-y/tile_h)),
1951                 u2(int((-x+drawing_area->get_width())/tile_w+1)),
1952                 v2(int((-y+drawing_area->get_height())/tile_h+1));
1953
1954         if(u2>width_in_tiles)u2=width_in_tiles;
1955         if(v2>height_in_tiles)v2=height_in_tiles;
1956         if(u1<0)u1=0;
1957         if(v1<0)v1=0;
1958
1959         int last_good_tile(-1);
1960
1961         for(v=v1;v<v2;v++)
1962                 for(u=u1;u<u2;u++)
1963                 {
1964                         int index(v*width_in_tiles+u);
1965                         if(tile_book[index].second<refreshes)
1966                         {
1967                                 last_good_tile=index;
1968                                 if(rand()%8==0)
1969                                         return index;
1970                         }
1971                 }
1972         return last_good_tile;
1973 }
1974
1975 /*
1976 template <typename F, typename T=WorkAreaRenderer, typename R=typename F::result_type>
1977 class handle2ptr_t : public std::unary_function<typename etl::handle<T>,R>
1978 {
1979 private:
1980         F func;
1981 public:
1982         handle2ptr_t(const F &func):func(func) { };
1983
1984         R operator()(typename etl::handle<T> x) { return func(*x); }
1985 };
1986
1987 template <typename F>
1988 handle2ptr_t<F>
1989 handle2ptr(F func)
1990 {
1991         return handle2ptr_t<F>(func);
1992 }
1993         for_each(
1994                 renderer_set_.begin(),
1995                 renderer_set_.end(),
1996                 handle2ptr(
1997                         sigc::bind(
1998                                 sigc::bind(
1999                                         sigc::mem_fun(
2000                                                 &WorkAreaRenderer::render_vfunc
2001                                         ),
2002                                         Gdk::Rectangle(event->area)
2003                                 ),
2004                                 drawing_area->get_window()
2005                         )
2006                 )
2007         );
2008 */
2009
2010 bool
2011 WorkArea::refresh(GdkEventExpose*event)
2012 {
2013         assert(get_canvas());
2014
2015         drawing_area->get_window()->clear();
2016
2017         //const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
2018
2019         const synfig::Vector focus_point(get_focus_point());
2020
2021         // Update the old focus point
2022         last_focus_point=focus_point;
2023
2024         // Draw out the renderables
2025         {
2026                 std::set<etl::handle<WorkAreaRenderer> >::iterator iter;
2027                 for(iter=renderer_set_.begin();iter!=renderer_set_.end();++iter)
2028                 {
2029                         if((*iter)->get_enabled())
2030                                 (*iter)->render_vfunc(
2031                                         drawing_area->get_window(),
2032                                         Gdk::Rectangle(&event->area)
2033                                 );
2034                 }
2035         }
2036
2037
2038         // Calculate the window coordinates of the top-left
2039         // corner of the canvas.
2040         //const synfig::Vector::value_type
2041         //      x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
2042         //      y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2043
2044         //const synfig::Vector::value_type window_startx(window_tl[0]);
2045         //const synfig::Vector::value_type window_endx(window_br[0]);
2046         //const synfig::Vector::value_type window_starty(window_tl[1]);
2047         //const synfig::Vector::value_type window_endy(window_br[1]);
2048
2049         Glib::RefPtr<Gdk::GC> gc=Gdk::GC::create(drawing_area->get_window());
2050
2051
2052
2053         // If we are in animate mode, draw a red border around the screen
2054         if(canvas_interface->get_mode()&synfigapp::MODE_ANIMATE)
2055         {
2056                 /*gc->set_rgb_fg_color(Gdk::Color("#FF0000"));
2057                 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
2058                 drawing_area->get_window()->draw_rectangle(
2059                         gc,
2060                         false,  // Fill?
2061                         0,0,    // x,y
2062                         drawing_area->get_width()-1,drawing_area->get_height()-1        //w,h
2063                 );
2064                 */
2065                 drawing_frame->modify_bg(Gtk::STATE_NORMAL,Gdk::Color("#FF0000"));
2066                 //get_window()->set_background(Gdk::Color("#FF0000"));
2067         }
2068         else
2069                 drawing_frame->unset_bg(Gtk::STATE_NORMAL);
2070
2071
2072
2073         previous_focus=get_focus_point();
2074
2075         return true;
2076 }
2077
2078 void
2079 WorkArea::done_rendering()
2080 {
2081 /*
2082         assert(buffer);
2083         assert(w>0);
2084         assert(h>0);
2085         pix_buf=Gdk::Pixbuf::create_from_data(
2086                 buffer, // pointer to the data
2087                 Gdk::COLORSPACE_RGB, // the colorspace
2088                 true, // has alpha?
2089                 8, // bits per sample
2090                 w,      // width
2091                 h,      // height
2092                 w*bpp); // stride (pitch)
2093         assert(pix_buf);
2094 */
2095 }
2096
2097
2098 void
2099 WorkArea::set_quality(int x)
2100 {
2101         if(x==quality)
2102                 return;
2103         quality=x;
2104         queue_render_preview();
2105 }
2106
2107
2108 class WorkAreaProgress : public synfig::ProgressCallback
2109 {
2110         WorkArea *work_area;
2111         ProgressCallback *cb;
2112
2113 public:
2114
2115         WorkAreaProgress(WorkArea *work_area,ProgressCallback *cb):
2116                 work_area(work_area),cb(cb)
2117         {
2118                 assert(cb);
2119         }
2120
2121         virtual bool
2122         task(const std::string &str)
2123         {
2124                 if(work_area->dirty)
2125                         return false;
2126                 return cb->task(str);
2127         }
2128
2129         virtual bool
2130         error(const std::string &err)
2131         {
2132                 if(work_area->dirty)
2133                         return false;
2134                 return cb->error(err);
2135         }
2136
2137         virtual bool
2138         amount_complete(int current, int total)
2139         {
2140                 if(work_area->dirty)
2141                         return false;
2142                 return cb->amount_complete(current,total);
2143         }
2144 };
2145
2146 bool
2147 studio::WorkArea::async_update_preview()
2148 {
2149         async_renderer=0;
2150
2151         queued=false;
2152         canceled_=false;
2153         get_canvas_view()->reset_cancel_status();
2154
2155         // This object will mark us as busy until
2156         // we are done.
2157         //studio::App::Busy busy;
2158
2159         //WorkAreaProgress callback(this,get_canvas_view()->get_ui_interface().get());
2160         //synfig::ProgressCallback *cb=&callback;
2161
2162         if(!is_visible())return false;
2163
2164         /*
2165         // If we are queued to render the scene at the next idle
2166         // go ahead and de-queue it.
2167         if(render_idle_func_id)
2168         {
2169                 g_source_remove(render_idle_func_id);
2170                 //queued=false;
2171                 render_idle_func_id=0;
2172         }
2173         */
2174
2175         dirty=false;
2176         get_canvas_view()->reset_cancel_status();
2177
2178         //bool ret=false;
2179         RendDesc desc=get_canvas()->rend_desc();
2180
2181         int w=(int)(desc.get_w()*zoom);
2182         int h=(int)(desc.get_h()*zoom);
2183
2184         // Setup the description parameters
2185         desc.set_antialias(1);
2186         desc.set_time(cur_time);
2187
2188         set_rend_desc(desc);
2189
2190         // Create the render target
2191         handle<Target> target;
2192
2193         if(w*h>(low_resolution?480*270:480*270/2))
2194         {
2195                 handle<WorkAreaTarget> trgt(new class WorkAreaTarget(this,w,h));
2196
2197                 trgt->set_rend_desc(&desc);
2198                 trgt->set_onion_skin(get_onion_skin());
2199                 target=trgt;
2200         }
2201         else
2202         {
2203                 handle<WorkAreaTarget_Full> trgt(new class WorkAreaTarget_Full(this,w,h));
2204
2205                 trgt->set_rend_desc(&desc);
2206                 trgt->set_onion_skin(get_onion_skin());
2207                 target=trgt;
2208         }
2209
2210         // We can rest assured that our time has already
2211         // been set, so there is no need to have to
2212         // recalculate that over again.
2213         // UPDATE: This is kind of needless with
2214         // the way that time is handled now in SYNFIG.
2215         //target->set_avoid_time_sync(true);
2216         async_renderer=new AsyncRenderer(target);
2217         async_renderer->signal_finished().connect(
2218                 sigc::mem_fun(this,&WorkArea::async_update_finished)
2219         );
2220
2221         rendering=true;
2222         async_renderer->start();
2223
2224         synfig::ProgressCallback *cb=get_canvas_view()->get_ui_interface().get();
2225
2226         rendering=true;
2227         cb->task("Rendering...");
2228         rendering=true;
2229
2230         return true;
2231 }
2232
2233 void
2234 studio::WorkArea::async_update_finished()
2235 {
2236         synfig::ProgressCallback *cb=get_canvas_view()->get_ui_interface().get();
2237
2238         rendering=false;
2239
2240         if(!async_renderer)
2241                 return;
2242
2243         // If we completed successfuly, then
2244         // we aren't dirty anymore
2245         if(async_renderer->has_success())
2246         {
2247                 dirty=false;
2248                 //queued=false;
2249                 cb->task("Idle");
2250         }
2251         else
2252         {
2253                 dirty=true;
2254                 cb->task("Render Failed");
2255         }
2256         //get_canvas_view()->reset_cancel_status();
2257         done_rendering();
2258 }
2259
2260 bool
2261 studio::WorkArea::sync_update_preview()
2262 {
2263         //      const Time &time(cur_time);
2264
2265         canceled_=false;
2266         get_canvas_view()->reset_cancel_status();
2267
2268         async_renderer=0;
2269
2270 again:
2271         // This object will mark us as busy until
2272         // we are done.
2273         studio::App::Busy busy;
2274
2275         WorkAreaProgress callback(this,get_canvas_view()->get_ui_interface().get());
2276         synfig::ProgressCallback *cb=&callback;
2277
2278         // We don't want to render if we are already rendering
2279         if(rendering)
2280         {
2281                 dirty=true;
2282                 return false;
2283         }
2284
2285         if(!is_visible())return false;
2286         get_canvas()->set_time(get_time());
2287         get_canvas_view()->get_smach().process_event(EVENT_REFRESH_DUCKS);
2288         signal_rendering()();
2289
2290         // If we are queued to render the scene at the next idle
2291         // go ahead and de-queue it.
2292         if(render_idle_func_id)
2293         {
2294                 g_source_remove(render_idle_func_id);
2295                 //queued=false;
2296                 render_idle_func_id=0;
2297         }
2298         // Start rendering
2299         rendering=true;
2300
2301         dirty=false;
2302         get_canvas_view()->reset_cancel_status();
2303
2304         bool ret=false;
2305         RendDesc desc=get_canvas()->rend_desc();
2306         //newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2307
2308         int w=(int)(desc.get_w()*zoom);
2309         int h=(int)(desc.get_h()*zoom);
2310
2311         // Setup the description parameters
2312         desc.set_antialias(1);
2313         desc.set_time(cur_time);
2314         //desc.set_wh(w,h);
2315
2316         set_rend_desc(desc);
2317
2318         // Create the render target
2319         handle<WorkAreaTarget> target(new class WorkAreaTarget(this,w,h));
2320
2321         target->set_rend_desc(&desc);
2322
2323         // We can rest assured that our time has already
2324         // been set, so there is no need to have to
2325         // recalculate that over again.
2326         target->set_avoid_time_sync(true);
2327
2328         if(cb)
2329                 cb->task(strprintf("Rendering canvas %s...",get_canvas()->get_name().c_str()));
2330
2331         target->render(cb);
2332
2333         if(!ret && !get_canvas_view()->get_cancel_status() && dirty)
2334         {
2335                 rendering=false;
2336                 //canceled_=true;
2337                 goto again;
2338         }
2339         if(get_canvas_view()->get_cancel_status())
2340                 canceled_=true;
2341
2342         if(cb)
2343         {
2344                 if(ret)
2345                         cb->task("Idle");
2346                 else
2347                         cb->task("Render Failed");
2348                 cb->amount_complete(0,1);
2349         }
2350
2351         // Refresh the work area to make sure that
2352         // it is being displayed correctly
2353         drawing_area->queue_draw();
2354
2355         // If we completed successfuly, then
2356         // we aren't dirty anymore
2357         if(ret)
2358         {
2359                 dirty=false;
2360                 //queued=false;
2361         }
2362         else dirty=true;
2363         rendering=false;
2364         //get_canvas_view()->reset_cancel_status();
2365         done_rendering();
2366         return ret;
2367 }
2368
2369 void
2370 studio::WorkArea::async_render_preview(Time time)
2371 {
2372         cur_time=time;
2373         //tile_book.clear();
2374
2375         refreshes+=5;
2376         if(!is_visible())return;
2377
2378         get_canvas()->set_time(get_time());
2379         get_canvas_view()->get_smach().process_event(EVENT_REFRESH_DUCKS);
2380         signal_rendering()();
2381
2382         async_update_preview();
2383 }
2384 void
2385 WorkArea::async_render_preview()
2386 {
2387         return async_render_preview(get_canvas_view()->get_time());
2388 }
2389
2390 bool
2391 studio::WorkArea::sync_render_preview(Time time)
2392 {
2393         cur_time=time;
2394         //tile_book.clear();
2395         refreshes+=5;
2396         if(!is_visible())return false;
2397         return sync_update_preview();
2398 }
2399
2400 bool
2401 WorkArea::sync_render_preview()
2402 {
2403         return sync_render_preview(get_canvas_view()->get_time());
2404 }
2405
2406 void
2407 WorkArea::sync_render_preview_hook()
2408 {
2409         sync_render_preview(get_canvas_view()->get_time());
2410 }
2411
2412 void
2413 WorkArea::queue_scroll()
2414 {
2415 //      const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
2416
2417         const synfig::Point focus_point(get_focus_point());
2418
2419         const synfig::Real
2420                 new_x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
2421                 new_y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2422
2423         const synfig::Real
2424                 old_x(last_focus_point[0]/pw+drawing_area->get_width()/2-w/2),
2425                 old_y(last_focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2426
2427         // If the coordinates didn't change, we shouldn't queue a draw
2428         if(old_x==new_x && old_y==new_y)
2429                 return;
2430
2431         const int
2432                 dx(round_to_int(old_x)-round_to_int(new_x)),
2433                 dy(round_to_int(old_y)-round_to_int(new_y));
2434
2435         drawing_area->get_window()->scroll(-dx,-dy);
2436
2437         /*drawing_area->queue_draw_area(
2438                 0,
2439                 0,
2440                 128,
2441                 64
2442         );
2443         */
2444         last_focus_point=focus_point;
2445 }
2446
2447 void
2448 studio::WorkArea::zoom_in()
2449 {
2450         set_zoom(zoom*1.25);
2451 }
2452
2453 void
2454 studio::WorkArea::zoom_out()
2455 {
2456         set_zoom(zoom/1.25);
2457 }
2458
2459 void
2460 studio::WorkArea::zoom_fit()
2461 {
2462         // This really doesn't zoom to fit. Bug.
2463         zoom_norm();
2464 }
2465
2466 void
2467 studio::WorkArea::zoom_norm()
2468 {
2469         if(zoom==1.0)
2470                 set_zoom(prev_zoom);
2471         else
2472         {
2473                 prev_zoom=zoom;
2474                 set_zoom(1.0f);
2475         }
2476 }
2477
2478 gboolean
2479 studio::WorkArea::__render_preview(gpointer data)
2480 {
2481
2482         WorkArea *work_area(static_cast<WorkArea*>(data));
2483
2484         work_area->queued=false;
2485         work_area->async_render_preview(work_area->get_canvas_view()->get_time());
2486
2487         return 0;
2488 }
2489
2490 void
2491 studio::WorkArea::queue_render_preview()
2492 {
2493         //synfig::info("queue_render_preview(): called for %s", get_canvas_view()->get_time().get_string().c_str());
2494
2495         if(queued==true)
2496         {
2497                 return;
2498                 //synfig::info("queue_render_preview(): already queued, unqueuing");
2499 /*              if(render_idle_func_id)
2500                         g_source_remove(render_idle_func_id);
2501                 render_idle_func_id=0;
2502                 queued=false;
2503 */
2504                 //async_renderer=0;
2505         }
2506
2507         if(dirty_trap_enabled)
2508         {
2509                 dirty_trap_queued++;
2510                 return;
2511         }
2512
2513         int queue_time=50;
2514
2515         if(rendering)
2516                 queue_time+=250;
2517
2518
2519         if(queued==false)
2520         {
2521                 //synfig::info("queue_render_preview(): (re)queuing...");
2522                 //render_idle_func_id=g_idle_add_full(G_PRIORITY_DEFAULT,__render_preview,this,NULL);
2523                 render_idle_func_id=g_timeout_add_full(G_PRIORITY_DEFAULT,queue_time,__render_preview,this,NULL);
2524                 queued=true;
2525         }
2526 /*      else if(rendering)
2527         {
2528                 refreshes+=5;
2529                 dirty=true;
2530                 queue_draw();
2531         }
2532 */
2533 }
2534
2535 DirtyTrap::DirtyTrap(WorkArea *work_area):work_area(work_area)
2536 {
2537         work_area->dirty_trap_enabled=true;
2538
2539         work_area->dirty_trap_queued=0;
2540 }
2541
2542 DirtyTrap::~DirtyTrap()
2543 {
2544         work_area->dirty_trap_enabled=false;
2545         if(work_area->dirty_trap_queued)
2546                 work_area->queue_render_preview();
2547 }
2548
2549 void
2550 studio::WorkArea::queue_draw_preview()
2551 {
2552         drawing_area->queue_draw();
2553 }
2554
2555 void
2556 studio::WorkArea::set_cursor(const Gdk::Cursor& x)
2557 {
2558         drawing_area->get_window()->set_cursor(x);
2559 }
2560 void
2561 studio::WorkArea::set_cursor(Gdk::CursorType x)
2562 {
2563         drawing_area->get_window()->set_cursor(Gdk::Cursor(x));
2564 }
2565
2566 #include "iconcontroler.h"
2567
2568 void
2569 studio::WorkArea::refresh_cursor()
2570 {
2571 //      set_cursor(IconControler::get_tool_cursor(canvas_view->get_smach().get_state_name(),drawing_area->get_window()));
2572 }
2573
2574 void
2575 studio::WorkArea::reset_cursor()
2576 {
2577         drawing_area->get_window()->set_cursor(Gdk::Cursor(Gdk::TOP_LEFT_ARROW));
2578 //      set_cursor(Gdk::TOP_LEFT_ARROW);
2579 }
2580
2581 void
2582 studio::WorkArea::set_zoom(float z)
2583 {
2584         z=max(1.0f/128.0f,min(128.0f,z));
2585         if(z==zoom)
2586                 return;
2587         zoom = z;
2588         refresh_dimension_info();
2589         /*if(async_renderer)
2590         {
2591                 async_renderer->stop();
2592                 async_renderer=0;
2593         }*/
2594         refreshes+=5;
2595         async_update_preview();
2596         //queue_render_preview();
2597 }
2598
2599 void
2600 WorkArea::set_selected_value_node(etl::loose_handle<synfig::ValueNode> x)
2601 {
2602         if(x!=selected_value_node_)
2603         {
2604                 selected_value_node_=x;
2605                 queue_draw();
2606         }
2607 }
2608
2609 void
2610 WorkArea::insert_renderer(const etl::handle<WorkAreaRenderer> &x)
2611 {
2612         renderer_set_.insert(x);
2613         x->set_work_area(this);
2614         queue_draw();
2615 }
2616
2617 void
2618 WorkArea::insert_renderer(const etl::handle<WorkAreaRenderer> &x, int priority)
2619 {
2620         x->set_priority(priority);
2621         insert_renderer(x);
2622 }
2623
2624 void
2625 WorkArea::erase_renderer(const etl::handle<WorkAreaRenderer> &x)
2626 {
2627         x->set_work_area(0);
2628         renderer_set_.erase(x);
2629         queue_draw();
2630 }
2631
2632 void
2633 WorkArea::resort_render_set()
2634 {
2635         std::set<etl::handle<WorkAreaRenderer> > tmp(
2636                 renderer_set_.begin(),
2637                 renderer_set_.end()
2638         );
2639         renderer_set_.swap(tmp);
2640         queue_draw();
2641 }