source: synfigstudio/trunk/fuentes/src/gui/workarea.cpp @ 481

Last change on this file since 481 was 481, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 95.3 KB
Line 
1/* === S Y N F I G ========================================================= */
2/*!     \file workarea.cpp
3**      \brief Template Header
4**
5**      $Id$
6**
7**      \legal
8**      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9**      Copyright (c) 2006 Yue Shi Lai
10**      Copyright (c) 2007, 2008 Chris Moore
11**  Copyright (c) 2011 Nikita Kitaev
12**
13**      This package is free software; you can redistribute it and/or
14**      modify it under the terms of the GNU General Public License as
15**      published by the Free Software Foundation; either version 2 of
16**      the License, or (at your option) any later version.
17**
18**      This package is distributed in the hope that it will be useful,
19**      but WITHOUT ANY WARRANTY; without even the implied warranty of
20**      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21**      General Public License for more details.
22**      \endlegal
23*/
24/* ========================================================================= */
25
26/* === H E A D E R S ======================================================= */
27
28#ifdef USING_PCH
29#       include "pch.h"
30#else
31#ifdef HAVE_CONFIG_H
32#       include <config.h>
33#endif
34
35#include <sigc++/sigc++.h>
36
37#include "workarea.h"
38#include "canvasview.h"
39#include "app.h"
40#include <gtkmm/window.h>
41#include <gtkmm/image.h>
42#include <gtkmm/drawingarea.h>
43#include <gtkmm/arrow.h>
44#include <gtkmm/image.h>
45#include <gtkmm/scrollbar.h>
46#include <cmath>
47#include <ETL/misc>
48
49#include <synfig/target_scanline.h>
50#include <synfig/target_tile.h>
51#include <synfig/target_cairo.h>
52#include <synfig/target_cairo_tile.h>
53#include <synfig/surface.h>
54#include <synfig/valuenodes/valuenode_composite.h>
55#include <synfigapp/canvasinterface.h>
56#include "event_mouse.h"
57#include "event_layerclick.h"
58#include "event_keyboard.h"
59#include "widgets/widget_color.h"
60#include <synfig/distance.h>
61#include <synfig/context.h>
62
63#include "workarearenderer/workarearenderer.h"
64#include "workarearenderer/renderer_background.h"
65#include "workarearenderer/renderer_canvas.h"
66#include "workarearenderer/renderer_grid.h"
67#include "workarearenderer/renderer_guides.h"
68#include "workarearenderer/renderer_timecode.h"
69#include "workarearenderer/renderer_bonesetup.h"
70#include "workarearenderer/renderer_ducks.h"
71#include "workarearenderer/renderer_dragbox.h"
72#include "workarearenderer/renderer_bbox.h"
73#include "asyncrenderer.h"
74#include <gtkmm/frame.h>
75
76#include <synfig/mutex.h>
77
78#include "general.h"
79
80#endif
81
82/* === U S I N G =========================================================== */
83
84using namespace std;
85using namespace etl;
86using namespace synfig;
87using namespace studio;
88
89/* === M A C R O S ========================================================= */
90
91#ifndef stratof
92#define stratof(X) (atof((X).c_str()))
93#define stratoi(X) (atoi((X).c_str()))
94#endif
95
96
97/* === G L O B A L S ======================================================= */
98
99/* === C L A S S E S ======================================================= */
100
101class studio::WorkAreaTarget_Cairo_Tile : public synfig::Target_Cairo_Tile
102{
103public:
104        WorkArea *workarea;
105        bool low_res;
106        int w,h;
107        int real_tile_w,real_tile_h;
108        int refresh_id;
109       
110        bool onionskin;
111        bool onion_first_tile;
112        int onion_layers;
113       
114        std::list<synfig::Time> onion_skin_queue;
115       
116        synfig::Mutex mutex;
117       
118        void set_onion_skin(bool x, int *onions)
119        {
120                onionskin=x;
121               
122                Time time(rend_desc().get_time_start());
123               
124                if(!onionskin)
125                        return;
126                onion_skin_queue.push_back(time);
127               
128                try
129                {
130                        Time thistime=time;
131                        for(int i=0; i<onions[0]; i++)
132                        {
133                                Time keytime=get_canvas()->keyframe_list().find_prev(thistime)->get_time();
134                                onion_skin_queue.push_back(keytime);
135                                thistime=keytime;
136                        }
137                }
138                catch(...)
139                {  }
140               
141                try
142                {
143                        Time thistime=time;
144                        for(int i=0; i<onions[1]; i++)
145                        {
146                                Time keytime=get_canvas()->keyframe_list().find_next(thistime)->get_time();
147                                onion_skin_queue.push_back(keytime);
148                                thistime=keytime;
149                        }
150                }
151                catch(...)
152                {  }
153               
154                onion_layers=onion_skin_queue.size();
155               
156                onion_first_tile=false;
157        }
158public:
159       
160        WorkAreaTarget_Cairo_Tile(WorkArea *workarea,int w, int h):
161                workarea(workarea),
162                low_res(workarea->get_low_resolution_flag()),
163                w(w),
164                h(h),
165                real_tile_w(workarea->tile_w),
166                real_tile_h(workarea->tile_h),
167                refresh_id(workarea->refreshes),
168                onionskin(false),
169                onion_first_tile(),
170                onion_layers(0)
171        {
172                set_clipping(true);
173                if(low_res)
174                {
175                        int div = workarea->get_low_res_pixel_size();
176                        set_tile_w(workarea->tile_w/div);
177                        set_tile_h(workarea->tile_h/div);
178                }
179                else
180                {
181                        set_tile_w(workarea->tile_w);
182                        set_tile_h(workarea->tile_h);
183                }
184                set_canvas(workarea->get_canvas());
185                set_quality(workarea->get_quality());
186        }
187       
188        ~WorkAreaTarget_Cairo_Tile()
189        {
190                workarea->queue_draw();
191        }
192       
193        virtual bool set_rend_desc(synfig::RendDesc *newdesc)
194        {
195                assert(workarea);
196                newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
197                if(low_res) {
198                        int div = workarea->get_low_res_pixel_size();
199                        newdesc->set_wh(w/div,h/div);
200                }
201                else
202                        newdesc->set_wh(w,h);
203               
204                if(
205                   workarea->get_w()!=w
206                   ||   workarea->get_h()!=h
207                   ) workarea->set_wh(w,h,4);
208                               
209                desc=*newdesc;
210                workarea->full_frame=false;
211                return true;
212        }
213       
214        virtual int total_tiles()const
215        {
216                int tw(rend_desc().get_w()/get_tile_w());
217                int th(rend_desc().get_h()/get_tile_h());
218                if(rend_desc().get_w()%get_tile_w()!=0)tw++;
219                if(rend_desc().get_h()%get_tile_h()!=0)th++;
220                return tw*th;
221        }
222       
223        virtual int next_frame(Time& time)
224        {
225                synfig::Mutex::Lock lock(mutex);
226               
227                if(!onionskin)
228                        return synfig::Target_Cairo_Tile::next_frame(time);
229               
230                onion_first_tile=(onion_layers==(signed)onion_skin_queue.size());
231               
232                if(!onion_skin_queue.empty())
233                {
234                        time=onion_skin_queue.front();
235                        onion_skin_queue.pop_front();
236                }
237                else
238                        return 0;
239               
240                return onion_skin_queue.size()+1;
241        }
242       
243        virtual int next_tile(int& x, int& y)
244        {
245                synfig::Mutex::Lock lock(mutex);
246
247                int curr_tile(workarea->next_unrendered_tile(refresh_id-onion_skin_queue.size()));
248                if(curr_tile<0)
249                        return 0;
250               
251                // Width of the image(in tiles)
252                int tw(rend_desc().get_w()/get_tile_w());
253                if(rend_desc().get_w()%get_tile_w()!=0)tw++;
254               
255                y=(curr_tile/tw)*get_tile_w();
256                x=(curr_tile%tw)*get_tile_h();
257               
258                // Mark this tile as "up-to-date"
259                if(onionskin)
260                        workarea->cairo_book[curr_tile].refreshes=refresh_id-onion_skin_queue.size();
261                else
262                        workarea->cairo_book[curr_tile].refreshes=refresh_id;
263               
264                return total_tiles()-curr_tile+1;
265        }
266       
267       
268        virtual bool start_frame(synfig::ProgressCallback */*cb*/)
269        {
270                synfig::Mutex::Lock lock(mutex);
271                workarea->cairo_book.resize(total_tiles());
272                return true;
273        }
274               
275        virtual bool add_tile(cairo_surface_t* tile_surface, int x, int y)
276        {
277                synfig::Mutex::Lock lock(mutex);
278                if(cairo_surface_status(tile_surface))
279                        return false;
280
281                gamma_filter(tile_surface, App::gamma);
282                x/=get_tile_w();
283                y/=get_tile_h();
284                int tw(rend_desc().get_w()/get_tile_w());
285                if(rend_desc().get_w()%get_tile_w()!=0)tw++;
286                unsigned int index=y*tw+x;
287               
288                // Sanity check
289                if(index>workarea->cairo_book.size())
290                        return false;
291               
292                if(!onionskin || onion_first_tile || !workarea->cairo_book[index].surface)
293                {
294                        if(workarea->cairo_book[index].surface)
295                                cairo_surface_destroy(workarea->cairo_book[index].surface);
296                        workarea->cairo_book[index].surface=cairo_surface_reference(tile_surface);
297                }
298                else
299                {
300                        cairo_t* cr=cairo_create(workarea->cairo_book[index].surface);
301                        cairo_set_source_surface(cr, tile_surface, 0, 0);
302                        cairo_paint_with_alpha(cr, 1.0/(onion_layers-onion_skin_queue.size()+1));
303                        cairo_destroy(cr);
304                }
305               
306                workarea->queue_draw();
307                assert(workarea->cairo_book[index].surface);
308
309                cairo_surface_destroy(tile_surface);
310                return true;
311        }
312        virtual void end_frame()
313        {
314        }
315};
316
317
318class studio::WorkAreaTarget : public synfig::Target_Tile
319{
320public:
321        WorkArea *workarea;
322        bool low_res;
323        int w,h;
324        int real_tile_w,real_tile_h;
325
326        int refresh_id;
327
328        bool onionskin;
329        bool onion_first_tile;
330        int onion_layers;
331
332        std::list<synfig::Time> onion_skin_queue;
333
334        synfig::Mutex mutex;
335
336        void set_onion_skin(bool x, int *onions)
337        {
338                onionskin=x;
339
340                Time time(rend_desc().get_time_start());
341
342                if(!onionskin)
343                        return;
344                onion_skin_queue.push_back(time);
345
346                try
347                {
348                Time thistime=time;
349                for(int i=0; i<onions[0]; i++)
350                        {
351                                Time keytime=get_canvas()->keyframe_list().find_prev(thistime)->get_time();
352                                onion_skin_queue.push_back(keytime);
353                                thistime=keytime;
354                        }
355                }
356                catch(...)
357                {  }
358
359                try
360                {
361                Time thistime=time;
362                for(int i=0; i<onions[1]; i++)
363                        {
364                                Time keytime=get_canvas()->keyframe_list().find_next(thistime)->get_time();
365                                onion_skin_queue.push_back(keytime);
366                                thistime=keytime;
367                        }
368                }
369                catch(...)
370                {  }
371
372                onion_layers=onion_skin_queue.size();
373
374                onion_first_tile=false;
375        }
376public:
377
378        WorkAreaTarget(WorkArea *workarea,int w, int h):
379                workarea(workarea),
380                low_res(workarea->get_low_resolution_flag()),
381                w(w),
382                h(h),
383                real_tile_w(workarea->tile_w),
384                real_tile_h(workarea->tile_h),
385                refresh_id(workarea->refreshes),
386                onionskin(false),
387                onion_first_tile(),
388                onion_layers(0)
389        {
390                //set_remove_alpha();
391                //set_avoid_time_sync();
392                set_clipping(true);
393                if(low_res)
394                {
395                        int div = workarea->get_low_res_pixel_size();
396                        set_tile_w(workarea->tile_w/div);
397                        set_tile_h(workarea->tile_h/div);
398                }
399                else
400                {
401                        set_tile_w(workarea->tile_w);
402                        set_tile_h(workarea->tile_h);
403                }
404                set_canvas(workarea->get_canvas());
405                set_quality(workarea->get_quality());
406        }
407
408        ~WorkAreaTarget()
409        {
410                workarea->queue_draw();
411        }
412
413        virtual bool set_rend_desc(synfig::RendDesc *newdesc)
414        {
415                assert(workarea);
416                newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
417                if(low_res) {
418                        int div = workarea->get_low_res_pixel_size();
419                        newdesc->set_wh(w/div,h/div);
420                }
421                else
422                        newdesc->set_wh(w,h);
423
424                if(
425                                workarea->get_w()!=w
426                        ||      workarea->get_h()!=h
427                ) workarea->set_wh(w,h,4);
428
429                workarea->full_frame=false;
430
431                desc=*newdesc;
432                return true;
433        }
434
435        virtual int total_tiles()const
436        {
437                int tw(rend_desc().get_w()/get_tile_w());
438                int th(rend_desc().get_h()/get_tile_h());
439                if(rend_desc().get_w()%get_tile_w()!=0)tw++;
440                if(rend_desc().get_h()%get_tile_h()!=0)th++;
441                return tw*th;
442        }
443
444        virtual int next_frame(Time& time)
445        {
446                synfig::Mutex::Lock lock(mutex);
447
448                if(!onionskin)
449                        return synfig::Target_Tile::next_frame(time);
450
451                onion_first_tile=(onion_layers==(signed)onion_skin_queue.size());
452
453                if(!onion_skin_queue.empty())
454                {
455                        time=onion_skin_queue.front();
456                        onion_skin_queue.pop_front();
457                }
458                else
459                        return 0;
460
461                return onion_skin_queue.size()+1;
462        }
463
464        virtual int next_tile(int& x, int& y)
465        {
466                synfig::Mutex::Lock lock(mutex);
467                //if(workarea->tile_queue.empty()) return 0;
468
469                //int curr_tile(workarea->tile_queue.front());
470                //workarea->tile_queue.pop_front();
471                int curr_tile(workarea->next_unrendered_tile(refresh_id-onion_skin_queue.size()));
472                if(curr_tile<0)
473                        return 0;
474
475                // Width of the image(in tiles)
476                int tw(rend_desc().get_w()/get_tile_w());
477                if(rend_desc().get_w()%get_tile_w()!=0)tw++;
478
479                y=(curr_tile/tw)*get_tile_w();
480                x=(curr_tile%tw)*get_tile_h();
481
482                // Mark this tile as "up-to-date"
483                if(onionskin)
484                        workarea->tile_book[curr_tile].second=refresh_id-onion_skin_queue.size();
485                else
486                        workarea->tile_book[curr_tile].second=refresh_id;
487
488                return total_tiles()-curr_tile+1;
489        }
490
491
492        virtual bool start_frame(synfig::ProgressCallback */*cb*/)
493        {
494                synfig::Mutex::Lock lock(mutex);
495                workarea->tile_book.resize(total_tiles());
496                return true;
497        }
498
499        static void free_buff(const guint8 *x) { free(const_cast<guint8*>(x)); }
500
501        virtual bool add_tile(const synfig::Surface &surface, int x, int y)
502        {
503                synfig::Mutex::Lock lock(mutex);
504                assert(surface);
505
506                PixelFormat pf(PF_RGB|PF_A);
507
508                const int total_bytes(get_tile_w()*get_tile_h()*synfig::channels(pf));
509
510                unsigned char *buffer((unsigned char*)malloc(total_bytes));
511
512                if(!surface || !buffer)
513                        return false;
514                {
515                        unsigned char *dest(buffer);
516                        const Color *src(surface[0]);
517                        int w(surface.get_w());
518                        int x(w*surface.get_h());
519                        for(int i=0;i<x;i++)
520                                dest=Color2PixelFormat(
521                                                                           (*(src++)).clamped(),
522                                                                           pf,
523                                                                           dest,
524                                                                           App::gamma
525                                                                           );
526                }
527
528                x/=get_tile_w();
529                y/=get_tile_h();
530                int tw(rend_desc().get_w()/get_tile_w());
531                if(rend_desc().get_w()%get_tile_w()!=0)tw++;
532                unsigned int index=y*tw+x;
533
534                // Sanity check
535                if(index>workarea->tile_book.size())
536                        return false;
537
538                Glib::RefPtr<Gdk::Pixbuf> pixbuf;
539
540                pixbuf=Gdk::Pixbuf::create_from_data(
541                        buffer, // pointer to the data
542                        Gdk::COLORSPACE_RGB, // the colorspace
543                        ((pf&PF_A)==PF_A), // has alpha?
544                        8, // bits per sample
545                        surface.get_w(),        // width
546                        surface.get_h(),        // height
547                        surface.get_w()*synfig::channels(pf), // stride (pitch)
548                        sigc::ptr_fun(&WorkAreaTarget::free_buff)
549                );
550
551                if(low_res)
552                {
553                        // We need to scale up
554                        int div = workarea->get_low_res_pixel_size();
555                        pixbuf=pixbuf->scale_simple(
556                                surface.get_w()*div,
557                                surface.get_h()*div,
558                                Gdk::INTERP_NEAREST
559                        );
560                }
561
562                if(!onionskin || onion_first_tile || !workarea->tile_book[index].first)
563                {
564                        workarea->tile_book[index].first=pixbuf;
565                }
566                else
567                {
568                        pixbuf->composite(
569                                workarea->tile_book[index].first, // Dest
570                                0,//int dest_x
571                                0,//int dest_y
572                                pixbuf->get_width(), // dest width
573                                pixbuf->get_height(), // dest_height,
574                                0, // double offset_x
575                                0, // double offset_y
576                                1, // double scale_x
577                                1, // double scale_y
578                                Gdk::INTERP_NEAREST, // interp
579                                255/(onion_layers-onion_skin_queue.size()+1) //int overall_alpha
580                        );
581                }
582
583                //if(index%2)
584                        workarea->queue_draw();
585                assert(workarea->tile_book[index].first);
586                return true;
587        }
588
589        virtual void end_frame()
590        {
591                //workarea->queue_draw();
592        }
593};
594
595
596class studio::WorkAreaTarget_Cairo: public synfig::Target_Cairo
597{
598public:
599        WorkArea *workarea;
600        bool low_res;
601        int w,h;
602        int refresh_id;
603        bool onionskin;
604        bool onion_first_tile;
605        int onion_layers;
606
607        std::list<synfig::Time> onion_skin_queue;
608
609        void set_onion_skin(bool status, int *onions)
610        {
611                onionskin=status;
612               
613                Time time(rend_desc().get_time_start());
614               
615                if(!onionskin)
616                        return;
617                onion_skin_queue.push_back(time);
618                try
619                {
620                        Time thistime=time;
621                        for(int i=0; i<onions[0]; i++)
622                        {
623                                Time keytime=get_canvas()->keyframe_list().find_prev(thistime)->get_time();
624                                onion_skin_queue.push_back(keytime);
625                                thistime=keytime;
626                        }
627                }
628                catch(...)
629                {  }
630               
631                try
632                {
633                        Time thistime=time;
634                        for(int i=0; i<onions[1]; i++)
635                        {
636                                Time keytime=get_canvas()->keyframe_list().find_next(thistime)->get_time();
637                                onion_skin_queue.push_back(keytime);
638                                thistime=keytime;
639                        }
640                }
641                catch(...)
642                {  }
643               
644                onion_layers=onion_skin_queue.size();
645               
646                onion_first_tile=false;
647        }
648public:
649       
650        WorkAreaTarget_Cairo(WorkArea *workarea,int w, int h):
651                workarea(workarea),
652                low_res(workarea->get_low_resolution_flag()),
653                w(w),
654                h(h),
655                refresh_id(workarea->refreshes),
656                onionskin(false),
657                onion_first_tile(),
658                onion_layers(0)
659        {
660                set_canvas(workarea->get_canvas());
661                set_quality(workarea->get_quality());
662        }
663
664        ~WorkAreaTarget_Cairo()
665        { }
666
667        virtual bool set_rend_desc(synfig::RendDesc *newdesc)
668        {
669                assert(workarea);
670                newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
671                if(low_res)
672                {
673                        int div = workarea->get_low_res_pixel_size();
674                        newdesc->set_wh(w/div,h/div);
675                }
676                else
677                        newdesc->set_wh(w,h);
678               
679                if(
680                   workarea->get_w()!=w
681                   ||   workarea->get_h()!=h
682                   ) workarea->set_wh(w,h,4);
683                               
684                desc=*newdesc;
685                workarea->full_frame=true;
686                workarea->cairo_book.resize(1);
687                return true;
688        }
689
690        virtual int next_frame(Time& time)
691        {
692                // Mark this tile as "up-to-date"
693                if(onionskin)
694                        workarea->cairo_book[0].refreshes=refresh_id-onion_skin_queue.size();
695                else
696                        workarea->cairo_book[0].refreshes=refresh_id;
697               
698                if(!onionskin)
699                        return synfig::Target_Cairo::next_frame(time);
700               
701                onion_first_tile=(onion_layers==(signed)onion_skin_queue.size());
702               
703                if(!onion_skin_queue.empty())
704                {
705                        time=onion_skin_queue.front();
706                        onion_skin_queue.pop_front();
707                }
708                else
709                        return 0;
710                return onion_skin_queue.size()+1;
711        }
712
713        virtual bool obtain_surface(cairo_surface_t*& surface)
714        {
715                int localw=desc.get_w(), localh=desc.get_h();
716                surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, localw, localh);
717                return true;
718        }
719
720        bool put_surface(cairo_surface_t *surf, synfig::ProgressCallback *cb)
721        {
722                if(!workarea)
723                        return false;
724                gamma_filter(surf, App::gamma);
725                if(cairo_surface_status(surf))
726                {
727                        if(cb) cb->error(_("Cairo Surface bad status"));
728                        return false;
729                }
730               
731                if(!onionskin || onion_first_tile || !workarea->cairo_book[0].surface)
732                {
733                        workarea->cairo_book[0].surface=cairo_surface_reference(surf);
734                }
735                else
736                {
737                        cairo_t* cr=cairo_create(workarea->cairo_book[0].surface);
738                        cairo_set_source_surface(cr, surf, 0, 0);
739                        cairo_paint_with_alpha(cr, 255/(onion_layers-onion_skin_queue.size()+1));
740                }
741               
742                workarea->queue_draw();
743                assert(workarea->cairo_book[0].surface);
744               
745                cairo_surface_destroy(surf);
746                return true;
747        }
748
749};
750
751class studio::WorkAreaTarget_Full : public synfig::Target_Scanline
752{
753public:
754        WorkArea *workarea;
755        bool low_res;
756        int w,h;
757        int real_tile_w,real_tile_h;
758
759        int refresh_id;
760
761        bool onionskin;
762        bool onion_first_tile;
763        int onion_layers;
764
765        Surface surface;
766
767        std::list<synfig::Time> onion_skin_queue;
768
769        void set_onion_skin(bool x, int *onions)
770        {
771                onionskin=x;
772
773                Time time(rend_desc().get_time_start());
774
775                if(!onionskin)
776                        return;
777                onion_skin_queue.push_back(time);
778                //onion_skin_queue.push_back(time-1);
779                //onion_skin_queue.push_back(time+1);
780                try
781                {
782                Time thistime=time;
783                for(int i=0; i<onions[0]; i++)
784                        {
785                                Time keytime=get_canvas()->keyframe_list().find_prev(thistime)->get_time();
786                                onion_skin_queue.push_back(keytime);
787                                thistime=keytime;
788                        }
789                }
790                catch(...)
791                {  }
792
793                try
794                {
795                Time thistime=time;
796                for(int i=0; i<onions[1]; i++)
797                        {
798                                Time keytime=get_canvas()->keyframe_list().find_next(thistime)->get_time();
799                                onion_skin_queue.push_back(keytime);
800                                thistime=keytime;
801                        }
802                }
803                catch(...)
804                {  }
805
806                onion_layers=onion_skin_queue.size();
807
808                onion_first_tile=false;
809        }
810public:
811
812        WorkAreaTarget_Full(WorkArea *workarea,int w, int h):
813                workarea(workarea),
814                low_res(workarea->get_low_resolution_flag()),
815                w(w),
816                h(h),
817                real_tile_w(),
818                real_tile_h(),
819                refresh_id(workarea->refreshes),
820                onionskin(false),
821                onion_first_tile(),
822                onion_layers(0)
823        {
824                set_canvas(workarea->get_canvas());
825                set_quality(workarea->get_quality());
826        }
827
828        ~WorkAreaTarget_Full()
829        { }
830
831        virtual bool set_rend_desc(synfig::RendDesc *newdesc)
832        {
833                assert(workarea);
834                newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
835                if(low_res)
836                {
837                        int div = workarea->get_low_res_pixel_size();
838                        newdesc->set_wh(w/div,h/div);
839                }
840                else
841                        newdesc->set_wh(w,h);
842
843                if(
844                                workarea->get_w()!=w
845                        ||      workarea->get_h()!=h
846                ) workarea->set_wh(w,h,4);
847
848                surface.set_wh(newdesc->get_w(),newdesc->get_h());
849
850                desc=*newdesc;
851                workarea->full_frame=true;
852                workarea->tile_book.resize(1);
853                return true;
854        }
855
856        virtual int next_frame(Time& time)
857        {
858                // Mark this tile as "up-to-date"
859                if(onionskin)
860                        workarea->tile_book[0].second=refresh_id-onion_skin_queue.size();
861                else
862                        workarea->tile_book[0].second=refresh_id;
863
864                if(!onionskin)
865                        return synfig::Target_Scanline::next_frame(time);
866
867                onion_first_tile=(onion_layers==(signed)onion_skin_queue.size());
868
869                if(!onion_skin_queue.empty())
870                {
871                        time=onion_skin_queue.front();
872                        onion_skin_queue.pop_front();
873                }
874                else
875                        return 0;
876                return onion_skin_queue.size()+1;
877        }
878
879
880        virtual bool start_frame(synfig::ProgressCallback */*cb*/)
881        {
882                return true;
883        }
884
885        virtual Color * start_scanline(int scanline)
886        {
887                return surface[scanline];
888        }
889
890        virtual bool end_scanline()
891        {
892                return true;
893        }
894
895        static void free_buff(const guint8 *x) { free(const_cast<guint8*>(x)); }
896
897        virtual void end_frame()
898        {
899                assert(surface);
900
901                PixelFormat pf(PF_RGB|PF_A);
902
903                const int total_bytes(surface.get_w()*surface.get_h()*synfig::channels(pf));
904
905                unsigned char *buffer((unsigned char*)malloc(total_bytes));
906
907                if(!surface || !buffer)
908                        return;
909                // Copy the content of surface to the buffer
910                {
911                        unsigned char *dest(buffer);
912                        const Color *src(surface[0]);
913                        int w(surface.get_w());
914                        int x(w*surface.get_h());
915                        for(int i=0;i<x;i++)
916                                dest=Color2PixelFormat(
917                                                                           (*(src++)).clamped(),
918                                                                           pf,
919                                                                           dest,
920                                                                           App::gamma
921                                                                           );
922                }
923
924                Glib::RefPtr<Gdk::Pixbuf> pixbuf;
925
926                pixbuf=Gdk::Pixbuf::create_from_data(
927                        buffer, // pointer to the data
928                        Gdk::COLORSPACE_RGB, // the colorspace
929                        ((pf&PF_A)==PF_A), // has alpha?
930                        8, // bits per sample
931                        surface.get_w(),        // width
932                        surface.get_h(),        // height
933                        surface.get_w()*synfig::channels(pf), // stride (pitch)
934                        sigc::ptr_fun(&WorkAreaTarget::free_buff)
935                );
936
937                if(low_res)
938                {
939                        // We need to scale up
940                        int div = workarea->get_low_res_pixel_size();
941                        pixbuf=pixbuf->scale_simple(
942                                surface.get_w()*div,
943                                surface.get_h()*div,
944                                Gdk::INTERP_NEAREST
945                        );
946                }
947
948                int index=0;
949
950                if(!onionskin || onion_first_tile || !workarea->tile_book[index].first)
951                {
952                        workarea->tile_book[index].first=pixbuf;
953                }
954                else
955                {
956                        pixbuf->composite(
957                                workarea->tile_book[index].first, // Dest
958                                0,//int dest_x
959                                0,//int dest_y
960                                pixbuf->get_width(), // dest width
961                                pixbuf->get_height(), // dest_height,
962                                0, // double offset_x
963                                0, // double offset_y
964                                1, // double scale_x
965                                1, // double scale_y
966                                Gdk::INTERP_NEAREST, // interp
967                                255/(onion_layers-onion_skin_queue.size()+1) //int overall_alpha
968                        );
969                }
970
971                workarea->queue_draw();
972                assert(workarea->tile_book[index].first);
973        }
974};
975
976
977
978
979/* === M E T H O D S ======================================================= */
980
981
982WorkArea::WorkArea(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface):
983        Gtk::Table(3, 3, false), /* 3 columns by 3 rows*/
984        Duckmatic(canvas_interface),
985        canvas_interface(canvas_interface),
986        canvas(canvas_interface->get_canvas()),
987        scrollx_adjustment(Gtk::Adjustment::create(0,-4,4,0.01,0.1)),
988        scrolly_adjustment(Gtk::Adjustment::create(0,-4,4,0.01,0.1)),
989        w(TILE_SIZE),
990        h(TILE_SIZE),
991        last_event_time(0),
992        progresscallback(0),
993        dragging(DRAG_NONE),
994        show_grid(false),
995        background_size(15,15),
996        background_first_color(0.88, 0.88, 0.88),  /* light gray */
997        background_second_color(0.65, 0.65, 0.65),  /* dark gray */
998        jack_offset(0),
999        tile_w(TILE_SIZE),
1000        tile_h(TILE_SIZE),
1001        timecode_width(0),
1002        timecode_height(0),
1003        bonesetup_width(0),
1004        bonesetup_height(0)
1005{
1006        show_guides=true;
1007        curr_input_device=0;
1008        full_frame=false;
1009        allow_duck_clicks=true;
1010        allow_bezier_clicks=true;
1011        allow_layer_clicks=true;
1012        render_idle_func_id=0;
1013        quality=10;
1014        low_res_pixel_size=2;
1015        rendering=false;
1016        canceled_=false;
1017        low_resolution=false;
1018        pw=0.001;
1019        ph=0.001;
1020        last_focus_point=Point(0,0);
1021        onion_skin=false;
1022        onion_skins[0]=1;
1023        onion_skins[1]=0;
1024        queued=false;
1025        dirty_trap_enabled=false;
1026        solid_lines=true;
1027
1028        dirty_trap_queued=0;
1029
1030        meta_data_lock=false;
1031
1032        insert_renderer(new Renderer_Background,000);
1033        insert_renderer(new Renderer_Canvas,    010);
1034        insert_renderer(new Renderer_Grid,              100);
1035        insert_renderer(new Renderer_Guides,    200);
1036        insert_renderer(new Renderer_Ducks,             300);
1037        insert_renderer(new Renderer_BBox,              399);
1038        insert_renderer(new Renderer_Dragbox,   400);
1039        insert_renderer(new Renderer_Timecode,  500);
1040        insert_renderer(new Renderer_BoneSetup, 501);
1041
1042        signal_duck_selection_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::queue_draw));
1043        signal_strokes_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::queue_draw));
1044        signal_grid_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::queue_draw));
1045        signal_grid_changed().connect(sigc::mem_fun(*this,&studio::WorkArea::save_meta_data));
1046        signal_sketch_saved().connect(sigc::mem_fun(*this,&studio::WorkArea::save_meta_data));
1047
1048        // Not that it really makes a difference... (setting this to zero, that is)
1049        refreshes=0;
1050
1051        drawing_area=manage(new class Gtk::DrawingArea());
1052        drawing_area->add_events(Gdk::SCROLL_MASK | Gdk::BUTTON3_MOTION_MASK);
1053        drawing_area->show();
1054
1055        drawing_frame=manage(new Gtk::Frame);
1056        drawing_frame->add(*drawing_area);
1057        //drawing_frame->set_shadow_type(Gtk::SHADOW_NONE);
1058        //drawing_frame->property_border_width()=5;
1059        //drawing_frame->modify_fg(Gtk::STATE_NORMAL,Gdk::Color("#00ffff"));
1060        //drawing_frame->modify_base(Gtk::STATE_NORMAL,Gdk::Color("#ff00ff"));
1061        /*drawing_frame->modify_fg(Gtk::STATE_ACTIVE,Gdk::Color("#00ffff"));
1062        drawing_frame->modify_base(Gtk::STATE_ACTIVE,Gdk::Color("#ff00ff"));
1063        drawing_frame->modify_bg(Gtk::STATE_ACTIVE,Gdk::Color("#00ff00"));
1064        drawing_frame->modify_fg(Gtk::STATE_INSENSITIVE,Gdk::Color("#00ffff"));
1065        drawing_frame->modify_base(Gtk::STATE_INSENSITIVE,Gdk::Color("#ff00ff"));
1066        drawing_frame->modify_bg(Gtk::STATE_INSENSITIVE,Gdk::Color("#00ff00"));
1067        drawing_frame->modify_fg(Gtk::STATE_SELECTED,Gdk::Color("#00ffff"));
1068        drawing_frame->modify_base(Gtk::STATE_SELECTED,Gdk::Color("#ff00ff"));
1069        drawing_frame->modify_bg(Gtk::STATE_SELECTED,Gdk::Color("#00ff00"));
1070        */
1071        //drawing_frame->set_state(Gtk::STATE_NORMAL);
1072
1073        drawing_frame->show();
1074
1075        attach(*drawing_frame, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
1076
1077        Gtk::IconSize iconsize=Gtk::IconSize::from_name("synfig-small_icon");
1078
1079        // Create the vertical and horizontal rulers
1080        vruler = manage(new Widget_Ruler(true));
1081        hruler = manage(new Widget_Ruler(false));
1082        vruler->show();
1083        hruler->show();
1084        attach(*vruler, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
1085        attach(*hruler, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
1086        hruler->signal_event().connect(sigc::mem_fun(*this, &WorkArea::on_hruler_event));
1087        vruler->signal_event().connect(sigc::mem_fun(*this, &WorkArea::on_vruler_event));
1088        hruler->add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON2_MOTION_MASK |Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
1089        vruler->add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON2_MOTION_MASK |Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
1090
1091        // Create the menu button
1092        menubutton=manage(new class Gtk::Button());
1093        //Gtk::Arrow *arrow1 = manage(new class Gtk::Arrow(Gtk::ARROW_RIGHT, Gtk::SHADOW_OUT));
1094        //arrow1->set_size_request(3,3);
1095        //menubutton->add(*arrow1);
1096        menubutton->show_all();
1097        menubutton->set_size_request(18, 18);
1098        menubutton->signal_pressed().connect(sigc::mem_fun(*this, &WorkArea::popup_menu));
1099        attach(*menubutton, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1100
1101        Gtk::HBox *hbox = manage(new class Gtk::HBox(false, 0));
1102
1103        Gtk::VScrollbar *vscrollbar1 = manage(new class Gtk::VScrollbar(get_scrolly_adjustment()));
1104        Gtk::HScrollbar *hscrollbar1 = manage(new class Gtk::HScrollbar(get_scrollx_adjustment()));
1105        vscrollbar1->show();
1106        attach(*vscrollbar1, 2, 3, 1, 2, Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
1107
1108        ZoomDial *zoomdial=manage(new class ZoomDial(iconsize));
1109        zoomdial->signal_zoom_in().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_in));
1110        zoomdial->signal_zoom_out().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_out));
1111        zoomdial->signal_zoom_fit().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_fit));
1112        zoomdial->signal_zoom_norm().connect(sigc::mem_fun(*this, &studio::WorkArea::zoom_norm));
1113
1114        hbox->pack_end(*hscrollbar1, Gtk::PACK_EXPAND_WIDGET,0);
1115        hscrollbar1->show();
1116        hbox->pack_start(*zoomdial, Gtk::PACK_SHRINK,0);
1117        zoomdial->show();
1118
1119        attach(*hbox, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
1120        hbox->show();
1121
1122        add_events(Gdk::KEY_PRESS_MASK);
1123        drawing_area->add_events(Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
1124        drawing_area->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
1125        drawing_area->add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON2_MOTION_MASK | Gdk::BUTTON3_MOTION_MASK | Gdk::POINTER_MOTION_MASK);
1126        drawing_area->add_events(Gdk::SCROLL_MASK);
1127
1128        // ----------------- Attach signals
1129
1130        drawing_area->signal_draw().connect(sigc::mem_fun(*this, &WorkArea::refresh));
1131        drawing_area->signal_event().connect(sigc::mem_fun(*this, &WorkArea::on_drawing_area_event));
1132        drawing_area->signal_size_allocate().connect(sigc::hide(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info)));
1133
1134
1135
1136        canvas_interface->signal_rend_desc_changed().connect(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info));
1137        // When either of the scrolling adjustments change, then redraw.
1138        get_scrollx_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::queue_scroll));
1139        get_scrolly_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::queue_scroll));
1140        get_scrollx_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info));
1141        get_scrolly_adjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &WorkArea::refresh_dimension_info));
1142
1143        get_canvas()->signal_meta_data_changed("grid_size").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1144        get_canvas()->signal_meta_data_changed("grid_color").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1145        get_canvas()->signal_meta_data_changed("grid_snap").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1146        get_canvas()->signal_meta_data_changed("grid_show").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1147        get_canvas()->signal_meta_data_changed("guide_show").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1148        get_canvas()->signal_meta_data_changed("guide_x").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1149        get_canvas()->signal_meta_data_changed("guide_y").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1150        get_canvas()->signal_meta_data_changed("onion_skin").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1151        get_canvas()->signal_meta_data_changed("onion_skin_past").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1152        get_canvas()->signal_meta_data_changed("onion_skin_future").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1153        get_canvas()->signal_meta_data_changed("guide_snap").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1154        get_canvas()->signal_meta_data_changed("guide_color").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1155        get_canvas()->signal_meta_data_changed("sketch").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1156        get_canvas()->signal_meta_data_changed("solid_lines").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1157        get_canvas()->signal_meta_data_changed("background_size").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1158        get_canvas()->signal_meta_data_changed("background_first_color").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1159        get_canvas()->signal_meta_data_changed("background_second_color").connect(sigc::mem_fun(*this,&WorkArea::load_meta_data));
1160
1161        queued=false;
1162        meta_data_lock=false;
1163        set_focus_point(Point(0,0));
1164
1165        // If no meta data in canvas, assume it's new file and save default
1166        if (!have_meta_data())
1167                save_meta_data();
1168
1169        load_meta_data();
1170        // Load sketch
1171        {
1172                String data(canvas->get_meta_data("sketch"));
1173                if(!data.empty())
1174                {
1175                        if(!load_sketch(data))
1176                                load_sketch(dirname(canvas->get_file_name())+ETL_DIRECTORY_SEPARATOR+basename(data));
1177                }
1178        }
1179
1180        drawing_area->set_can_focus(true);
1181}
1182
1183WorkArea::~WorkArea()
1184{
1185//      delete [] buffer;
1186
1187        // don't leave the render function queued if we are about to vanish;
1188        // that causes crashes
1189        if(render_idle_func_id)
1190                render_idle_func_id=0;
1191}
1192
1193#ifdef SINGLE_THREADED
1194bool
1195WorkArea::get_updating()const
1196{
1197        return App::single_threaded && async_renderer && async_renderer->updating;
1198}
1199#endif
1200
1201#ifdef SINGLE_THREADED
1202void
1203WorkArea::stop_updating(bool cancel)
1204{
1205        async_renderer->stop();
1206        if (cancel) canceled_=true;
1207}
1208#endif
1209
1210void
1211WorkArea::save_meta_data()
1212{
1213    ChangeLocale change_locale(LC_NUMERIC, "C");
1214
1215    if(meta_data_lock)
1216                return;
1217        meta_data_lock=true;
1218
1219        Vector s(get_grid_size());
1220        canvas_interface->set_meta_data("grid_size",strprintf("%f %f",s[0],s[1]));
1221        Color c(get_grid_color());
1222        canvas_interface->set_meta_data("grid_color",strprintf("%f %f %f",c.get_r(),c.get_g(),c.get_b()));
1223        c = get_guides_color();
1224        canvas_interface->set_meta_data("guide_color",strprintf("%f %f %f",c.get_r(),c.get_g(),c.get_b()));
1225        canvas_interface->set_meta_data("grid_snap",get_grid_snap()?"1":"0");
1226        canvas_interface->set_meta_data("guide_snap",get_guide_snap()?"1":"0");
1227        canvas_interface->set_meta_data("guide_show",get_show_guides()?"1":"0");
1228        canvas_interface->set_meta_data("grid_show",show_grid?"1":"0");
1229        canvas_interface->set_meta_data("jack_offset",strprintf("%f", (double)jack_offset));
1230        canvas_interface->set_meta_data("onion_skin",onion_skin?"1":"0");
1231        canvas_interface->set_meta_data("onion_skin_past", strprintf("%d", onion_skins[0]));
1232        canvas_interface->set_meta_data("onion_skin_future", strprintf("%d", onion_skins[1]));
1233
1234        s = get_background_size();
1235        canvas_interface->set_meta_data("background_size",strprintf("%f %f",s[0],s[1]));
1236        c = get_background_first_color();
1237        canvas_interface->set_meta_data("background_first_color",strprintf("%f %f %f",c.get_r(),c.get_g(),c.get_b()));
1238        c = get_background_second_color();
1239        canvas_interface->set_meta_data("background_second_color",strprintf("%f %f %f",c.get_r(),c.get_g(),c.get_b()));
1240
1241        {
1242                String data;
1243                GuideList::const_iterator iter;
1244                for(iter=get_guide_list_x().begin();iter!=get_guide_list_x().end();++iter)
1245                {
1246                        if(!data.empty())
1247                                data+=' ';
1248                        data+=strprintf("%f",*iter);
1249                }
1250                if(!data.empty())
1251                        canvas_interface->set_meta_data("guide_x",data);
1252                else if (!canvas->get_meta_data("guide_x").empty())
1253                        canvas_interface->erase_meta_data("guide_x");
1254
1255                data.clear();
1256                for(iter=get_guide_list_y().begin();iter!=get_guide_list_y().end();++iter)
1257                {
1258                        if(!data.empty())
1259                                data+=' ';
1260                        data+=strprintf("%f",*iter);
1261                }
1262                if(!data.empty())
1263                        canvas_interface->set_meta_data("guide_y",data);
1264                else if (!canvas->get_meta_data("guide_y").empty())
1265                        canvas_interface->erase_meta_data("guide_y");
1266        }
1267
1268        if(get_sketch_filename().size())
1269        {
1270                if(dirname(canvas->get_file_name())==dirname(get_sketch_filename()))
1271                        canvas_interface->set_meta_data("sketch",basename(get_sketch_filename()));
1272                else
1273                        canvas_interface->set_meta_data("sketch",get_sketch_filename());
1274        }
1275
1276        meta_data_lock=false;
1277}
1278
1279bool
1280WorkArea::have_meta_data()
1281{
1282        String data_size, data_show;
1283
1284        data_size=canvas->get_meta_data("grid_size");
1285        data_show=canvas->get_meta_data("grid_show");
1286
1287        if(data_size.empty() && !data_show.size())
1288                return false;
1289
1290        return true;
1291}
1292
1293void
1294WorkArea::load_meta_data()
1295{
1296        // we need to set locale careful, without calling functions and signals,
1297        // otherwise it can affect strings in GUI
1298    // ChangeLocale change_locale(LC_NUMERIC, "C");
1299
1300    if(meta_data_lock)
1301                return;
1302        meta_data_lock=true;
1303
1304        String data;
1305
1306        data=canvas->get_meta_data("grid_size");
1307        if(!data.empty())
1308        {
1309                float gx(get_grid_size()[0]),gy(get_grid_size()[1]);
1310
1311                String::iterator iter(find(data.begin(),data.end(),' '));
1312                String tmp(data.begin(),iter);
1313
1314                {
1315                    ChangeLocale change_locale(LC_NUMERIC, "C");
1316                        if(!tmp.empty())
1317                                gx=stratof(tmp);
1318                        else
1319                                synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"grid_size\", which was \"%s\"",data.c_str());
1320
1321                        if(iter==data.end())
1322                                tmp.clear();
1323                        else
1324                                tmp=String(iter+1,data.end());
1325
1326                        if(!tmp.empty())
1327                                gy=stratof(tmp);
1328                        else
1329                                synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"grid_size\", which was \"%s\"",data.c_str());
1330                }
1331
1332                set_grid_size(Vector(gx,gy));
1333        }
1334
1335        data=canvas->get_meta_data("grid_color");
1336        if(!data.empty())
1337        {
1338                float gr(get_grid_color().get_r()),gg(get_grid_color().get_g()),gb(get_grid_color().get_b());
1339
1340                String tmp;
1341                // Insert the string into a stream
1342                stringstream ss(data);
1343                // Create vector to hold our colors
1344                std::vector<String> tokens;
1345
1346                int imaxcolor = 0;
1347                while (ss >> tmp && imaxcolor++ < 3)
1348                        tokens.push_back(tmp);
1349
1350                if (tokens.size() != 3 || imaxcolor > 3)
1351                {
1352                        synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"grid_color\", which was \"%s\". \"red green blue\" in [0,1] was expected",data.c_str());
1353                        canvas_interface->get_ui_interface()->warning(_("Unable to set \"grid_color\""));
1354                }
1355                else
1356                {
1357                    ChangeLocale change_locale(LC_NUMERIC, "C");
1358                        gr=atof(tokens.at(0).data());
1359                        gg=atof(tokens.at(1).data());
1360                        gb=atof(tokens.at(2).data());
1361                }
1362
1363                set_grid_color(synfig::Color(gr,gg,gb));
1364        }
1365
1366        data=canvas->get_meta_data("guide_color");
1367        if(!data.empty())
1368        {
1369                float gr(get_guides_color().get_r()),gg(get_guides_color().get_g()),gb(get_guides_color().get_b());
1370
1371                String tmp;
1372                // Insert the string into a stream
1373                stringstream ss(data);
1374                // Create vector to hold our colors
1375                std::vector<String> tokens;
1376
1377                int imaxcolor = 0;
1378                while (ss >> tmp && imaxcolor++ < 3)
1379                        tokens.push_back(tmp);
1380
1381                if (tokens.size() != 3 || imaxcolor > 3)
1382                {
1383                        synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"guide_color\", which was \"%s\". \"red green blue\" in [0,1] was expected",data.c_str());
1384                        canvas_interface->get_ui_interface()->warning(_("Unable to set \"guide_color\""));
1385                }
1386                else
1387                {
1388                    ChangeLocale change_locale(LC_NUMERIC, "C");
1389                        gr=atof(tokens.at(0).data());
1390                        gg=atof(tokens.at(1).data());
1391                        gb=atof(tokens.at(2).data());
1392                }
1393
1394                set_guides_color(synfig::Color(gr,gg,gb));
1395        }
1396
1397        data=canvas->get_meta_data("grid_show");
1398        if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
1399                show_grid=true;
1400        if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
1401                show_grid=false;
1402
1403        data=canvas->get_meta_data("solid_lines");
1404        if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
1405                solid_lines=true;
1406        if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
1407                solid_lines=false;
1408
1409        data=canvas->get_meta_data("guide_show");
1410        if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
1411                show_guides=true;
1412        if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
1413                show_guides=false;
1414
1415        data=canvas->get_meta_data("grid_snap");
1416        if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
1417                set_grid_snap(true);
1418        if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
1419                set_grid_snap(false);
1420
1421        data=canvas->get_meta_data("guide_snap");
1422        if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
1423                set_guide_snap(true);
1424        if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
1425                set_guide_snap(false);
1426
1427        data=canvas->get_meta_data("onion_skin");
1428        if(data.size() && (data=="1" || data[0]=='t' || data[0]=='T'))
1429                set_onion_skin(true);
1430        if(data.size() && (data=="0" || data[0]=='f' || data[0]=='F'))
1431                set_onion_skin(false);
1432
1433        bool queue_render = false;
1434        data=canvas->get_meta_data("onion_skin_past");
1435        if(data.size())
1436        {
1437                int past_kf = stratoi(data);
1438                if (past_kf > ONION_SKIN_PAST) past_kf = ONION_SKIN_PAST;
1439                else if (past_kf < 0) past_kf =  0;
1440
1441                if (past_kf != onion_skins[0])
1442                {
1443                        onion_skins[0] = past_kf;
1444                        queue_render = true;
1445                }
1446        }
1447        data=canvas->get_meta_data("onion_skin_future");
1448        if(data.size())
1449        {
1450                int future_kf = stratoi(data);
1451                if (future_kf > ONION_SKIN_FUTURE) future_kf = ONION_SKIN_FUTURE;
1452                else if (future_kf < 0) future_kf =  0;
1453
1454                if (future_kf != onion_skins[1])
1455                {
1456                        onion_skins[1] = future_kf;
1457                        queue_render = true;
1458                }
1459        }
1460        // Update the canvas
1461        if(onion_skin && queue_render)  queue_render_preview();
1462
1463        data=canvas->get_meta_data("guide_x");
1464        get_guide_list_x().clear();
1465        while(!data.empty())
1466        {
1467                String::iterator iter(find(data.begin(),data.end(),' '));
1468                String guide(data.begin(),iter);
1469            ChangeLocale change_locale(LC_NUMERIC, "C");
1470
1471                if(!guide.empty())
1472                        get_guide_list_x().push_back(stratof(guide));
1473
1474                if(iter==data.end())
1475                        data.clear();
1476                else
1477                        data=String(iter+1,data.end());
1478        }
1479        //sort(get_guide_list_x());
1480
1481        data=canvas->get_meta_data("guide_y");
1482        get_guide_list_y().clear();
1483        while(!data.empty())
1484        {
1485                String::iterator iter(find(data.begin(),data.end(),' '));
1486                String guide(data.begin(),iter);
1487            ChangeLocale change_locale(LC_NUMERIC, "C");
1488
1489                if(!guide.empty())
1490                        get_guide_list_y().push_back(stratof(guide));
1491
1492                if(iter==data.end())
1493                        data.clear();
1494                else
1495                        data=String(iter+1,data.end());
1496        }
1497        //sort(get_guide_list_y());
1498
1499        data = canvas->get_meta_data("jack_offset");
1500        if (!data.empty())
1501                jack_offset = stratof(data);
1502
1503        data=canvas->get_meta_data("background_size");
1504        if(!data.empty())
1505        {
1506                float gx(get_background_size()[0]),gy(get_background_size()[1]);
1507
1508                String::iterator iter(find(data.begin(),data.end(),' '));
1509                String tmp(data.begin(),iter);
1510
1511                {
1512                    ChangeLocale change_locale(LC_NUMERIC, "C");
1513                        if(!tmp.empty())
1514                                gx=stratof(tmp);
1515                        else
1516                                synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"background_size\", which was \"%s\"",data.c_str());
1517
1518                        if(iter==data.end())
1519                                tmp.clear();
1520                        else
1521                                tmp=String(iter+1,data.end());
1522
1523                        if(!tmp.empty())
1524                                gy=stratof(tmp);
1525                        else
1526                                synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"background_size\", which was \"%s\"",data.c_str());
1527                }
1528
1529                set_background_size(Vector(gx,gy));
1530        }
1531
1532        data=canvas->get_meta_data("background_first_color");
1533        if(!data.empty())
1534        {
1535                float gr(get_background_first_color().get_r()),gg(get_background_first_color().get_g()),gb(get_background_first_color().get_b());
1536
1537                String tmp;
1538                // Insert the string into a stream
1539                stringstream ss(data);
1540                // Create vector to hold our colors
1541                std::vector<String> tokens;
1542
1543                int imaxcolor = 0;
1544                while (ss >> tmp && imaxcolor++ < 3)
1545                        tokens.push_back(tmp);
1546
1547                if (tokens.size() != 3 || imaxcolor > 3)
1548                {
1549                        synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"background_first_color\", which was \"%s\". \"red green blue\" in [0,1] was expected",data.c_str());
1550                        canvas_interface->get_ui_interface()->warning(_("Unable to set \"background_first_color\""));
1551                }
1552                else
1553                {
1554                    ChangeLocale change_locale(LC_NUMERIC, "C");
1555                        gr=atof(tokens.at(0).data());
1556                        gg=atof(tokens.at(1).data());
1557                        gb=atof(tokens.at(2).data());
1558                }
1559
1560                set_background_first_color(synfig::Color(gr,gg,gb));
1561        }
1562
1563        data=canvas->get_meta_data("background_second_color");
1564        if(!data.empty())
1565        {
1566                float gr(get_background_second_color().get_r()),gg(get_background_second_color().get_g()),gb(get_background_second_color().get_b());
1567
1568                String tmp;
1569                // Insert the string into a stream
1570                stringstream ss(data);
1571                // Create vector to hold our colors
1572                std::vector<String> tokens;
1573
1574                int imaxcolor = 0;
1575                while (ss >> tmp && imaxcolor++ < 3)
1576                        tokens.push_back(tmp);
1577
1578                if (tokens.size() != 3 || imaxcolor > 3)
1579                {
1580                        synfig::error("WorkArea::load_meta_data(): Unable to parse data for \"background_second_color\", which was \"%s\". \"red green blue\" in [0,1] was expected",data.c_str());
1581                        canvas_interface->get_ui_interface()->warning(_("Unable to set \"background_second_color\""));
1582                }
1583                else
1584                {
1585                    ChangeLocale change_locale(LC_NUMERIC, "C");
1586                        gr=atof(tokens.at(0).data());
1587                        gg=atof(tokens.at(1).data());
1588                        gb=atof(tokens.at(2).data());
1589                }
1590
1591                set_background_second_color(synfig::Color(gr,gg,gb));
1592        }
1593
1594        meta_data_lock=false;
1595        queue_draw();
1596        signal_meta_data_changed()();
1597}
1598
1599void
1600WorkArea::set_onion_skin(bool x)
1601{
1602        if(onion_skin==x)
1603                return;
1604        onion_skin=x;
1605        save_meta_data();
1606        queue_render_preview();
1607}
1608
1609bool
1610WorkArea::get_onion_skin()const
1611{
1612        return onion_skin;
1613}
1614
1615void WorkArea::set_onion_skins(int *onions)
1616{
1617        onion_skins[0]=onions[0];
1618        onion_skins[1]=onions[1];
1619        if(onion_skin)
1620                queue_render_preview();
1621        save_meta_data();
1622}
1623
1624int const *
1625WorkArea::get_onion_skins()const
1626{
1627        return onion_skins;
1628}
1629
1630void
1631WorkArea::enable_grid()
1632{
1633        show_grid=true;
1634        save_meta_data();
1635        queue_draw();
1636}
1637
1638void
1639WorkArea::disable_grid()
1640{
1641        show_grid=false;
1642        save_meta_data();
1643        queue_draw();
1644}
1645
1646
1647
1648void
1649WorkArea::toggle_grid()
1650{
1651        show_grid=!show_grid;
1652        save_meta_data();
1653        queue_draw();
1654}
1655
1656void
1657WorkArea::toggle_grid_snap()
1658{
1659        Duckmatic::toggle_grid_snap();
1660        save_meta_data();
1661        queue_draw();
1662}
1663
1664void
1665WorkArea::set_show_guides(bool x)
1666{
1667        show_guides=x;
1668        save_meta_data();
1669        queue_draw();
1670}
1671
1672void
1673WorkArea::toggle_guide_snap()
1674{
1675        Duckmatic::toggle_guide_snap();
1676        save_meta_data();
1677        queue_draw();
1678}
1679
1680void
1681WorkArea::set_guides_color(const synfig::Color &c)
1682{
1683        Duckmatic::set_guides_color(c);
1684        save_meta_data();
1685        queue_draw();
1686}
1687
1688void
1689WorkArea::set_jack_offset(const synfig::Time &x) {
1690        if (jack_offset == x) return;
1691        jack_offset = x;
1692        save_meta_data();
1693}
1694
1695void
1696WorkArea::set_low_resolution_flag(bool x)
1697{
1698        if(x!=low_resolution)
1699        {
1700                low_resolution=x;
1701                queue_render_preview();
1702        }
1703}
1704
1705void
1706WorkArea::toggle_low_resolution_flag()
1707{
1708        set_low_resolution_flag(!get_low_resolution_flag());
1709}
1710
1711void
1712WorkArea::popup_menu()
1713{
1714        signal_popup_menu()();
1715}
1716
1717void
1718WorkArea::set_grid_size(const synfig::Vector &s)
1719{
1720        Duckmatic::set_grid_size(s);
1721        save_meta_data();
1722        queue_draw();
1723}
1724
1725void
1726WorkArea::set_grid_color(const synfig::Color &c)
1727{
1728        Duckmatic::set_grid_color(c);
1729        save_meta_data();
1730        queue_draw();
1731}
1732
1733void
1734WorkArea::set_background_size(const synfig::Vector &s)
1735{
1736        if (background_size != s)
1737        {
1738           background_size = s;
1739       save_meta_data();
1740        }
1741        queue_draw();
1742}
1743
1744void
1745WorkArea::set_background_first_color(const synfig::Color &c)
1746{
1747        if(background_first_color != c)
1748        {
1749                background_first_color = c;
1750                save_meta_data();
1751        }
1752        queue_draw();
1753}
1754
1755void
1756WorkArea::set_background_second_color(const synfig::Color &c)
1757{
1758        if(background_second_color != c)
1759        {
1760                background_second_color = c;
1761                save_meta_data();
1762        }
1763        queue_draw();
1764}
1765
1766void
1767WorkArea::set_focus_point(const synfig::Point &point)
1768{
1769        // These next three lines try to ensure that we place the
1770        // focus on a pixel boundary
1771        /*Point adjusted(point[0]/abs(get_pw()),point[1]/abs(get_ph()));
1772        adjusted[0]=(abs(adjusted[0]-floor(adjusted[0]))<0.5)?floor(adjusted[0])*abs(get_pw()):ceil(adjusted[0])*abs(get_ph());
1773        adjusted[1]=(abs(adjusted[1]-floor(adjusted[1]))<0.5)?floor(adjusted[1])*abs(get_ph()):ceil(adjusted[1])*abs(get_ph());
1774        */
1775        const synfig::Point& adjusted(point);
1776
1777        synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1778        Real x_factor=(rend_desc.get_br()[0]-rend_desc.get_tl()[0]>0)?-1:1;
1779        Real y_factor=(rend_desc.get_br()[1]-rend_desc.get_tl()[1]>0)?-1:1;
1780
1781        get_scrollx_adjustment()->set_value(adjusted[0]*x_factor);
1782        get_scrolly_adjustment()->set_value(adjusted[1]*y_factor);
1783}
1784
1785synfig::Point
1786WorkArea::get_focus_point()const
1787{
1788        synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
1789        Real x_factor=(rend_desc.get_br()[0]-rend_desc.get_tl()[0]>0)?-1:1;
1790        Real y_factor=(rend_desc.get_br()[1]-rend_desc.get_tl()[1]>0)?-1:1;
1791
1792        return synfig::Point(get_scrollx_adjustment()->get_value()*x_factor, get_scrolly_adjustment()->get_value()*y_factor);
1793}
1794
1795bool
1796WorkArea::set_wh(int W, int H,int CHAN)
1797{
1798        // If our size is already set, don't set it again
1799        if(W==w && H==h && CHAN==bpp)
1800        {
1801                return true;
1802        }
1803        if(W<=0 || H<=0 || CHAN<=0)
1804                return false;
1805
1806        assert(W>0);
1807        assert(H>0);
1808        assert(CHAN>0);
1809
1810        // Set all of the parameters
1811        w=W;
1812        h=H;
1813        bpp=CHAN;
1814
1815        refresh_dimension_info();
1816
1817        tile_book.clear();
1818        cairo_book.clear();
1819
1820        return true;
1821}
1822
1823bool
1824WorkArea::on_key_press_event(GdkEventKey* event)
1825{
1826        if (Smach::RESULT_OK == canvas_view->get_smach().process_event(
1827                EventKeyboard(EVENT_WORKAREA_KEY_DOWN, event->keyval, Gdk::ModifierType(event->state))))
1828                        return true;
1829
1830        if(get_selected_ducks().empty())
1831                return false;
1832
1833        Real multiplier(1.0);
1834
1835        if(Gdk::ModifierType(event->state)&GDK_SHIFT_MASK)
1836                multiplier=10.0;
1837
1838        Vector nudge;
1839        switch(event->keyval)
1840        {
1841                case GDK_KEY_Left:
1842                        nudge=Vector(-pw,0);
1843                        break;
1844                case GDK_KEY_Right:
1845                        nudge=Vector(pw,0);
1846                        break;
1847                case GDK_KEY_Up:
1848                        nudge=Vector(0,-ph);
1849                        break;
1850                case GDK_KEY_Down:
1851                        nudge=Vector(0,ph);
1852                        break;
1853                default:
1854                        return false;
1855                        break;
1856        }
1857
1858        synfigapp::Action::PassiveGrouper grouper(instance.get(),_("Nudge"));
1859
1860        // Grid snap does not apply to nudging
1861        bool grid_snap_holder(get_grid_snap());
1862        bool guide_snap_holder(get_guide_snap());
1863        set_grid_snap(false);
1864
1865        try {
1866                start_duck_drag(get_selected_duck()->get_trans_point());
1867                translate_selected_ducks(get_selected_duck()->get_trans_point()+nudge*multiplier);
1868                end_duck_drag();
1869        }
1870        catch(String)
1871        {
1872                canvas_view->duck_refresh_flag=true;
1873                canvas_view->queue_rebuild_ducks();
1874        }
1875
1876        set_grid_snap(grid_snap_holder);
1877        set_guide_snap(guide_snap_holder);
1878
1879        return true;
1880}
1881
1882bool
1883WorkArea::on_key_release_event(GdkEventKey* event)
1884{
1885        return Smach::RESULT_OK == canvas_view->get_smach().process_event(
1886                EventKeyboard(EVENT_WORKAREA_KEY_UP, event->keyval, Gdk::ModifierType(event->state)) );
1887}
1888
1889bool
1890WorkArea::on_drawing_area_event(GdkEvent *event)
1891{
1892        synfig::Point mouse_pos;
1893    float bezier_click_pos;
1894        const float radius((abs(pw)+abs(ph))*4);
1895        int button_pressed(0);
1896        float pressure(0);
1897        Gdk::ModifierType modifier(Gdk::ModifierType(0));
1898
1899        // Handle input stuff
1900        if (event->any.type==GDK_MOTION_NOTIFY)
1901        {
1902                GdkDevice *device = event->motion.device;
1903                modifier = Gdk::ModifierType(event->motion.state);
1904
1905                // Make sure we recognize the device
1906                if(curr_input_device)
1907                {
1908                        if(curr_input_device!=device)
1909                        {
1910                                assert(device);
1911                                curr_input_device=device;
1912                                signal_input_device_changed()(curr_input_device);
1913                        }
1914                }
1915                else
1916                if(device)
1917                {
1918                        curr_input_device=device;
1919                        signal_input_device_changed()(curr_input_device);
1920                }
1921
1922                assert(curr_input_device);
1923
1924                // Calculate the position of the
1925                // input device in canvas coordinates
1926
1927                double x = 0.0, y = 0.0, p = 0.0;
1928                int ox = 0, oy = 0;
1929                Gtk::Container *toplevel = drawing_frame->get_toplevel();
1930                if (toplevel) drawing_frame->translate_coordinates(*toplevel, 0, 0, ox, oy);
1931
1932                if (gdk_device_get_axis(device, event->motion.axes, GDK_AXIS_X, &x))
1933                        x -= ox; else x = event->motion.x;
1934                if (gdk_device_get_axis(device, event->motion.axes, GDK_AXIS_Y, &y))
1935                        y -= oy; else y = event->motion.y;
1936                if (gdk_device_get_axis(device, event->motion.axes, GDK_AXIS_PRESSURE, &p))
1937                        p = std::max(0.0, (p - 0.04)/(1.0 - 0.04)); else p = 1.0;
1938
1939                if(isnan(x) || isnan(y) || isnan(p))
1940                        return false;
1941
1942                mouse_pos=synfig::Point(screen_to_comp_coords(synfig::Point(x, y)));
1943                pressure = (float)p;
1944        }
1945        else
1946        if(     event->any.type==GDK_BUTTON_PRESS  ||
1947                event->any.type==GDK_2BUTTON_PRESS ||
1948                event->any.type==GDK_3BUTTON_PRESS ||
1949                event->any.type==GDK_BUTTON_RELEASE )
1950        {
1951                GdkDevice *device = event->button.device;
1952                modifier = Gdk::ModifierType(event->button.state);
1953                drawing_area->grab_focus();
1954
1955                // Make sure we recognize the device
1956                if(curr_input_device)
1957                {
1958                        if(curr_input_device!=device)
1959                        {
1960                                assert(device);
1961                                curr_input_device=device;
1962                                signal_input_device_changed()(curr_input_device);
1963                        }
1964                }
1965                else
1966                if(device)
1967                {
1968                        curr_input_device=device;
1969                        signal_input_device_changed()(curr_input_device);
1970                }
1971
1972                assert(curr_input_device);
1973
1974                // Calculate the position of the
1975                // input device in canvas coordinates
1976                // and the buttons
1977
1978                double x = 0.0, y = 0.0, p = 0.0;
1979                int ox = 0, oy = 0;
1980                Gtk::Container *toplevel = drawing_frame->get_toplevel();
1981                drawing_frame->translate_coordinates(*toplevel, 0, 0, ox, oy);
1982
1983                if (gdk_device_get_axis(device, event->motion.axes, GDK_AXIS_X, &x))
1984                        x -= ox; else x = event->motion.x;
1985                if (gdk_device_get_axis(device, event->motion.axes, GDK_AXIS_Y, &y))
1986                        y -= oy; else y = event->motion.y;
1987                if (gdk_device_get_axis(device, event->motion.axes, GDK_AXIS_PRESSURE, &p))
1988                        p = std::max(0.0, (p - 0.04)/(1.0 - 0.04)); else p = 1.0;
1989
1990                if(isnan(x) || isnan(y) || isnan(p))
1991                        return false;
1992
1993                mouse_pos=synfig::Point(screen_to_comp_coords(synfig::Point(x, y)));
1994                pressure = (float)p;
1995                button_pressed=event->button.button;
1996                if(button_pressed==1 && pressure<=0.f && (event->any.type!=GDK_BUTTON_RELEASE && event->any.type!=GDK_BUTTON_PRESS))
1997                        button_pressed=0;
1998        }
1999        else
2000        // GDK mouse scrolling events
2001        if(event->any.type==GDK_SCROLL)
2002        {
2003                // GDK information needed to properly interpret mouse
2004                // scrolling events are: scroll.state, scroll.x/scroll.y, and
2005                // scroll.direction. The value of scroll.direction will be
2006                // obtained later.
2007
2008                modifier=Gdk::ModifierType(event->scroll.state);
2009                mouse_pos=synfig::Point(screen_to_comp_coords(synfig::Point(event->scroll.x,event->scroll.y)));
2010        }
2011
2012        // Handle the renderables
2013        {
2014                std::set<etl::handle<WorkAreaRenderer> >::iterator iter;
2015                for(iter=renderer_set_.begin();iter!=renderer_set_.end();++iter)
2016                {
2017                        if((*iter)->get_enabled())
2018                                if((*iter)->event_vfunc(event))
2019                                {
2020                                        // Event handled. Return true.
2021                                        return true;
2022                                }
2023                }
2024        }
2025
2026        // Event hasn't been handled, pass it down
2027        switch(event->type)
2028    {
2029        case GDK_BUTTON_PRESS:
2030                {
2031                switch(button_pressed)
2032                {
2033                case 1: // Attempt to click on a duck
2034                {
2035                        etl::handle<Duck> duck;
2036                        dragging=DRAG_NONE;
2037
2038                        if(allow_duck_clicks)
2039                        {
2040                                duck=find_duck(mouse_pos,radius);
2041
2042                                //!TODO Remove HARDCODE Ui Specification, make it config ready
2043
2044                                // Single click duck selection on WorkArea [Part I] (Part II lower in code)
2045                                if(duck)
2046                                {
2047                                        // make a note of whether the duck we click on was selected or not
2048                                        if(duck_is_selected(duck))
2049                                                clicked_duck=duck;
2050                                        else
2051                                        {
2052                                                clicked_duck=0;
2053                                                // if CTRL or SHIFT isn't pressed, clicking an unselected duck will unselect all other ducks
2054                                                if(!(modifier&(GDK_CONTROL_MASK|GDK_SHIFT_MASK)))
2055                                                        clear_selected_ducks();
2056                                                select_duck(duck);
2057                                        }
2058                                }
2059                        }
2060                        //else
2061                        //      clear_selected_ducks();
2062
2063                        if(allow_bezier_clicks)
2064                        {
2065                                selected_bezier=find_bezier(mouse_pos,radius,&bezier_click_pos);
2066                        }
2067                        else
2068                        {
2069                                selected_bezier=0;
2070                        }
2071
2072                        if(duck)
2073                        {
2074                                if (!duck->get_editable(get_alternative_mode()))
2075                                        return true;
2076
2077                                //get_selected_duck()->signal_user_click(0)();
2078                                //if(clicked_duck)clicked_duck->signal_user_click(0)();
2079
2080                                // if the user is holding shift while clicking on a tangent duck, consider splitting the tangent
2081                                if ((event->button.state&GDK_SHIFT_MASK) && duck->get_type() == Duck::TYPE_TANGENT)
2082                                {
2083                                        synfigapp::ValueDesc value_desc = duck->get_value_desc();
2084
2085                                        // we have the tangent, but need the vertex - that's the parent
2086                                        if (value_desc.is_value_node()) {
2087                                                ValueNode_Composite::Handle value_node = value_desc.get_value_node();
2088                                                BLinePoint bp((*value_node)(get_time()).get(BLinePoint()));
2089                                                // if the tangent isn't split, then split it
2090                                                if (!bp.get_split_tangent_both())
2091                                                {
2092                                                        if (get_canvas_view()->canvas_interface()->change_value(synfigapp::ValueDesc(
2093                                                                        value_node,
2094                                                                        value_node->get_link_index_from_name("split_radius")),
2095                                                                        true)
2096                                                         && get_canvas_view()->canvas_interface()->change_value(synfigapp::ValueDesc(
2097                                                                        value_node,
2098                                                                        value_node->get_link_index_from_name("split_angle")),
2099                                                                        true )
2100                                                        )
2101                                                        {
2102                                                                // rebuild the ducks from scratch, so the tangents ducks aren't connected
2103                                                                get_canvas_view()->rebuild_ducks();
2104
2105                                                                // reprocess the mouse click
2106                                                                return on_drawing_area_event(event);
2107                                                        }
2108                                                        else
2109                                                                return true;
2110                                                }
2111                                        } else {
2112                                                // I don't know how to access the vertex from the tangent duck when originally drawing the bline in the bline tool
2113
2114                                                // synfig::ValueNode::Handle vn = value_desc.get_value_node();
2115                                                synfig::info("parent isn't value node?  shift-drag-tangent doesn't work in bline tool yet...");
2116                                        }
2117                                }
2118
2119                                dragging=DRAG_DUCK;
2120                                drag_point=mouse_pos;
2121                                //drawing_area->queue_draw();
2122                                start_duck_drag(mouse_pos);
2123                                get_canvas_view()->reset_cancel_status();
2124                                return true;
2125                        }
2126                        else
2127                        if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_LEFT,mouse_pos,pressure,modifier))==Smach::RESULT_OK)
2128                        {
2129                                if (selected_bezier)
2130                                {
2131                                        synfig::Point distance_1 = selected_bezier->p1->get_trans_point() - mouse_pos;
2132                                        synfig::Point distance_2 = selected_bezier->p2->get_trans_point() - mouse_pos;
2133                                        if( distance_1.mag() > radius*2
2134                                            && distance_2.mag() > radius*2
2135                                                )
2136                                        // If we click a selected bezier
2137                                        // not too close to the endpoints
2138                                        {
2139                                                // We give the states first priority to process the
2140                                                // event so as not to interfere with the bline tool
2141                                                dragging=DRAG_BEZIER;
2142                                                drag_point=mouse_pos;
2143                                                start_bezier_drag(mouse_pos, bezier_click_pos);
2144                                                return true;
2145                                        }
2146                                }
2147// I commented out this section because
2148// it was causing issues when rotoscoping.
2149// At the moment, we don't need it, so
2150// this was the easiest way to fix the problem.
2151/*
2152                                else
2153                                if(selected_bezier)
2154                                {
2155                                        selected_duck=0;
2156                                        selected_bezier->signal_user_click(0)(bezier_click_pos);
2157                                }
2158*/
2159
2160                                // Check for a guide click
2161                                if (show_guides)
2162                                {
2163                                        GuideList::iterator iter;
2164
2165                                        iter=find_guide_x(mouse_pos,radius);
2166                                        if(iter==get_guide_list_x().end())
2167                                        {
2168                                                curr_guide_is_x=false;
2169                                                iter=find_guide_y(mouse_pos,radius);
2170                                        }
2171                                        else
2172                                                curr_guide_is_x=true;
2173                                        if(iter!=get_guide_list_x().end() && iter!=get_guide_list_y().end())
2174                                        {
2175                                                dragging=DRAG_GUIDE;
2176                                                curr_guide=iter;
2177                                                return true;
2178                                        }
2179                                }
2180                                // All else fails, try making a selection box
2181                                dragging=DRAG_BOX;
2182                                curr_point=drag_point=mouse_pos;
2183                                return true;
2184                        }
2185                        selected_bezier=0;
2186                        break;
2187                }
2188                case 2: // Attempt to drag and move the window
2189                {
2190                        etl::handle<Duck> duck=find_duck(mouse_pos,radius);
2191                        etl::handle<Bezier> bezier=find_bezier(mouse_pos,radius,&bezier_click_pos);
2192                        if(duck)
2193                                duck->signal_user_click(1)();
2194                        else
2195                        if(bezier)
2196                                bezier->signal_user_click(1)(bezier_click_pos);
2197
2198                        if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_MIDDLE,mouse_pos,pressure,modifier))==Smach::RESULT_OK)
2199
2200                        dragging=DRAG_WINDOW;
2201                        drag_point=mouse_pos;
2202                        signal_user_click(1)(mouse_pos);
2203
2204                        break;
2205                }
2206                case 3: // Attempt to either get info on a duck, or open the menu
2207                {
2208                        etl::handle<Duck> duck=find_duck(mouse_pos,radius);
2209                        etl::handle<Bezier> bezier=find_bezier(mouse_pos,radius,&bezier_click_pos);
2210
2211                        Layer::Handle layer(get_canvas()->find_layer(get_canvas_view()->get_context_params(),mouse_pos));
2212                        if(duck)
2213                        {
2214                                if(get_selected_ducks().size()<=1)
2215                                        duck->signal_user_click(2)();
2216                                else
2217                                        canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MULTIPLE_DUCKS_CLICKED,BUTTON_RIGHT,mouse_pos,pressure,modifier,duck));
2218                                return true;
2219                        }
2220                        else if(bezier)
2221                        {
2222                                bezier->signal_user_click(2)(bezier_click_pos);
2223                                return true;
2224                        }
2225                        else if (layer)
2226                        {
2227                                if(canvas_view->get_smach().process_event(EventLayerClick(layer,BUTTON_RIGHT,mouse_pos))==Smach::RESULT_OK)
2228                                        return false;
2229                                return true;
2230                        }
2231                        else
2232                                canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_RIGHT,mouse_pos,pressure,modifier));
2233                        /*
2234                        if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_RIGHT,mouse_pos,pressure,modifier))==Smach::RESULT_OK)
2235                        {
2236                                //popup_menu();
2237                                return true;
2238                        }
2239                        */
2240                        break;
2241                }
2242                case 4:
2243                        signal_user_click(3)(mouse_pos);
2244                        break;
2245                case 5:
2246                        signal_user_click(4)(mouse_pos);
2247                        break;
2248                default:
2249                        break;
2250                }
2251                }
2252                break;
2253        case GDK_MOTION_NOTIFY:
2254                curr_point=mouse_pos;
2255
2256                if(event->motion.time-last_event_time<25)
2257                        return true;
2258                else
2259                        last_event_time=event->motion.time;
2260
2261                signal_cursor_moved_();
2262
2263                // Guide/Duck highlights on hover
2264                switch(dragging)
2265                {
2266                case DRAG_NONE:
2267                   {
2268            GuideList::iterator iter;
2269
2270            iter=find_guide_x(mouse_pos,radius);
2271            if(iter==get_guide_list_x().end())
2272                iter=find_guide_y(mouse_pos,radius);
2273
2274            if(iter!=curr_guide)
2275            {
2276                curr_guide=iter;
2277                drawing_area->queue_draw();
2278            }
2279
2280            etl::handle<Duck> duck;
2281            duck=find_duck(mouse_pos,radius);
2282            if(duck!=hover_duck)
2283            {
2284                hover_duck=duck;
2285                drawing_area->queue_draw();
2286            }
2287                   }
2288                break;
2289
2290                case DRAG_DUCK :
2291                {
2292                        if(canvas_view->get_cancel_status())
2293                        {
2294                                dragging=DRAG_NONE;
2295                                canvas_view->queue_rebuild_ducks();
2296                                return true;
2297                        }
2298                        /*
2299                        Point point((mouse_pos-selected_duck->get_origin())/selected_duck->get_scalar());
2300                        if(get_grid_snap())
2301                        {
2302                                point[0]=floor(point[0]/grid_size[0]+0.5)*grid_size[0];
2303                                point[1]=floor(point[1]/grid_size[1]+0.5)*grid_size[1];
2304                        }
2305                        selected_duck->set_point(point);
2306                        */
2307
2308                        //Point p(mouse_pos);
2309
2310                        set_axis_lock(event->motion.state&GDK_SHIFT_MASK);
2311
2312                        translate_selected_ducks(mouse_pos);
2313
2314                        drawing_area->queue_draw();
2315                }
2316                break;
2317
2318                case DRAG_BEZIER :
2319                {
2320                        if(canvas_view->get_cancel_status())
2321                        {
2322                                dragging=DRAG_NONE;
2323                                canvas_view->queue_rebuild_ducks();
2324                                return true;
2325                        }
2326
2327                        translate_selected_bezier(mouse_pos);
2328
2329                        drawing_area->queue_draw();
2330                }
2331        break;
2332
2333                case DRAG_BOX:
2334                {
2335                        curr_point=mouse_pos;
2336                        drawing_area->queue_draw();
2337                }
2338        break;
2339
2340                case DRAG_GUIDE :
2341                {
2342                        if(curr_guide_is_x)
2343                                *curr_guide=mouse_pos[0];
2344                        else
2345                                *curr_guide=mouse_pos[1];
2346                        drawing_area->queue_draw();
2347                }
2348        break;
2349                default:
2350                {
2351
2352                }
2353                }//end switch dragging
2354
2355                if(dragging!=DRAG_WINDOW)
2356                {       // Update those triangle things on the rulers
2357                        const synfig::Point point(mouse_pos);
2358                        hruler->set_position( Distance(point[0],Distance::SYSTEM_UNITS).get(App::distance_system,get_canvas()->rend_desc()) );
2359                        vruler->set_position( Distance(point[1],Distance::SYSTEM_UNITS).get(App::distance_system,get_canvas()->rend_desc()) );
2360                }
2361
2362                if(dragging == DRAG_WINDOW)
2363                        set_focus_point(get_focus_point() + mouse_pos-drag_point);
2364                else if ((event->motion.state & GDK_BUTTON1_MASK) &&
2365                                canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DRAG, BUTTON_LEFT,
2366                                                                                                                                  mouse_pos,pressure,modifier)) == Smach::RESULT_ACCEPT)
2367                        return true;
2368                else if ((event->motion.state & GDK_BUTTON2_MASK) &&
2369                                 canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DRAG, BUTTON_MIDDLE,
2370                                                                                                                                   mouse_pos, pressure, modifier)) == Smach::RESULT_ACCEPT)
2371                        return true;
2372                else if ((event->motion.state & GDK_BUTTON3_MASK) &&
2373                                 canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DRAG, BUTTON_RIGHT,
2374                                                                                                                                   mouse_pos, pressure, modifier)) == Smach::RESULT_ACCEPT)
2375                        return true;
2376                else if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_MOTION, BUTTON_NONE,
2377                                                                                                                                  mouse_pos, pressure,modifier)) == Smach::RESULT_ACCEPT)
2378                        return true;
2379
2380                break;
2381
2382        case GDK_BUTTON_RELEASE:
2383        {
2384                bool ret(false);
2385
2386                switch(dragging)
2387                {
2388                case DRAG_GUIDE :
2389                {
2390                        double y,x;
2391                        if(*(event->button.axes))
2392                        {
2393                                x=(event->button.axes[0]);
2394                                y=(event->button.axes[1]);
2395                        }
2396                        else
2397                        {
2398                                x=event->button.x;
2399                                y=event->button.y;
2400                        }
2401
2402                        // Erase the guides if dragged into the rulers
2403                        if(curr_guide_is_x && !isnan(x) && x<0.0 )
2404                        {
2405                                get_guide_list_x().erase(curr_guide);
2406                        }
2407                        else if(!curr_guide_is_x && !isnan(y) && y<0.0 )
2408                        {
2409                                get_guide_list_y().erase(curr_guide);
2410                        }
2411
2412                        drawing_area->queue_draw();
2413
2414                        dragging=DRAG_NONE;
2415                        save_meta_data();
2416                        return true;
2417                }
2418                break;
2419                case DRAG_DUCK :
2420                {
2421                        synfigapp::Action::PassiveGrouper grouper(instance.get(),_("Move"));
2422                        dragging=DRAG_NONE;
2423                        //translate_selected_ducks(mouse_pos);
2424                        set_axis_lock(false);
2425
2426                        try{
2427                        get_canvas_view()->duck_refresh_flag=false;
2428                        get_canvas_view()->duck_refresh_needed=false;
2429                        const bool drag_did_anything(end_duck_drag());
2430                        get_canvas_view()->duck_refresh_flag=true;
2431                        if(!drag_did_anything)
2432                        {
2433                        //!TODO Remove HARDCODED UI SPECIFICATION, make it config ready
2434
2435                // Single click duck selection on WorkArea [Part II]
2436                                // if we originally clicked on a selected duck ...
2437                                if(clicked_duck)
2438                                {
2439                                        // ... and CTRL is pressed, then just toggle the clicked duck
2440                                        //     or not SHIFT is pressed, make the clicked duck the
2441                                    //     only selected duck. (Nota : SHIFT just add to the selection)
2442                                        if(modifier&GDK_CONTROL_MASK)
2443                                                unselect_duck(clicked_duck);
2444                                        else if (!(modifier&GDK_SHIFT_MASK))
2445                                        {
2446                                                clear_selected_ducks();
2447                                                select_duck(clicked_duck);
2448                                        }
2449                                        clicked_duck->signal_user_click(0)();
2450                                }
2451                        }
2452                        else
2453                        {
2454                                if(canvas_view->duck_refresh_needed)
2455                                        canvas_view->queue_rebuild_ducks();
2456                                return true;
2457                        }
2458                        }catch(String)
2459                        {
2460                                canvas_view->duck_refresh_flag=true;
2461                                canvas_view->queue_rebuild_ducks();
2462                                return true;
2463                        }
2464                        //queue_draw();
2465                        clicked_duck=0;
2466
2467                        ret=true;
2468                }
2469                break;
2470                case DRAG_BEZIER :
2471                {
2472                        synfigapp::Action::PassiveGrouper grouper(instance.get(),_("Move"));
2473                        dragging=DRAG_NONE;
2474                        //translate_selected_ducks(mouse_pos);
2475                        set_axis_lock(false);
2476
2477                        try{
2478                        get_canvas_view()->duck_refresh_flag=false;
2479                        get_canvas_view()->duck_refresh_needed=false;
2480                        const bool drag_did_anything(end_bezier_drag());
2481                        get_canvas_view()->duck_refresh_flag=true;
2482                        if(!drag_did_anything)
2483                        {
2484                                // We didn't move the bezier, just clicked on it
2485                                canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,BUTTON_LEFT,mouse_pos,pressure,modifier));
2486                                canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_UP,BUTTON_LEFT,mouse_pos,pressure,modifier));
2487                        }
2488                        else
2489                        {
2490                                if(canvas_view->duck_refresh_needed)
2491                                        canvas_view->queue_rebuild_ducks();
2492                                return true;
2493                        }
2494                        }catch(String)
2495                        {
2496                                canvas_view->duck_refresh_flag=true;
2497                                canvas_view->queue_rebuild_ducks();
2498                                return true;
2499                        }
2500                        //queue_draw();
2501                        clicked_duck=0;
2502
2503                        ret=true;
2504                }
2505                break;
2506
2507                case DRAG_BOX:
2508                {
2509                        dragging=DRAG_NONE;
2510                        if((drag_point-mouse_pos).mag()>radius/2.0f)
2511                        {
2512                                if(canvas_view->get_smach().process_event(EventBox(drag_point,mouse_pos,MouseButton(event->button.button),modifier))==Smach::RESULT_ACCEPT)
2513                                        return true;
2514
2515                /*
2516                 * Commented out because now the work is
2517                 * done in Renderer_Dragbox::event_vfunc
2518                 *
2519
2520                                // when dragging a box around some ducks:
2521                                // SHIFT selects; CTRL toggles; SHIFT+CTRL unselects; <none> clears all then selects
2522
2523                                if(modifier&GDK_SHIFT_MASK)
2524                                        select_ducks_in_box(drag_point,mouse_pos);
2525
2526                                if(modifier&GDK_CONTROL_MASK)
2527                                        toggle_select_ducks_in_box(drag_point,mouse_pos);
2528                                else if(!(modifier&GDK_SHIFT_MASK))
2529                                {
2530                                        clear_selected_ducks();
2531                                        select_ducks_in_box(drag_point,mouse_pos);
2532                                }
2533                                *
2534                                */
2535                                ret=true;
2536                        }
2537                        else
2538                        {
2539                                if(allow_layer_clicks)
2540                                {
2541                                        Layer::Handle layer(get_canvas()->find_layer(get_canvas_view()->get_context_params(),drag_point));
2542                                        //if(layer)
2543                                        {
2544                                                if(canvas_view->get_smach().process_event(EventLayerClick(layer,BUTTON_LEFT,mouse_pos,modifier))==Smach::RESULT_OK)
2545                                                        signal_layer_selected_(layer);
2546                                                ret=true;
2547                                        }
2548                                }
2549                                else
2550                                {
2551                                        signal_user_click(0)(mouse_pos);
2552                                }
2553                        }
2554                        drawing_area->queue_draw();
2555                }
2556                break;
2557                default:
2558                {
2559                }
2560                } //end switch dragging
2561
2562                dragging=DRAG_NONE;
2563
2564                if(canvas_view->get_smach().process_event(EventMouse(EVENT_WORKAREA_MOUSE_BUTTON_UP,MouseButton(event->button.button),mouse_pos,pressure,modifier))==Smach::RESULT_ACCEPT)
2565                        ret=true;
2566
2567                return ret;
2568        }
2569                break;
2570        case GDK_SCROLL:
2571        {
2572                // Handle a mouse scrolling event like Xara Xtreme and
2573                // Inkscape:
2574
2575            //!TODO Remove HARDCODED UI SPECIFICATION, make it config ready
2576
2577                // Scroll up/down: scroll up/down
2578                // Shift + scroll up/down: scroll left/right
2579                // Control + scroll up/down: zoom in/out
2580
2581                if(modifier&GDK_CONTROL_MASK)
2582                {
2583
2584                        // The zoom is performed while preserving the pointer
2585                        // position as a fixed point (similarly to Xara Xtreme and
2586                        // Inkscape).
2587
2588                        // The strategy used below is to scroll to the updated
2589                        // position, then zoom. This is easy to implement within
2590                        // the present architecture, but has the disadvantage of
2591                        // triggering multiple visible refreshes. Note: 1.25 is
2592                        // the hard wired ratio in zoom_in()/zoom_out(). The
2593                        // variable "drift" compensates additional inaccuracies in
2594                        // the zoom. There is also an additional minus sign for
2595                        // the inverted y coordinates.
2596
2597                        // FIXME: One might want to figure out where in the code
2598                        // this empirical drift is been introduced.
2599
2600                        const synfig::Point scroll_point(get_scrollx_adjustment()->get_value(),get_scrolly_adjustment()->get_value());
2601                        const double drift = 0.052;
2602
2603                        switch(event->scroll.direction)
2604                        {
2605                                case GDK_SCROLL_UP:
2606                                case GDK_SCROLL_RIGHT:
2607                                        get_scrollx_adjustment()->set_value(scroll_point[0]+(mouse_pos[0]-scroll_point[0])*(1.25-(1+drift)));
2608                                        get_scrolly_adjustment()->set_value(scroll_point[1]-(mouse_pos[1]+scroll_point[1])*(1.25-(1+drift)));
2609                                        zoom_in();
2610                                        break;
2611                                case GDK_SCROLL_DOWN:
2612                                case GDK_SCROLL_LEFT:
2613                                        get_scrollx_adjustment()->set_value(scroll_point[0]+(mouse_pos[0]-scroll_point[0])*(1/1.25-(1+drift)));
2614                                        get_scrolly_adjustment()->set_value(scroll_point[1]-(mouse_pos[1]+scroll_point[1])*(1/1.25-(1+drift)));
2615                                        zoom_out();
2616                                        break;
2617                                default:
2618                                        break;
2619                        }
2620                }
2621                else if(modifier&GDK_SHIFT_MASK)
2622                {
2623                        // Scroll in either direction by 20 pixels. Ideally, the
2624                        // amount of pixels per scrolling event should be
2625                        // configurable. Xara Xtreme currently uses an (hard
2626                        // wired) amount 20 pixel, Inkscape defaults to 40 pixels.
2627
2628                        const int scroll_pixel = 20;
2629
2630                        switch(event->scroll.direction)
2631                        {
2632                                case GDK_SCROLL_UP:
2633                                        get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()-scroll_pixel*pw);
2634                                        break;
2635                                case GDK_SCROLL_DOWN:
2636                                        get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()+scroll_pixel*pw);
2637                                        break;
2638                                case GDK_SCROLL_LEFT:
2639                                        get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()+scroll_pixel*ph);
2640                                        break;
2641                                case GDK_SCROLL_RIGHT:
2642                                        get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()-scroll_pixel*ph);
2643                                        break;
2644                                default:
2645                                        break;
2646                        }
2647                }
2648                else
2649                {
2650                        // Scroll in either direction by 20 pixels. Ideally, the
2651                        // amount of pixels per scrolling event should be
2652                        // configurable. Xara Xtreme currently uses an (hard
2653                        // wired) amount 20 pixel, Inkscape defaults to 40 pixels.
2654
2655                        const int scroll_pixel = 20;
2656
2657                        switch(event->scroll.direction)
2658                        {
2659                                case GDK_SCROLL_UP:
2660                                        get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()+scroll_pixel*ph);
2661                                        break;
2662                                case GDK_SCROLL_DOWN:
2663                                        get_scrolly_adjustment()->set_value(get_scrolly_adjustment()->get_value()-scroll_pixel*ph);
2664                                        break;
2665                                case GDK_SCROLL_LEFT:
2666                                        get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()-scroll_pixel*pw);
2667                                        break;
2668                                case GDK_SCROLL_RIGHT:
2669                                        get_scrollx_adjustment()->set_value(get_scrollx_adjustment()->get_value()+scroll_pixel*pw);
2670                                        break;
2671                                default:
2672                                        break;
2673                        }
2674                }
2675        }
2676                break;
2677        default:
2678                break;
2679        }
2680                return false;
2681}
2682
2683bool
2684WorkArea::on_hruler_event(GdkEvent *event)
2685{
2686        switch(event->type)
2687    {
2688        case GDK_BUTTON_PRESS:
2689                if(dragging==DRAG_NONE && show_guides)
2690                {
2691                        dragging=DRAG_GUIDE;
2692                        curr_guide=get_guide_list_y().insert(get_guide_list_y().begin(), 0.0);
2693                        curr_guide_is_x=false;
2694                }
2695                return true;
2696                break;
2697
2698        case GDK_MOTION_NOTIFY:
2699                // Guide movement
2700                if(dragging==DRAG_GUIDE && curr_guide_is_x==false)
2701                {
2702                        // Event is in the hruler, which has a slightly different
2703                        // coordinate system from the canvas.
2704                        gint exes_count = gdk_device_get_n_axes(event->motion.device);
2705                        for(gint i = 0; i < exes_count; ++i)
2706                                if (gdk_device_get_axis_use(event->motion.device, i) == GDK_AXIS_Y)
2707                                        event->motion.axes[i] -= hruler->get_height()+2;
2708                        event->motion.y -= hruler->get_height()+2;
2709
2710                        // call the on drawing area event to refresh eveything.
2711                        on_drawing_area_event(event);
2712                }
2713                return true;
2714                break;
2715
2716        case GDK_BUTTON_RELEASE:
2717                if(dragging==DRAG_GUIDE && curr_guide_is_x==false)
2718                {
2719                        dragging=DRAG_NONE;
2720                        save_meta_data();
2721//                      get_guide_list_y().erase(curr_guide);
2722                }
2723                break;
2724                return true;
2725        default:
2726                break;
2727        }
2728        return false;
2729}
2730
2731bool
2732WorkArea::on_vruler_event(GdkEvent *event)
2733{
2734        switch(event->type)
2735    {
2736        case GDK_BUTTON_PRESS:
2737                if(dragging==DRAG_NONE && show_guides)
2738                {
2739                        dragging=DRAG_GUIDE;
2740                        curr_guide=get_guide_list_x().insert(get_guide_list_x().begin(),0.0);
2741                        curr_guide_is_x=true;
2742                }
2743                return true;
2744                break;
2745
2746        case GDK_MOTION_NOTIFY:
2747                // Guide movement
2748                if(dragging==DRAG_GUIDE && curr_guide_is_x==true)
2749                {
2750                        // Event is in the vruler, which has a slightly different
2751                        // coordinate system from the canvas.
2752                        gint exes_count = gdk_device_get_n_axes(event->motion.device);
2753                        for(gint i = 0; i < exes_count; ++i)
2754                                if (gdk_device_get_axis_use(event->motion.device, i) == GDK_AXIS_X)
2755                                        event->motion.axes[i] -= vruler->get_width()+2;
2756                        event->motion.x -= vruler->get_width()+2;
2757
2758                        // call the on drawing area event to refresh eveything.
2759                        on_drawing_area_event(event);
2760                }
2761                return true;
2762                break;
2763
2764        case GDK_BUTTON_RELEASE:
2765                if(dragging==DRAG_GUIDE && curr_guide_is_x==true)
2766                {
2767                        dragging=DRAG_NONE;
2768                        save_meta_data();
2769//                      get_guide_list_x().erase(curr_guide);
2770                }
2771                break;
2772                return true;
2773        default:
2774                break;
2775        }
2776        return false;
2777}
2778
2779
2780void
2781WorkArea::refresh_dimension_info()
2782{
2783        synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
2784
2785        canvaswidth=rend_desc.get_br()[0]-rend_desc.get_tl()[0];
2786        canvasheight=rend_desc.get_br()[1]-rend_desc.get_tl()[1];
2787
2788        pw=canvaswidth/w;
2789        ph=canvasheight/h;
2790
2791        scrollx_adjustment->set_page_increment(abs(get_grid_size()[0]));
2792        scrollx_adjustment->set_step_increment(abs(pw));
2793        scrollx_adjustment->set_lower(-abs(canvaswidth));
2794        scrollx_adjustment->set_upper(abs(canvaswidth));
2795        scrolly_adjustment->set_lower(-abs(canvasheight));
2796        scrolly_adjustment->set_upper(abs(canvasheight));
2797        scrolly_adjustment->set_step_increment(abs(ph));
2798        scrolly_adjustment->set_page_increment(abs(get_grid_size()[1]));
2799
2800        if(drawing_area->get_width()<=0 || drawing_area->get_height()<=0 || w==0 || h==0)
2801                return;
2802
2803        const synfig::Point focus_point(get_focus_point());
2804        const synfig::Real x(focus_point[0]/pw+drawing_area->get_width()/2-w/2);
2805        const synfig::Real y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2806
2807        window_tl[0]=rend_desc.get_tl()[0]-pw*x;
2808        window_br[0]=rend_desc.get_br()[0]+pw*(drawing_area->get_width()-x-w);
2809
2810        window_tl[1]=rend_desc.get_tl()[1]-ph*y;
2811        window_br[1]=rend_desc.get_br()[1]+ph*(drawing_area->get_height()-y-h);
2812
2813        hruler->set_min( Distance(window_tl[0],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc) );
2814        hruler->set_max( Distance(window_br[0],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc) );
2815        vruler->set_min( Distance(window_tl[1],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc) );
2816        vruler->set_max( Distance(window_br[1],Distance::SYSTEM_UNITS).get(App::distance_system,rend_desc) );
2817
2818        view_window_changed();
2819}
2820
2821
2822synfig::Point
2823WorkArea::screen_to_comp_coords(synfig::Point pos)const
2824{
2825        synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
2826        //synfig::Vector::value_type canvaswidth=rend_desc.get_br()[0]-rend_desc.get_tl()[0];
2827        //synfig::Vector::value_type canvasheight=rend_desc.get_br()[1]-rend_desc.get_tl()[1];
2828        //synfig::Vector::value_type pw=canvaswidth/w;
2829        //synfig::Vector::value_type ph=canvasheight/h;
2830        Vector focus_point=get_focus_point();
2831        synfig::Vector::value_type x=focus_point[0]/pw+drawing_area->get_width()/2-w/2;
2832        synfig::Vector::value_type y=focus_point[1]/ph+drawing_area->get_height()/2-h/2;
2833
2834        return rend_desc.get_tl()-synfig::Point(pw*x,ph*y)+synfig::Point(pw*pos[0],ph*pos[1]);
2835}
2836
2837synfig::Point
2838WorkArea::comp_to_screen_coords(synfig::Point /*pos*/)const
2839{
2840        synfig::warning("WorkArea::comp_to_screen_coords: Not yet implemented");
2841        return synfig::Point();
2842}
2843
2844int
2845WorkArea::next_unrendered_tile(int refreshes)const
2846{
2847        bool uses_cairo=studio::App::workarea_uses_cairo;
2848        if(tile_book.empty() && !uses_cairo)
2849                return -1;
2850        else if (cairo_book.empty() && uses_cairo)
2851                return -1;
2852
2853        //const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
2854
2855        const synfig::Vector focus_point(get_focus_point());
2856
2857        // Calculate the window coordinates of the top-left
2858        // corner of the canvas.
2859        const synfig::Vector::value_type
2860                x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
2861                y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
2862
2863        int div = low_res_pixel_size;
2864        const int width_in_tiles(w/tile_w+((low_resolution?((w/div)%(tile_w/div)):(w%tile_w))?1:0));
2865        const int height_in_tiles(h/tile_h+(h%tile_h?1:0));
2866
2867        int
2868                u(0),v(0),
2869                u1(int(-x/tile_w)),
2870                v1(int(-y/tile_h)),
2871                u2(int((-x+drawing_area->get_width())/tile_w+1)),
2872                v2(int((-y+drawing_area->get_height())/tile_h+1));
2873
2874        if(u2>width_in_tiles)u2=width_in_tiles;
2875        if(v2>height_in_tiles)v2=height_in_tiles;
2876        if(u1<0)u1=0;
2877        if(v1<0)v1=0;
2878
2879        int last_good_tile(-1);
2880
2881        for(v=v1;v<v2;v++)
2882                for(u=u1;u<u2;u++)
2883                {
2884                        int index(v*width_in_tiles+u);
2885                        if(!uses_cairo)
2886                        {
2887                                if(tile_book[index].second<refreshes)
2888                                {
2889                                        last_good_tile=index;
2890                                        if(rand()%8==0)
2891                                                return index;
2892                                }
2893                        }
2894                        else
2895                        {
2896                               
2897                                if(cairo_book[index].refreshes<refreshes)
2898                                {
2899                                        last_good_tile=index;
2900                                        if(rand()%8==0)
2901                                                return index;
2902                                }
2903                        }
2904                       
2905                }
2906        return last_good_tile;
2907}
2908
2909/*
2910template <typename F, typename T=WorkAreaRenderer, typename R=typename F::result_type>
2911class handle2ptr_t : public std::unary_function<typename etl::handle<T>,R>
2912{
2913private:
2914        F func;
2915public:
2916        handle2ptr_t(const F &func):func(func) { };
2917
2918        R operator()(typename etl::handle<T> x) { return func(*x); }
2919};
2920
2921template <typename F>
2922handle2ptr_t<F>
2923handle2ptr(F func)
2924{
2925        return handle2ptr_t<F>(func);
2926}
2927        for_each(
2928                renderer_set_.begin(),
2929                renderer_set_.end(),
2930                handle2ptr(
2931                        sigc::bind(
2932                                sigc::bind(
2933                                        sigc::mem_fun(
2934                                                &WorkAreaRenderer::render_vfunc
2935                                        ),
2936                                        Gdk::Rectangle(event->area)
2937                                ),
2938                                drawing_area->get_window()
2939                        )
2940                )
2941        );
2942*/
2943
2944#ifdef SINGLE_THREADED
2945/* resize bug workaround */
2946gboolean
2947WorkArea::__refresh_second_check(gpointer data)
2948{
2949        WorkArea *work_area(static_cast<WorkArea*>(data));
2950        work_area->refresh_second_check();
2951        return 0;
2952}
2953
2954void
2955WorkArea::refresh_second_check()
2956{
2957        //resize_timeout_connect.disconnect();
2958        int width = canvas_view->get_width();
2959        int height = canvas_view->get_height();
2960        if (width==old_window_width && height==old_window_height ) {
2961                queue_draw();
2962                //GdkEventExpose event;
2963                //refresh(&event);
2964        }
2965}
2966#endif
2967
2968bool
2969WorkArea::refresh(const Cairo::RefPtr<Cairo::Context> &cr)
2970{
2971#ifdef SINGLE_THREADED
2972        /* resize bug workaround */
2973        if (App::single_threaded) {
2974                int width;
2975                int height;
2976                bool resize_in_progress;
2977                resize_in_progress = false;
2978                width = canvas_view->get_width();
2979                height = canvas_view->get_height();
2980                //synfig::info("Size: %i, %i",width,height);
2981                if (width!=old_window_width || height!=old_window_height ) {
2982
2983                        resize_in_progress = true;
2984
2985                        //queue second check
2986                        int func_id;
2987                        func_id=g_timeout_add_full(
2988                                G_PRIORITY_DEFAULT,     // priority -
2989                                200,                    // interval - the time between calls to the function, in milliseconds (1/1000ths of a second)
2990                                __refresh_second_check, // function - function to call
2991                                this,                           // data     - data to pass to function
2992                                NULL);                          // notify   - function to call when the idle is removed, or NULL
2993                }
2994                old_window_width=width;
2995                old_window_height=height;
2996                if (resize_in_progress){
2997                        if (get_updating())
2998                        {
2999                                stop_updating();
3000                        }
3001                        return true;
3002                }
3003        }
3004#endif
3005       
3006        assert(get_canvas());
3007
3008        //!Check if the window we want draw is ready
3009        Glib::RefPtr<Gdk::Window> draw_area_window = drawing_area->get_window();
3010        if(!draw_area_window) return false;
3011
3012        //const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
3013
3014        const synfig::Vector focus_point(get_focus_point());
3015
3016        // Update the old focus point
3017        last_focus_point=focus_point;
3018
3019        // Draw out the renderables
3020        {
3021                std::set<etl::handle<WorkAreaRenderer> >::iterator iter;
3022                for(iter=renderer_set_.begin();iter!=renderer_set_.end();++iter)
3023                {
3024                        if((*iter)->get_enabled())
3025                                (*iter)->render_vfunc(
3026                                        draw_area_window,
3027                                        Gdk::Rectangle(0, 0, draw_area_window->get_width(), draw_area_window->get_height())
3028                                );
3029                }
3030        }
3031
3032        // Calculate the window coordinates of the top-left
3033        // corner of the canvas.
3034        //const synfig::Vector::value_type
3035        //      x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
3036        //      y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
3037
3038        //const synfig::Vector::value_type window_startx(window_tl[0]);
3039        //const synfig::Vector::value_type window_endx(window_br[0]);
3040        //const synfig::Vector::value_type window_starty(window_tl[1]);
3041        //const synfig::Vector::value_type window_endy(window_br[1]);
3042
3043        // If we are in animate mode, draw a red border around the screen
3044        if(canvas_interface->get_mode()&synfigapp::MODE_ANIMATE)
3045        {
3046// #define USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
3047#ifdef USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
3048                // This method of drawing the red border doesn't work on any
3049                // Gtk theme which uses the crux-engine, hcengine, industrial,
3050                // mist, or ubuntulooks engine, such as the default ubuntu
3051                // 'Human' theme.
3052                drawing_frame->modify_bg(Gtk::STATE_NORMAL,Gdk::Color("#FF0000"));
3053#else
3054                // So let's do it in a more primitive fashion.
3055                Cairo::RefPtr<Cairo::Context> cr = draw_area_window->create_cairo_context();
3056                cr->save();
3057
3058                cr->set_source_rgb(1,0,0);
3059                cr->set_line_cap(Cairo::LINE_CAP_BUTT);
3060                cr->set_line_join(Cairo::LINE_JOIN_MITER);
3061                cr->set_antialias(Cairo::ANTIALIAS_NONE);
3062                cr->set_line_width(10);
3063
3064                cr->rectangle(
3065                        0,0, // x,y
3066                        drawing_area->get_width(),drawing_area->get_height() //w,h
3067                        );
3068                cr->stroke();
3069                cr->restore();
3070#endif
3071        }
3072#ifdef USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
3073        else
3074                drawing_frame->unset_bg(Gtk::STATE_NORMAL);
3075#endif
3076
3077        return true;
3078}
3079
3080void
3081WorkArea::done_rendering()
3082{
3083/*
3084        assert(buffer);
3085        assert(w>0);
3086        assert(h>0);
3087        pix_buf=Gdk::Pixbuf::create_from_data(
3088                buffer, // pointer to the data
3089                Gdk::COLORSPACE_RGB, // the colorspace
3090                true, // has alpha?
3091                8, // bits per sample
3092                w,      // width
3093                h,      // height
3094                w*bpp); // stride (pitch)
3095        assert(pix_buf);
3096*/
3097}
3098
3099
3100void
3101WorkArea::set_quality(int x)
3102{
3103        if(x==quality)
3104                return;
3105        quality=x;
3106        queue_render_preview();
3107}
3108
3109void
3110WorkArea::set_low_res_pixel_size(int x)
3111{
3112        if(x==low_res_pixel_size)
3113                return;
3114        low_res_pixel_size=x;
3115        queue_render_preview();
3116}
3117
3118namespace studio
3119{
3120class WorkAreaProgress : public synfig::ProgressCallback
3121{
3122        WorkArea *work_area;
3123        ProgressCallback *cb;
3124
3125public:
3126
3127        WorkAreaProgress(WorkArea *work_area,ProgressCallback *cb):
3128                work_area(work_area),cb(cb)
3129        {
3130                assert(cb);
3131        }
3132
3133        virtual bool
3134        task(const std::string &str)
3135        {
3136                if(work_area->dirty)
3137                        return false;
3138                return cb->task(str);
3139        }
3140
3141        virtual bool
3142        error(const std::string &err)
3143        {
3144                if(work_area->dirty)
3145                        return false;
3146                return cb->error(err);
3147        }
3148
3149        virtual bool
3150        amount_complete(int current, int total)
3151        {
3152                if(work_area->dirty)
3153                        return false;
3154                return cb->amount_complete(current,total);
3155        }
3156};
3157}
3158
3159bool
3160studio::WorkArea::async_update_preview()
3161{
3162#ifdef SINGLE_THREADED
3163        if (get_updating())
3164        {
3165                stop_updating();
3166                queue_render_preview();
3167                return false;
3168        }
3169#endif
3170
3171        async_renderer=0;
3172
3173        queued=false;
3174        canceled_=false;
3175        get_canvas_view()->reset_cancel_status();
3176
3177        // This object will mark us as busy until
3178        // we are done.
3179        //studio::App::Busy busy;
3180
3181        //WorkAreaProgress callback(this,get_canvas_view()->get_ui_interface().get());
3182        //synfig::ProgressCallback *cb=&callback;
3183
3184        if(!get_visible())return false;
3185
3186        /*
3187        // If we are queued to render the scene at the next idle
3188        // go ahead and de-queue it.
3189        if(render_idle_func_id)
3190        {
3191                g_source_remove(render_idle_func_id);
3192                //queued=false;
3193                render_idle_func_id=0;
3194        }
3195        */
3196
3197        dirty=false;
3198        get_canvas_view()->reset_cancel_status();
3199
3200        //bool ret=false;
3201        RendDesc desc=get_canvas()->rend_desc();
3202
3203        int w=(int)(desc.get_w()*zoom);
3204        int h=(int)(desc.get_h()*zoom);
3205
3206        // ensure that the size we draw is at least one pixel in each dimension
3207        int min_size = low_resolution ? low_res_pixel_size : 1;
3208        if (w < min_size) w = min_size;
3209        if (h < min_size) h = min_size;
3210
3211        // Setup the description parameters
3212        desc.set_antialias(1);
3213        desc.set_time(cur_time);
3214        desc.set_render_excluded_contexts(true);
3215
3216        set_rend_desc(desc);
3217
3218        // Create the render target
3219        handle<Target> target;
3220
3221        // if we have lots of pixels to render and the tile renderer isn't disabled, use it
3222        int div;
3223        div = low_resolution ? low_res_pixel_size : 1;
3224        if(studio::App::workarea_uses_cairo)
3225        {
3226                if ((w*h > 240*div*135*div && !getenv("SYNFIG_DISABLE_TILE_RENDER")) || getenv("SYNFIG_FORCE_TILE_RENDER"))
3227                {
3228                        handle<WorkAreaTarget_Cairo_Tile> trgt(new class WorkAreaTarget_Cairo_Tile(this,w,h));
3229                        trgt->set_rend_desc(&desc);
3230                        trgt->set_onion_skin(get_onion_skin(), onion_skins);
3231                        target=trgt;
3232                }
3233                else
3234                {
3235                        handle<WorkAreaTarget_Cairo> trgt(new class WorkAreaTarget_Cairo(this,w,h));
3236                        trgt->set_rend_desc(&desc);
3237                        trgt->set_onion_skin(get_onion_skin(), onion_skins);
3238                        target=trgt;
3239                }
3240
3241        }
3242        else if ((w*h > 240*div*135*div && !getenv("SYNFIG_DISABLE_TILE_RENDER")) || getenv("SYNFIG_FORCE_TILE_RENDER"))
3243        {
3244                // do a tile render
3245                handle<WorkAreaTarget> trgt(new class WorkAreaTarget(this,w,h));
3246
3247                trgt->set_rend_desc(&desc);
3248                trgt->set_onion_skin(get_onion_skin(), onion_skins);
3249                target=trgt;
3250        }
3251        else
3252        {
3253                // do a scanline render
3254                handle<WorkAreaTarget_Full> trgt(new class WorkAreaTarget_Full(this,w,h));
3255
3256                trgt->set_rend_desc(&desc);
3257                trgt->set_onion_skin(get_onion_skin(), onion_skins);
3258                target=trgt;
3259        }
3260
3261        // We can rest assured that our time has already
3262        // been set, so there is no need to have to
3263        // recalculate that over again.
3264        // UPDATE: This is kind of needless with
3265        // the way that time is handled now in SYNFIG.
3266        //target->set_avoid_time_sync(true);
3267        async_renderer=new AsyncRenderer(target);
3268        async_renderer->signal_finished().connect(
3269                sigc::mem_fun(this,&WorkArea::async_update_finished)
3270        );
3271
3272        rendering=true;
3273        async_renderer->start();
3274
3275        synfig::ProgressCallback *cb=get_canvas_view()->get_ui_interface().get();
3276
3277        rendering=true;
3278        cb->task(_("Rendering..."));
3279        rendering=true;
3280
3281        return true;
3282}
3283
3284void
3285studio::WorkArea::async_update_finished()
3286{
3287        synfig::ProgressCallback *cb=get_canvas_view()->get_ui_interface().get();
3288
3289        rendering=false;
3290
3291        if(!async_renderer)
3292                return;
3293
3294        // If we completed successfully, then
3295        // we aren't dirty anymore
3296        if(async_renderer->has_success())
3297        {
3298                Real execution_time = async_renderer->get_execution_time();
3299                if (execution_time > 0.0)
3300                {
3301                        cb->task( strprintf("%s (%s %f %s)",
3302                                _("Idle"),
3303                                _("Last rendering time"),
3304                                async_renderer->get_execution_time(),
3305                                _("sec") ));
3306                }
3307                else
3308                {
3309                        cb->task(_("Idle"));
3310                }
3311
3312        }
3313        else
3314        {
3315                dirty=true;
3316                cb->task(_("Render Failed"));
3317        }
3318        //get_canvas_view()->reset_cancel_status();
3319        done_rendering();
3320}
3321
3322bool
3323studio::WorkArea::sync_update_preview()
3324{
3325        //      const Time &time(cur_time);
3326
3327        canceled_=false;
3328        get_canvas_view()->reset_cancel_status();
3329
3330        async_renderer=0;
3331
3332again:
3333        // This object will mark us as busy until
3334        // we are done.
3335        studio::App::Busy busy;
3336
3337        WorkAreaProgress callback(this,get_canvas_view()->get_ui_interface().get());
3338        synfig::ProgressCallback *cb=&callback;
3339
3340        // We don't want to render if we are already rendering
3341        if(rendering)
3342        {
3343                dirty=true;
3344                return false;
3345        }
3346
3347        if(!get_visible())return false;
3348        get_canvas()->set_time(get_time());
3349        get_canvas_view()->get_smach().process_event(EVENT_REFRESH_DUCKS);
3350        signal_rendering()();
3351
3352        // If we are queued to render the scene at the next idle
3353        // go ahead and de-queue it.
3354        if(render_idle_func_id)
3355        {
3356                g_source_remove(render_idle_func_id);
3357                //queued=false;
3358                render_idle_func_id=0;
3359        }
3360        // Start rendering
3361        rendering=true;
3362
3363        dirty=false;
3364        get_canvas_view()->reset_cancel_status();
3365
3366        RendDesc desc=get_canvas()->rend_desc();
3367        //newdesc->set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
3368
3369        int w=(int)(desc.get_w()*zoom);
3370        int h=(int)(desc.get_h()*zoom);
3371
3372        // Setup the description parameters
3373        desc.set_antialias(1);
3374        desc.set_time(cur_time);
3375        //desc.set_wh(w,h);
3376
3377        set_rend_desc(desc);
3378
3379        // Create the render target
3380        handle<Target> target;
3381        if(studio::App::workarea_uses_cairo)
3382                target=new class WorkAreaTarget_Cairo(this,w,h);
3383        else
3384                target=new class WorkAreaTarget(this, w, h);
3385
3386        target->set_rend_desc(&desc);
3387
3388        // We can rest assured that our time has already
3389        // been set, so there is no need to have to
3390        // recalculate that over again.
3391        target->set_avoid_time_sync(true);
3392
3393        if(cb)
3394                cb->task(strprintf(_("Rendering canvas %s..."),get_canvas()->get_name().c_str()));
3395
3396        bool ret = target->render(cb);
3397
3398        if(!ret && !get_canvas_view()->get_cancel_status() && dirty)
3399        {
3400                rendering=false;
3401                //canceled_=true;
3402                goto again;
3403        }
3404        if(get_canvas_view()->get_cancel_status())
3405                canceled_=true;
3406
3407        if(cb)
3408        {
3409                if(ret)
3410                        cb->task(_("Idle"));
3411                else
3412                        cb->task(_("Render Failed"));
3413                cb->amount_complete(0,1);
3414        }
3415
3416        // Refresh the work area to make sure that
3417        // it is being displayed correctly
3418        drawing_area->queue_draw();
3419
3420        // If we completed successfully, then
3421        // we aren't dirty anymore
3422        if(ret)
3423        {
3424                dirty=false;
3425                //queued=false;
3426        }
3427        else dirty=true;
3428        rendering=false;
3429        //get_canvas_view()->reset_cancel_status();
3430        done_rendering();
3431        return ret;
3432}
3433
3434void
3435studio::WorkArea::async_render_preview(synfig::Time time)
3436{
3437        cur_time=time;
3438        //tile_book.clear();
3439
3440        refreshes+=5;
3441        if(!get_visible())return;
3442
3443        get_canvas()->set_time(get_time());
3444        get_canvas_view()->get_smach().process_event(EVENT_REFRESH_DUCKS);
3445        signal_rendering()();
3446
3447        async_update_preview();
3448}
3449void
3450WorkArea::async_render_preview()
3451{
3452        return async_render_preview(get_canvas_view()->get_time());
3453}
3454
3455bool
3456studio::WorkArea::sync_render_preview(synfig::Time time)
3457{
3458        cur_time=time;
3459        //tile_book.clear();
3460        refreshes+=5;
3461        if(!get_visible())return false;
3462        return sync_update_preview();
3463}
3464
3465bool
3466WorkArea::sync_render_preview()
3467{
3468        return sync_render_preview(get_canvas_view()->get_time());
3469}
3470
3471void
3472WorkArea::sync_render_preview_hook()
3473{
3474        sync_render_preview(get_canvas_view()->get_time());
3475}
3476
3477void
3478WorkArea::queue_scroll()
3479{
3480        //!Check if the window we want draw is ready
3481        Glib::RefPtr<Gdk::Window> draw_area_window = drawing_area->get_window();
3482        if(!draw_area_window) return;
3483
3484//      const synfig::RendDesc &rend_desc(get_canvas()->rend_desc());
3485
3486        const synfig::Point focus_point(get_focus_point());
3487
3488        const synfig::Real
3489                new_x(focus_point[0]/pw+drawing_area->get_width()/2-w/2),
3490                new_y(focus_point[1]/ph+drawing_area->get_height()/2-h/2);
3491
3492        const synfig::Real
3493                old_x(last_focus_point[0]/pw+drawing_area->get_width()/2-w/2),
3494                old_y(last_focus_point[1]/ph+drawing_area->get_height()/2-h/2);
3495
3496        // If the coordinates didn't change, we shouldn't queue a draw
3497        if(old_x==new_x && old_y==new_y)
3498                return;
3499
3500        const int
3501                dx(round_to_int(old_x)-round_to_int(new_x)),
3502                dy(round_to_int(old_y)-round_to_int(new_y));
3503
3504        draw_area_window->scroll(-dx,-dy);
3505
3506        if (timecode_width && timecode_height)
3507        {
3508                drawing_area->queue_draw_area(timecode_x,    timecode_y,    timecode_x+timecode_width,    timecode_y+timecode_height);
3509                drawing_area->queue_draw_area(timecode_x-dx, timecode_y-dy, timecode_x-dx+timecode_width, timecode_y-dy+timecode_height);
3510        }
3511
3512        if (bonesetup_width && bonesetup_height)
3513        {
3514                drawing_area->queue_draw_area(bonesetup_x,    bonesetup_y,    bonesetup_x+bonesetup_width,    bonesetup_y+bonesetup_height);
3515                drawing_area->queue_draw_area(bonesetup_x-dx, bonesetup_y-dy, bonesetup_x-dx+bonesetup_width, bonesetup_y-dy+bonesetup_height);
3516        }
3517
3518#ifndef USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
3519        if(canvas_interface->get_mode()&synfigapp::MODE_ANIMATE)
3520        {
3521                int maxx = drawing_area->get_width()-1;
3522                int maxy = drawing_area->get_height()-1;
3523
3524                if (dx > 0)
3525                {
3526                        drawing_area->queue_draw_area(      0, 0,       1, maxy);
3527                        drawing_area->queue_draw_area(maxx-dx, 0, maxx-dx, maxy);
3528                }
3529                else if (dx < 0)
3530                {
3531                        drawing_area->queue_draw_area(   maxx, 0,    maxx, maxy);
3532                        drawing_area->queue_draw_area(    -dx, 0,     -dx, maxy);
3533                }
3534                if (dy > 0)
3535                {
3536                        drawing_area->queue_draw_area(0,       0, maxx,       1);
3537                        drawing_area->queue_draw_area(0, maxy-dy, maxx, maxy-dy);
3538                }
3539                else if (dy < 0)
3540                {
3541                        drawing_area->queue_draw_area(0,    maxy, maxx,    maxy);
3542                        drawing_area->queue_draw_area(0,     -dy, maxx,     -dy);
3543                }
3544        }
3545#endif // USE_FRAME_BACKGROUND_TO_SHOW_EDIT_MODE
3546
3547        last_focus_point=focus_point;
3548}
3549
3550void
3551studio::WorkArea::zoom_in()
3552{
3553        set_zoom(zoom*1.25);
3554}
3555
3556void
3557studio::WorkArea::zoom_out()
3558{
3559        set_zoom(zoom/1.25);
3560}
3561
3562void
3563studio::WorkArea::zoom_fit()
3564{
3565        float new_zoom(min(drawing_area->get_width() * zoom / w,
3566                                           drawing_area->get_height() * zoom / h) * 0.995);
3567        if (zoom / new_zoom > 0.995 && new_zoom / zoom > 0.995)
3568        {
3569                set_zoom(prev_zoom);
3570                return set_focus_point(previous_focus);
3571        }
3572        previous_focus = get_focus_point();
3573        prev_zoom = zoom;
3574        set_zoom(new_zoom);
3575        set_focus_point(Point(0,0));
3576}
3577
3578void
3579studio::WorkArea::zoom_norm()
3580{
3581        if (zoom == 1.0) return set_zoom(prev_zoom);
3582        prev_zoom = zoom;
3583        set_zoom(1.0f);
3584}
3585
3586gboolean
3587studio::WorkArea::__render_preview(gpointer data)
3588{
3589        WorkArea *work_area(static_cast<WorkArea*>(data));
3590
3591        // there's no point anyone trying to cancel the timer now - it's gone off already
3592        work_area->render_idle_func_id = 0;
3593
3594        work_area->queued=false;
3595        work_area->async_render_preview(work_area->get_canvas_view()->get_time());
3596
3597        return 0;
3598}
3599
3600void
3601studio::WorkArea::queue_render_preview()
3602{
3603        //synfig::info("queue_render_preview(): called for %s", get_canvas_view()->get_time().get_string().c_str());
3604
3605        if(queued==true)
3606        {
3607                return;
3608                //synfig::info("queue_render_preview(): already queued, unqueuing");
3609/*              if(render_idle_func_id)
3610                        g_source_remove(render_idle_func_id);
3611                render_idle_func_id=0;
3612                queued=false;
3613*/
3614                //async_renderer=0;
3615        }
3616
3617        if(dirty_trap_enabled)
3618        {
3619                dirty_trap_queued++;
3620                return;
3621        }
3622
3623        int queue_time=50;
3624
3625        if(rendering)
3626                queue_time+=250;
3627
3628
3629        if(queued==false)
3630        {
3631                queued=true;
3632                //synfig::info("queue_render_preview(): (re)queuing...");
3633                //render_idle_func_id=g_idle_add_full(G_PRIORITY_DEFAULT,__render_preview,this,NULL);
3634                render_idle_func_id=g_timeout_add_full(
3635                        G_PRIORITY_DEFAULT,     // priority -
3636                        queue_time,                     // interval - the time between calls to the function, in milliseconds (1/1000ths of a second)
3637                        __render_preview,       // function - function to call
3638                        this,                           // data     - data to pass to function
3639                        NULL);                          // notify   - function to call when the idle is removed, or NULL
3640        }
3641/*      else if(rendering)
3642        {
3643                refreshes+=5;
3644                dirty=true;
3645                queue_draw();
3646        }
3647*/
3648}
3649
3650DirtyTrap::DirtyTrap(WorkArea *work_area):work_area(work_area)
3651{
3652        work_area->dirty_trap_enabled=true;
3653
3654        work_area->dirty_trap_queued=0;
3655}
3656
3657DirtyTrap::~DirtyTrap()
3658{
3659        work_area->dirty_trap_enabled=false;
3660        if(work_area->dirty_trap_queued)
3661                work_area->queue_render_preview();
3662}
3663
3664void
3665studio::WorkArea::queue_draw_preview()
3666{
3667        drawing_area->queue_draw();
3668}
3669
3670void
3671studio::WorkArea::set_cursor(const Glib::RefPtr<Gdk::Cursor> &x)
3672{
3673        //!Check if the window we want draw is ready
3674        Glib::RefPtr<Gdk::Window> draw_area_window = drawing_area->get_window();
3675        if(!draw_area_window) return;
3676       
3677        draw_area_window->set_cursor(x);
3678}
3679void
3680studio::WorkArea::set_cursor(Gdk::CursorType x)
3681{
3682        //!Check if the window we want draw is ready
3683        Glib::RefPtr<Gdk::Window> draw_area_window = drawing_area->get_window();
3684        if(!draw_area_window) return;
3685       
3686        draw_area_window->set_cursor(Gdk::Cursor::create(x));
3687}
3688
3689//#include "iconcontroller.h"
3690void
3691studio::WorkArea::refresh_cursor()
3692{
3693//      set_cursor(IconController::get_tool_cursor(canvas_view->get_smach().get_state_name(),drawing_area->get_window()));
3694}
3695
3696void
3697studio::WorkArea::reset_cursor()
3698{
3699        //!Check if the window we want draw is ready
3700        Glib::RefPtr<Gdk::Window> draw_area_window = drawing_area->get_window();
3701        if(!draw_area_window) return;
3702       
3703        draw_area_window->set_cursor(Gdk::Cursor::create(Gdk::TOP_LEFT_ARROW));
3704//      set_cursor(Gdk::TOP_LEFT_ARROW);
3705}
3706
3707void
3708studio::WorkArea::set_zoom(float z)
3709{
3710        z=max(1.0f/128.0f,min(128.0f,z));
3711        if(z==zoom)
3712                return;
3713        zoom = z;
3714        refresh_dimension_info();
3715        /*if(async_renderer)
3716        {
3717                async_renderer->stop();
3718                async_renderer=0;
3719        }*/
3720        refreshes+=5;
3721        async_update_preview();
3722        //queue_render_preview();
3723        // TODO: FIXME: QuickHack
3724        if (canvas_view->get_smach().get_state_name() != std::string("polygon")
3725         && canvas_view->get_smach().get_state_name() != std::string("bline"))
3726                        canvas_view->queue_rebuild_ducks();
3727}
3728
3729void
3730WorkArea::set_selected_value_node(etl::loose_handle<synfig::ValueNode> x)
3731{
3732        if(x!=selected_value_node_)
3733        {
3734                selected_value_node_=x;
3735                queue_draw();
3736        }
3737}
3738
3739void
3740WorkArea::insert_renderer(const etl::handle<WorkAreaRenderer> &x)
3741{
3742        renderer_set_.insert(x);
3743        x->set_work_area(this);
3744        queue_draw();
3745}
3746
3747void
3748WorkArea::insert_renderer(const etl::handle<WorkAreaRenderer> &x, int priority)
3749{
3750        x->set_priority(priority);
3751        insert_renderer(x);
3752}
3753
3754void
3755WorkArea::erase_renderer(const etl::handle<WorkAreaRenderer> &x)
3756{
3757        x->set_work_area(0);
3758        renderer_set_.erase(x);
3759        queue_draw();
3760}
3761
3762void
3763WorkArea::resort_render_set()
3764{
3765        std::set<etl::handle<WorkAreaRenderer> > tmp(
3766                renderer_set_.begin(),
3767                renderer_set_.end()
3768        );
3769        renderer_set_.swap(tmp);
3770        queue_draw();
3771}
3772
3773WorkArea::PushState::PushState(WorkArea *workarea_):
3774        workarea_(workarea_)
3775{
3776        type_mask=workarea_->get_type_mask();
3777        allow_duck_clicks=workarea_->get_allow_duck_clicks();
3778        allow_bezier_clicks=workarea_->get_allow_bezier_clicks();
3779        allow_layer_clicks=workarea_->get_allow_layer_clicks();
3780        needs_restore=true;
3781}
3782
3783WorkArea::PushState::~PushState()
3784{
3785        if(needs_restore)
3786                restore();
3787}
3788
3789void
3790WorkArea::PushState::restore()
3791{
3792        workarea_->set_type_mask(type_mask);
3793        // update the toggle buttons for the duck types
3794        workarea_->get_canvas_view()->toggle_duck_mask(Duck::TYPE_NONE);
3795        workarea_->set_allow_duck_clicks(allow_duck_clicks);
3796        workarea_->set_allow_bezier_clicks(allow_bezier_clicks);
3797        workarea_->set_allow_layer_clicks(allow_layer_clicks);
3798        needs_restore=false;
3799}
Note: See TracBrowser for help on using the repository browser.