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