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