Fix bug 3060560: Link two tangents and one is animated, the animated is lost.
[synfig.git] / synfig-studio / src / gui / preview.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file preview.cpp
3 **      \brief Preview implementation file
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "preview.h"
34 #include "app.h"
35 #include "audiocontainer.h"
36 #include <gtkmm/stock.h>
37 #include <gtkmm/separator.h>
38
39 #include <synfig/target_scanline.h>
40 #include <synfig/surface.h>
41
42 #include <algorithm>
43 #include "asyncrenderer.h"
44
45 #include "general.h"
46
47 #endif
48
49 /* === U S I N G =========================================================== */
50
51 using namespace std;
52 using namespace etl;
53 using namespace synfig;
54 using namespace studio;
55
56 /* === M A C R O S ========================================================= */
57
58 /* === G L O B A L S ======================================================= */
59
60 /* === P R O C E D U R E S ================================================= */
61
62 /* === M E T H O D S ======================================================= */
63
64 /* === E N T R Y P O I N T ================================================= */
65
66 class studio::Preview::Preview_Target : public Target_Scanline
67 {
68         Surface surface;
69
70         sigc::signal<void, const Preview_Target *>              signal_frame_done_;
71
72         int scanline;
73
74         double  tbegin,tend;
75
76         int             nframes,curframe;
77
78 public:
79
80         Preview_Target()
81         {
82                 set_remove_alpha();
83                 tbegin = tend = 0;
84                 scanline = 0;
85                 nframes = curframe = 0;
86         }
87
88         const RendDesc &get_rend_desc() const { return desc; }
89
90         virtual bool set_rend_desc(RendDesc *r)
91         {
92                 if(Target_Scanline::set_rend_desc(r))
93                 {
94                         /*synfig::warning("Succeeded in setting the desc to new one: %d x %d, %.2f fps [%.2f,%.2f]",
95                                                         desc.get_w(),desc.get_h(),desc.get_frame_rate(),
96                                         (float)desc.get_time_start(),(float)desc.get_time_end());*/
97
98                         surface.set_wh(desc.get_w(),desc.get_h());
99
100                         curframe = 0;
101                         nframes = (int)floor((desc.get_time_end() - desc.get_time_start())*desc.get_frame_rate());
102
103                         tbegin = desc.get_time_start();
104                         tend = tbegin + nframes/desc.get_frame_rate();
105
106                         return true;
107                 }
108                 return false;
109         }
110
111         virtual bool start_frame(ProgressCallback */*cb*/=NULL)
112         {
113                 return true;
114         }
115
116         virtual void end_frame()
117         {
118                 //ok... notify our subscribers...
119                 signal_frame_done_(this);
120                 curframe += 1;
121                 //synfig::warning("Finished the frame stuff, and changed time to %.3f",t);
122         }
123
124         virtual Color * start_scanline(int scanline)
125         {
126                 return surface[scanline];
127         }
128
129         virtual bool end_scanline() {return true;}
130
131         sigc::signal<void, const Preview_Target *>      &signal_frame_done() {return signal_frame_done_;}
132
133         const Surface &get_surface() const {return surface;}
134
135         float get_time() const
136         {
137                 double time = ((nframes-curframe)/(double)nframes)*tbegin
138                                         + ((curframe)/(double)nframes)*tend;
139                 return time;
140         }
141 };
142
143 studio::Preview::Preview(const studio::CanvasView::LooseHandle &h, float zoom, float f)
144 :canvasview(h),zoom(zoom),fps(f)
145 {
146         overbegin = false;
147         overend = false;
148 }
149
150 void studio::Preview::set_canvasview(const studio::CanvasView::LooseHandle &h)
151 {
152         canvasview = h;
153
154         if(canvasview)
155         {
156                 //perhaps reset override values...
157                 const RendDesc &r = canvasview->get_canvas()->rend_desc();
158                 if(r.get_frame_rate())
159                 {
160                         float rate = 1/r.get_frame_rate();
161                         overbegin = false; begintime = r.get_time_start() + r.get_frame_start()*rate;
162                         overend = false; endtime = r.get_time_start() + r.get_frame_end()*rate;
163                 }
164         }
165 }
166
167 studio::Preview::~Preview()
168 {
169         signal_destroyed_(this); //tell anything that attached to us, we're dying
170 }
171
172 void studio::Preview::render()
173 {
174         if(canvasview)
175         {
176                 //render using the preview target
177                 etl::handle<Preview_Target>     target = new Preview_Target;
178
179                 //connect our information to his...
180                 //synfig::warning("Connecting to the end frame function...");
181                 target->signal_frame_done().connect(sigc::mem_fun(*this,&Preview::frame_finish));
182
183                 //set the options
184                 //synfig::warning("Setting Canvas");
185                 target->set_canvas(get_canvas());
186                 target->set_quality(quality);
187
188                 //render description
189                 RendDesc desc = get_canvas()->rend_desc();
190
191                 //set the global fps of the preview
192                 set_global_fps(desc.get_frame_rate());
193
194                 desc.clear_flags();
195
196                 int neww = (int)floor(desc.get_w()*zoom+0.5),
197                         newh = (int)floor(desc.get_h()*zoom+0.5);
198                 float newfps = fps;
199
200                 /*synfig::warning("Setting the render description: %d x %d, %f fps, [%f,%f]",
201                                                 neww,newh,newfps, overbegin?begintime:(float)desc.get_time_start(),
202                                                 overend?endtime:(float)desc.get_time_end());*/
203
204                 desc.set_w(neww);
205                 desc.set_h(newh);
206                 desc.set_frame_rate(newfps);
207
208                 if(overbegin)
209                 {
210                         desc.set_time_start(std::max(begintime,(float)desc.get_time_start()));
211                         //synfig::warning("Set start time to %.2f...",(float)desc.get_time_start());
212                 }
213                 if(overend)
214                 {
215                         desc.set_time_end(std::min(endtime,(float)desc.get_time_end()));
216                         //synfig::warning("Set end time to %.2f...",(float)desc.get_time_end());
217                 }
218
219                 //setting the description
220
221                 //HACK - add on one extra frame because the renderer can't render the last frame
222                 desc.set_time_end(desc.get_time_end() + 1.000001/fps);
223
224                 target->set_rend_desc(&desc);
225
226                 //... first we must clear our current selves of space
227                 frames.resize(0);
228
229                 //now tell it to go... with inherited prog. reporting...
230                 //synfig::info("Rendering Asynchronously...");
231                 if(renderer) renderer->stop();
232                 renderer = new AsyncRenderer(target);
233                 renderer->start();
234         }
235 }
236
237 static void free_guint8(const guint8 *mem)
238 {
239         free((void*)mem);
240 }
241
242 void studio::Preview::frame_finish(const Preview_Target *targ)
243 {
244         //copy image with time to next frame (can just push back)
245         FlipbookElem    fe;
246         float time = targ->get_time();
247         const Surface &surf = targ->get_surface();
248         const RendDesc& r = targ->get_rend_desc();
249
250         //synfig::warning("Finished a frame at %f s",time);
251
252         //copy EVERYTHING!
253         PixelFormat pf(PF_RGB);
254         const int total_bytes(r.get_w()*r.get_h()*synfig::channels(pf));
255
256         //synfig::warning("Creating a buffer");
257         unsigned char *buffer((unsigned char*)malloc(total_bytes));
258
259         if(!buffer)
260                 return;
261
262         //convert all the pixels to the pixbuf... buffer... thing...
263         //synfig::warning("Converting...");
264         convert_color_format(buffer, surf[0], surf.get_w()*surf.get_h(), pf, App::gamma);
265
266         //load time
267         fe.t = time;
268         //uses and manages the memory for the buffer...
269         //synfig::warning("Create a pixmap...");
270         fe.buf =
271         Gdk::Pixbuf::create_from_data(
272                 buffer, // pointer to the data
273                 Gdk::COLORSPACE_RGB, // the colorspace
274                 ((pf&PF_A)==PF_A), // has alpha?
275                 8, // bits per sample
276                 surf.get_w(),   // width
277                 surf.get_h(),   // height
278                 surf.get_w()*synfig::channels(pf), // stride (pitch)
279                 sigc::ptr_fun(free_guint8)
280         );
281
282         //add the flipbook element to the list (assume time is correct)
283         //synfig::info("Prev: Adding %f s to the list", time);
284         frames.push_back(fe);
285
286         signal_changed()();
287 }
288
289 #define IMAGIFY_BUTTON(button,stockid,tooltip)                                  \
290         icon=manage(new Gtk::Image(Gtk::StockID(stockid),Gtk::ICON_SIZE_BUTTON));       \
291         button->add(*icon);     \
292         tooltips.set_tip(*button,tooltip);      \
293         icon->set_padding(0,0);\
294         icon->show();
295
296 Widget_Preview::Widget_Preview()
297 :Gtk::Table(5,5,false),
298 adj_time_scrub(0,0,1000,1,10,0),
299 scr_time_scrub(adj_time_scrub),
300 b_loop(/*_("Loop")*/),
301 currentindex(0),
302 audiotime(0),
303 adj_sound(0,0,4),
304 l_lasttime("0s"),
305 playing(false)
306 {
307         //connect to expose events
308         //signal_expose_event().connect(sigc::mem_fun(*this, &studio::Widget_Preview::redraw));
309
310         //manage all the change in values etc...
311         adj_time_scrub.signal_value_changed().connect(sigc::mem_fun(*this,&Widget_Preview::slider_move));
312         scr_time_scrub.signal_event().connect(sigc::mem_fun(*this,&Widget_Preview::scroll_move_event));
313         draw_area.signal_expose_event().connect(sigc::mem_fun(*this,&Widget_Preview::redraw));
314
315         disp_sound.set_time_adjustment(&adj_sound);
316         timedisp = -1;
317
318         //Set up signals to modify time value as it should be...
319         disp_sound.signal_start_scrubbing().connect(sigc::mem_fun(*this,&Widget_Preview::scrub_updated));
320         disp_sound.signal_scrub().connect(sigc::mem_fun(*this,&Widget_Preview::scrub_updated));
321
322         /*
323         ---------------------------------
324         |                                                               |
325         |                                                               |
326         |                                                               |
327         |                                                               |
328         |                                                               |
329         |                                                               |
330         |                                                               |
331         ---------------------------------
332         |loop|play|stop                                 | hbox
333         |lastl|lastt|rerender|haltrend  | hbox
334         |
335         |sound                                                  |
336         */
337
338         Gtk::HBox *hbox = 0;
339         Gtk::Button *button = 0;
340         Gtk::Image *icon = 0;
341
342         //should set up the dialog using attach etc.
343         attach(draw_area, 0, 1, 0, 1);
344         attach(scr_time_scrub, 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK);
345
346         #if 1
347
348         //2nd row
349         hbox = manage(new Gtk::HBox);
350
351         button = &b_loop;
352         IMAGIFY_BUTTON(button,Gtk::Stock::REFRESH,_("Toggle Looping"));
353         hbox->pack_start(b_loop,Gtk::PACK_SHRINK,0);
354         //attach(b_loop,0,1,2,3,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
355
356         button = manage(new Gtk::Button(/*_("Play")*/));
357         button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::play));
358         IMAGIFY_BUTTON(button,Gtk::Stock::GO_FORWARD,_("Play"));
359         hbox->pack_start(*button,Gtk::PACK_SHRINK,0);
360         //attach(*button,1,2,2,3,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
361
362         button = manage(new Gtk::Button(/*_("Stop")*/));
363         button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::stop));
364         IMAGIFY_BUTTON(button,Gtk::Stock::NO,_("Stop"));
365         hbox->pack_start(*button,Gtk::PACK_SHRINK,0);
366         //attach(*button,2,3,2,3,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
367
368         //attack the stop render and erase all buttons to same line...
369         {
370                 Gtk::VSeparator *vsep = manage(new Gtk::VSeparator);
371                 hbox->pack_start(*vsep,Gtk::PACK_SHRINK,0);
372         }
373
374         button = manage(new Gtk::Button(/*_("Halt Render")*/));
375         button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::stoprender));
376         IMAGIFY_BUTTON(button,Gtk::Stock::STOP,_("Halt Render"));
377         hbox->pack_start(*button,Gtk::PACK_SHRINK,0);
378         //attach(*button,2,3,3,4,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
379
380         button = manage(new Gtk::Button(/*_("Re-Preview")*/));
381         button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::repreview));
382         IMAGIFY_BUTTON(button,Gtk::Stock::CONVERT,_("Re-Preview"));
383         hbox->pack_start(*button,Gtk::PACK_SHRINK,0);
384         //attach(*button,0,2,4,5,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
385
386         button = manage(new Gtk::Button(/*_("Erase All")*/));
387         button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::eraseall));
388         IMAGIFY_BUTTON(button,Gtk::Stock::DELETE,_("Erase All"));
389         hbox->pack_start(*button,Gtk::PACK_SHRINK,0);
390         //attach(*button,2,3,4,5,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
391
392         hbox->show_all();
393         attach(*hbox,0,1,2,3,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK|Gtk::FILL);
394
395         //3rd row
396         hbox = manage(new Gtk::HBox);
397         {
398                 Gtk::Label *label = manage(new Gtk::Label(_("Last Rendered: ")));
399                 //label->show();
400                 hbox->pack_start(*label,Gtk::PACK_SHRINK,10);
401                 //attach(*manage(new Gtk::Label(_("Last Rendered: "))),0,1,3,4,Gtk::SHRINK,Gtk::SHRINK);
402         }
403         //l_lasttime.show();
404         hbox->pack_start(l_lasttime,Gtk::PACK_SHRINK,0);
405         hbox->show_all();
406         attach(*hbox,0,1,3,4,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
407         //attach(l_lasttime,0,1,3,4,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
408
409         //5th row
410         disp_sound.set_size_request(-1,32);
411         attach(disp_sound,0,1,4,5,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK);
412
413         show_all();
414
415         //if(draw_area.get_window()) gc_area = Gdk::GC::create(draw_area.get_window());
416         #endif
417 }
418
419 studio::Widget_Preview::~Widget_Preview()
420 {
421 }
422
423 void studio::Widget_Preview::update()
424 {
425         //the meat goes in this locker...
426         double time = adj_time_scrub.get_value();
427
428         //find the frame and display it...
429         if(preview)
430         {
431                 //synfig::warning("Updating at %.3f s",time);
432
433                 //use time to find closest frame...
434                 studio::Preview::FlipBook::const_iterator       beg = preview->begin(),end = preview->end();
435                 studio::Preview::FlipBook::const_iterator       i;
436
437                 i = beg;
438
439                 //go to current hint if need be...
440                 if(currentindex >= 0 && currentindex < (int)preview->numframes())
441                 {
442                         i = beg+currentindex;
443                 }
444
445                 //we can't have a picture if there are none to get
446                 if(beg != end)
447                 {
448                         //don't bother with binary search it will just be slower...
449
450                         //synfig::info("Search for time %f",time);
451
452                         //incrementally go in either direction
453                         //(bias downward towards beg, because that's what we want)
454                         for(;i != end;++i)
455                         {
456                                 //synfig::info("Look at %f",i->t);
457                                 if(i->t > time) break;
458                                 //synfig::info("Go past...");
459                         }
460
461                         //if(i!=beg)--i;
462
463                         //bias down, so we can't be at end... and it still is valid...
464                         for(;i != beg;)
465                         {
466                                 --i;
467                                 //synfig::info("Look at %f",i->t);
468                                 if(i->t <= time) break;
469                                 //synfig::info("Go past...");
470                         }
471
472                         /*i = preview->begin(); end = preview->end();
473                         if(i == end) return;
474
475                         j = i;
476                         for(;i != end; j = i++)
477                         {
478                                 if(i->t > time) break;
479                         }*/
480
481                         //we should be at a valid edge since we biased downward
482
483                         //don't get the closest, round down... (if we can)
484                         if(i == end)
485                         {
486                                 synfig::error("i == end....");
487                                 //assert(0);
488                                 currentbuf.clear();
489                                 currentindex = 0;
490                                 timedisp = -1;
491                         }else
492                         {
493                                 currentbuf = i->buf;
494                                 currentindex = i-beg;
495                                 if(timedisp != i->t)
496                                 {
497                                         timedisp = i->t;
498                                         //synfig::warning("Update at: %f seconds (%f s)",time,timedisp);
499                                         preview_draw();
500                                         //synfig::warning("success!");
501                                 }
502                         }
503                 }
504         }
505
506         if(disp_sound.get_profile() && adj_sound.get_value() != time)
507         {
508                 //timeupdate = time;
509
510                 //Set the position of the sound (short circuited for sound modifying the time)
511
512                 disp_sound.set_position(time);
513                 disp_sound.queue_draw();
514         }
515 }
516 void studio::Widget_Preview::preview_draw()
517 {
518         draw_area.queue_draw();//on_expose_event();
519 }
520
521 bool studio::Widget_Preview::redraw(GdkEventExpose */*heh*/)
522 {
523         //And render the drawing area
524         Glib::RefPtr<Gdk::Pixbuf> pxnew, px = currentbuf;
525
526         if(!px || draw_area.get_height() == 0
527                 || px->get_height() == 0 || px->get_width() == 0 /*|| is_visible()*/) //made not need this line
528                 return true;
529
530         //figure out the scaling factors...
531         float sx, sy;
532         int nw,nh;
533
534         sx = draw_area.get_width() / (float)px->get_width();
535         sy = draw_area.get_height() / (float)px->get_height();
536
537         //synfig::info("widget_preview redraw: now to scale the bitmap: %.3f x %.3f",sx,sy);
538
539         //round to smallest scale (fit entire thing in window without distortion)
540         if(sx > sy) sx = sy;
541         //else sy = sx;
542
543         //scale to a new pixmap and then copy over to the window
544         nw = (int)(px->get_width()*sx);
545         nh = (int)(px->get_height()*sx);
546
547         if(nw == 0 || nh == 0)return true;
548
549         pxnew = px->scale_simple(nw,nh,Gdk::INTERP_NEAREST);
550
551         //synfig::info("Now to draw to the window...");
552         //copy to window
553         Glib::RefPtr<Gdk::Window>       wind = draw_area.get_window();
554         Glib::RefPtr<Gdk::Drawable> surf = Glib::RefPtr<Gdk::Drawable>::cast_static(wind);
555         Glib::RefPtr<Gdk::GC>           gc = Gdk::GC::create(wind);
556
557         {
558                 Gdk::Rectangle r(0,0,draw_area.get_width(),draw_area.get_height());
559                 draw_area.get_window()->begin_paint_rect(r);
560         }
561
562         if(!wind) synfig::warning("The destination window is broken...");
563         if(!surf) synfig::warning("The destination is not drawable...");
564
565         if(surf)
566         {
567                 /* Options for drawing...
568                         1) store with alpha, then clear and render with alpha every frame
569                                 - more time consuming
570                                 + more expandable
571                         2) store with just pixel info
572                                 - less expandable
573                                 + faster
574                                 + better memory footprint
575                 */
576                 //px->composite(const Glib::RefPtr<Gdk::Pixbuf>& dest, int dest_x, int dest_y, int dest_width, int dest_height, double offset_x, double offset_y, double scale_x, double scale_y, InterpType interp_type, int overall_alpha) const
577
578                 surf->draw_pixbuf(
579                         gc, //GC
580                         pxnew, //pixbuf
581                         0, 0,   // Source X and Y
582                         0, 0,   // Dest X and Y
583                         -1,-1,  // Width and Height
584                         Gdk::RGB_DITHER_NONE, // RgbDither
585                         0, 0 // Dither offset X and Y
586                 );
587
588                 if(timedisp >= 0)
589                 {
590                         Glib::RefPtr<Pango::Layout> layout(Pango::Layout::create(get_pango_context()));
591                         Glib::ustring timecode(Time((double)timedisp).round(preview->get_global_fps())
592                                                                                                                         .get_string(preview->get_global_fps(),
593                                                                                                                                                         App::get_time_format()));
594                         //synfig::info("Time for preview draw is: %s for time %g", timecode.c_str(), adj_time_scrub.get_value());
595
596                         gc->set_rgb_fg_color(Gdk::Color("#FF0000"));
597                         layout->set_text(timecode);
598                         surf->draw_layout(gc,4,4,layout);
599                 }
600         }
601
602         draw_area.get_window()->end_paint();
603
604         //synfig::warning("Refresh the draw area");
605         //make sure the widget refreshes
606
607         return false;
608 }
609
610 bool studio::Widget_Preview::play_update()
611 {
612         float diff = timer.pop_time();
613         //synfig::info("Play update: diff = %.2f",diff);
614
615         if(playing)
616         {
617                 //we go to the next one...
618                 double time = adj_time_scrub.get_value() + diff;
619
620                 //adjust it to be synced with the audio if it can...
621                 {
622                         double newtime = audiotime;
623                         if(audio && audio->is_playing()) audio->get_current_time(newtime);
624
625                         if(newtime != audiotime)
626                         {
627                                 //synfig::info("Adjusted time from %.3lf to %.3lf", time,newtime);
628                                 time = audiotime = newtime;
629                         }
630                 }
631
632                 //Looping conditions...
633                 if(time >= adj_time_scrub.get_upper())
634                 {
635                         if(get_loop_flag())
636                         {
637                                 time = adj_time_scrub.get_lower();// + time-adj_time_scrub.get_upper();
638                                 currentindex = 0;
639                         }else
640                         {
641                                 time = adj_time_scrub.get_upper();
642                                 adj_time_scrub.set_value(time);
643                                 play_stop();
644                                 update();
645
646                                 //synfig::info("Play Stopped: time set to %f",adj_time_scrub.get_value());
647                                 return false;
648                         }
649                 }
650
651                 //set the new time...
652                 adj_time_scrub.set_value(time);
653                 adj_time_scrub.value_changed();
654
655                 //update the window to the correct image we might want to do this later...
656                 //update();
657                 //synfig::warning("Did update pu");
658         }
659         return true;
660 }
661
662 void studio::Widget_Preview::slider_move()
663 {
664         //if(!playing)
665         {
666                 update();
667                 //synfig::warning("Did update sm");
668         }
669 }
670
671 //for other things updating the value changed signal...
672 void studio::Widget_Preview::scrub_updated(double t)
673 {
674         stop();
675
676         //Attempt at being more accurate... the time is adjusted to be exactly where the sound says it is
677         //double oldt = t;
678         if(audio)
679         {
680                 if(!audio->isPaused())
681                 {
682                         audio->get_current_time(t);
683                 }
684         }
685
686         //synfig::info("Scrubbing to %.3f, setting adj to %.3f",oldt,t);
687
688         if(adj_time_scrub.get_value() != t)
689         {
690                 adj_time_scrub.set_value(t);
691                 adj_time_scrub.value_changed();
692         }
693 }
694
695 void studio::Widget_Preview::disconnect_preview(Preview *prev)
696 {
697         if(prev == preview)
698         {
699                 preview = 0;
700                 prevchanged.disconnect();
701         }
702 }
703
704 void studio::Widget_Preview::set_preview(etl::handle<Preview>   prev)
705 {
706         preview = prev;
707
708         synfig::info("Setting preview");
709
710         //stop playing the mini animation...
711         stop();
712
713         if(preview)
714         {
715                 //set the internal values
716                 float rate = preview->get_fps();
717                 synfig::info("  FPS = %f",rate);
718                 if(rate)
719                 {
720                         float start = preview->get_begintime();
721                         float end = preview->get_endtime();
722
723                         rate = 1/rate;
724
725                         adj_time_scrub.set_lower(start);
726                         adj_time_scrub.set_upper(end);
727                         adj_time_scrub.set_value(start);
728                         adj_time_scrub.set_step_increment(rate);
729                         adj_time_scrub.set_page_increment(10*rate);
730
731                         //if the begin time and the end time are the same there is only a single frame
732                         singleframe = end==start;
733                 }else
734                 {
735                         adj_time_scrub.set_lower(0);
736                         adj_time_scrub.set_upper(0);
737                         adj_time_scrub.set_value(0);
738                         adj_time_scrub.set_step_increment(0);
739                         adj_time_scrub.set_page_increment(0);
740                         singleframe = true;
741                 }
742
743                 //connect so future information will be found...
744                 prevchanged = prev->signal_changed().connect(sigc::mem_fun(*this,&Widget_Preview::whenupdated));
745                 prev->signal_destroyed().connect(sigc::mem_fun(*this,&Widget_Preview::disconnect_preview));
746                 update();
747                 //synfig::warning("Did update sp");
748                 queue_draw();
749         }
750 }
751
752 void studio::Widget_Preview::whenupdated()
753 {
754         l_lasttime.set_text((Time((double)(--preview->end())->t)
755                                                         .round(preview->get_global_fps())
756                                                         .get_string(preview->get_global_fps(),App::get_time_format())));
757         update();
758 }
759
760 void studio::Widget_Preview::clear()
761 {
762         preview = 0;
763         prevchanged.disconnect();
764 }
765
766 void studio::Widget_Preview::play()
767 {
768         if(preview && !playing)
769         {
770                 //synfig::info("Playing at %lf",adj_time_scrub.get_value());
771                 //audiotime = adj_time_scrub.get_value();
772                 playing = true;
773
774                 //adj_time_scrub.set_value(adj_time_scrub.get_lower());
775                 update(); //we don't want to call play update because that will try to advance the timer
776                 //synfig::warning("Did update p");
777
778                 //approximate length of time in seconds, right?
779                 double rate = /*std::min(*/adj_time_scrub.get_step_increment()/*,1/30.0)*/;
780                 int timeout = (int)floor(1000*rate);
781
782                 //synfig::info("        rate = %.3lfs = %d ms",rate,timeout);
783
784                 signal_play_(adj_time_scrub.get_value());
785
786                 //play the audio...
787                 if(audio) audio->play(adj_time_scrub.get_value());
788
789                 timecon = Glib::signal_timeout().connect(sigc::mem_fun(*this,&Widget_Preview::play_update),timeout);
790                 timer.reset();
791         }
792
793 }
794
795 void studio::Widget_Preview::play_stop()
796 {
797         playing = false;
798         signal_stop()();
799         if(audio) audio->stop(); //!< stop the audio
800         //synfig::info("Stopping...");
801 }
802
803 void studio::Widget_Preview::stop()
804 {
805         //synfig::warning("stopping");
806         play_stop();
807         timecon.disconnect();
808 }
809
810 bool studio::Widget_Preview::scroll_move_event(GdkEvent *event)
811 {
812         switch(event->type)
813         {
814                 case GDK_BUTTON_PRESS:
815                 {
816                         if(event->button.button == 1 || event->button.button == 3)
817                         {
818                                 stop();
819                         }
820                 }
821
822                 default: break;
823         }
824
825         return false;
826 }
827
828 void studio::Widget_Preview::set_audioprofile(etl::handle<AudioProfile> p)
829 {
830         disp_sound.set_profile(p);
831 }
832
833 void studio::Widget_Preview::set_audio(etl::handle<AudioContainer> a)
834 {
835         audio = a;
836
837         //disconnect any previous signals
838         scrstartcon.disconnect(); scrstopcon.disconnect(); scrubcon.disconnect();
839
840         //connect the new signals
841         scrstartcon = disp_sound.signal_start_scrubbing().connect(sigc::mem_fun(*a,&AudioContainer::start_scrubbing));
842         scrstopcon = disp_sound.signal_stop_scrubbing().connect(sigc::mem_fun(*a,&AudioContainer::stop_scrubbing));
843         scrubcon = disp_sound.signal_scrub().connect(sigc::mem_fun(*a,&AudioContainer::scrub));
844 }
845
846 void studio::Widget_Preview::seek(float t)
847 {
848         stop();
849         adj_time_scrub.set_value(t);
850 }
851
852 void studio::Widget_Preview::repreview()
853 {
854         if(preview)
855         {
856                 stoprender();
857                 stop();
858                 preview->get_canvasview()->preview_option();
859         }
860 }
861
862 void studio::Widget_Preview::stoprender()
863 {
864         if(preview)
865         {
866                 // don't crash if the render has already been stopped
867                 if (!preview->renderer)
868                         return;
869
870 #ifdef SINGLE_THREADED
871                 if (preview->renderer->updating)
872                         preview->renderer->stop();
873                 else
874 #endif
875                         preview->renderer.detach();
876         }
877 }
878
879 void studio::Widget_Preview::eraseall()
880 {
881         stop();
882         stoprender();
883
884         currentbuf.clear();
885         currentindex = 0;
886         timedisp = 0;
887         queue_draw();
888
889         if(preview)
890         {
891                 preview->clear();
892         }
893 }