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