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