minor fix
[synfig.git] / synfig-studio / trunk / src / gtkmm / widget_timeslider.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file widget_timeslider.cpp
3 **      \brief Time Slider Widget Implementation File
4 **
5 **      $Id: widget_timeslider.cpp,v 1.1.1.1 2005/01/07 03:34:37 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2004 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 "widget_timeslider.h"
33
34 #include <ETL/misc>
35
36 #include <cmath>
37
38 #endif
39
40 /* === U S I N G =========================================================== */
41
42 using namespace std;
43 using namespace etl;
44 using namespace synfig;
45
46 using studio::Widget_Timeslider;
47
48 /* === M A C R O S ========================================================= */
49
50 /* === G L O B A L S ======================================================= */
51 const double zoominfactor = 0.75;
52 const double zoomoutfactor = 1/zoominfactor;
53
54 /* === P R O C E D U R E S ================================================= */
55
56 Gdk::Color get_interp_color(synfig::Interpolation x)
57 {
58         switch(x)
59         {
60         case INTERPOLATION_TCB:
61                 return Gdk::Color("#00B000");
62
63                 break;
64
65         case INTERPOLATION_LINEAR:
66                 return Gdk::Color("#B0B000");
67                 break;
68
69         case INTERPOLATION_CONSTANT:
70                 return Gdk::Color("#C70000");
71                 break;
72
73         case INTERPOLATION_HALT:
74                 return Gdk::Color("#00b0b0");
75                 break;
76
77         case INTERPOLATION_MANUAL:
78                 return Gdk::Color("#B000B0");
79                 break;
80
81         case INTERPOLATION_UNDEFINED: default:
82                 return Gdk::Color("#808080");
83                 break;
84         }
85 }
86
87 static Gdk::Color
88 color_darken(Gdk::Color x, float amount)
89 {
90         x.set_rgb_p(
91                 x.get_red_p()*amount,
92                 x.get_green_p()*amount,
93                 x.get_blue_p()*amount
94         );
95         return x;
96 }
97
98 void
99 studio::render_time_point_to_window(
100         const Glib::RefPtr<Gdk::Drawable>& window,
101         const Gdk::Rectangle& area,
102         const synfig::TimePoint &tp,
103         bool selected
104 )
105 {
106         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
107         const Gdk::Color black("#000000");
108         
109         if(selected)
110                 gc->set_line_attributes(2,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
111         else
112                 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
113
114         Gdk::Color color;
115         std::vector<Gdk::Point> points;
116         
117 /*-     BEFORE ------------------------------------- */
118
119         color=get_interp_color(tp.get_before());
120         color=color_darken(color,1.0f);
121         if(selected)color=color_darken(color,1.3f);
122         gc->set_rgb_fg_color(color);
123
124         switch(tp.get_before())
125         {
126         case INTERPOLATION_TCB:
127                 window->draw_arc(
128                         gc,
129                         true,
130                         area.get_x(),
131                         area.get_y(),
132                         area.get_width(),
133                         area.get_height(),
134                         64*90,
135                         64*180
136                 );
137                 gc->set_rgb_fg_color(black);
138                 window->draw_arc(
139                         gc,
140                         false,
141                         area.get_x(),
142                         area.get_y(),
143                         area.get_width(),
144                         area.get_height(),
145                         64*90,
146                         64*180
147                 );
148                 break;
149
150         case INTERPOLATION_HALT:
151                 window->draw_arc(
152                         gc,
153                         true,
154                         area.get_x(),
155                         area.get_y(),
156                         area.get_width(),
157                         area.get_height()*2,
158                         64*90,
159                         64*90
160                 );
161                 gc->set_rgb_fg_color(black);
162                 window->draw_arc(
163                         gc,
164                         false,
165                         area.get_x(),
166                         area.get_y(),
167                         area.get_width(),
168                         area.get_height()*2,
169                         64*90,
170                         64*90
171                 );
172                 break;
173
174         case INTERPOLATION_LINEAR:
175                 points.clear();
176                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
177                 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()));
178                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
179                 window->draw_polygon(gc,true,points);
180                 gc->set_rgb_fg_color(black);
181                 window->draw_lines(gc,points);  
182                 break;
183
184         case INTERPOLATION_CONSTANT:
185                 points.clear();
186                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
187                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/4,area.get_y()));
188                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/4,area.get_y()+area.get_height()/2));
189                 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()/2));
190                 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()));
191                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
192                 window->draw_polygon(gc,true,points);
193                 gc->set_rgb_fg_color(black);
194                 window->draw_lines(gc,points);  
195                 break;
196
197         case INTERPOLATION_UNDEFINED: default:
198                 points.clear();
199                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
200                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/3,area.get_y()));
201                 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()/3));
202                 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()*2/3));
203                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/3,area.get_y()+area.get_height()));
204                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
205                 window->draw_polygon(gc,true,points);
206                 gc->set_rgb_fg_color(black);
207                 window->draw_lines(gc,points);  
208                 break;
209         }
210         
211 /*-     AFTER -------------------------------------- */
212
213         color=get_interp_color(tp.get_after());
214         color=color_darken(color,0.8f);
215         if(selected)color=color_darken(color,1.3f);
216         gc->set_rgb_fg_color(color);
217
218
219         switch(tp.get_after())
220         {
221         case INTERPOLATION_TCB:
222                 window->draw_arc(
223                         gc,
224                         true,
225                         area.get_x(),
226                         area.get_y(),
227                         area.get_width(),
228                         area.get_height(),
229                         64*270,
230                         64*180
231                 );
232                 gc->set_rgb_fg_color(black);
233                 window->draw_arc(
234                         gc,
235                         false,
236                         area.get_x(),
237                         area.get_y(),
238                         area.get_width(),
239                         area.get_height(),
240                         64*270,
241                         64*180
242                 );
243                 break;
244
245         case INTERPOLATION_HALT:
246                 window->draw_arc(
247                         gc,
248                         true,
249                         area.get_x(),
250                         area.get_y()-area.get_height(),
251                         area.get_width(),
252                         area.get_height()*2,
253                         64*270,
254                         64*90
255                 );
256                 gc->set_rgb_fg_color(black);
257                 window->draw_arc(
258                         gc,
259                         false,
260                         area.get_x(),
261                         area.get_y()-area.get_height(),
262                         area.get_width(),
263                         area.get_height()*2,
264                         64*270,
265                         64*90
266                 );
267                 break;
268
269         case INTERPOLATION_LINEAR:
270                 points.clear();
271                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
272                 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()));
273                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
274                 window->draw_polygon(gc,true,points);
275                 gc->set_rgb_fg_color(black);
276                 window->draw_lines(gc,points);  
277                 break;
278
279         case INTERPOLATION_CONSTANT:
280                 points.clear();
281                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
282                 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()));
283                 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()/2));
284                 points.push_back(Gdk::Point(area.get_x()+area.get_width()*3/4,area.get_y()+area.get_height()/2));
285                 points.push_back(Gdk::Point(area.get_x()+area.get_width()*3/4,area.get_y()+area.get_height()));
286                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
287                 window->draw_polygon(gc,true,points);
288                 gc->set_rgb_fg_color(black);
289                 window->draw_lines(gc,points);  
290                 break;
291
292         case INTERPOLATION_UNDEFINED: default:
293                 points.clear();
294                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
295                 points.push_back(Gdk::Point(area.get_x()+area.get_width()*2/3,area.get_y()));
296                 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()/3));
297                 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()*2/3));
298                 points.push_back(Gdk::Point(area.get_x()+area.get_width()*2/3,area.get_y()+area.get_height()));
299                 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
300                 window->draw_polygon(gc,true,points);
301                 gc->set_rgb_fg_color(black);
302                 window->draw_lines(gc,points);  
303                 break;
304         }
305
306 }
307
308 /* === M E T H O D S ======================================================= */
309
310 /* === E N T R Y P O I N T ================================================= */
311 double defaultfps = 0;
312 const int fullheight = 20;
313
314 Widget_Timeslider::Widget_Timeslider()
315 :layout(Pango::Layout::create(get_pango_context())),
316 adj_default(0,0,2,1/defaultfps,10/defaultfps),
317 adj_timescale(0),
318 //invalidated(false),
319 fps(defaultfps),
320 dragscroll(false)
321 {
322         set_size_request(-1,fullheight);
323         
324         //                click                    scroll                     zoom
325         add_events( Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK 
326                                 | Gdk::BUTTON_MOTION_MASK | Gdk::SCROLL_MASK );
327         
328         set_time_adjustment(&adj_default);
329         //update_times();
330 }
331
332 Widget_Timeslider::~Widget_Timeslider()
333 {
334 }
335
336 void Widget_Timeslider::set_time_adjustment(Gtk::Adjustment *x)
337 {
338         //disconnect old connections
339         time_value_change.disconnect();
340         time_other_change.disconnect();
341         
342         //connect update function to new adjustment
343         adj_timescale = x;
344         
345         if(x)
346         {
347                 time_value_change = x->signal_value_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw));
348                 time_other_change = x->signal_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw));   
349                 //invalidated = true;
350                 //refresh();
351         }
352 }
353
354 void Widget_Timeslider::set_global_fps(float d)
355 {
356         if(fps != d)
357         {
358                 fps = d;
359                 
360                 //update everything since we need to redraw already
361                 //invalidated = true;
362                 //refresh();
363                 queue_draw();
364         }
365 }
366
367 /*void Widget_Timeslider::update_times()
368 {
369         if(adj_timescale)
370         {
371                 start = adj_timescale->get_lower();
372                 end = adj_timescale->get_upper();
373                 current = adj_timescale->get_value();
374         }
375 }*/
376
377 void Widget_Timeslider::refresh() 
378 {
379 }
380 /*      
381 {
382         if(invalidated)
383         {
384                 queue_draw();
385         }else if(adj_timescale)
386         {
387                 double  l = adj_timescale->get_lower(),
388                                 u = adj_timescale->get_upper(),
389                                 v = adj_timescale->get_value();
390                 
391                 bool invalid = (l != start) || (u != end) || (v != current);
392                 
393                 start = l;
394                 end = u;
395                 current = v;
396                 
397                 if(invalid) queue_draw();
398         }
399 }*/
400
401 bool Widget_Timeslider::redraw(bool doublebuffer)
402 {
403         Glib::RefPtr<Gdk::Window> window = get_window();
404         
405         if(!window) return false;
406         
407         Glib::RefPtr<Gdk::GC>   gc = Gdk::GC::create(window);   
408         if(!gc) return false;
409         
410         //synfig::info("Drawing Timeslider");
411         //clear and update to current values
412         //invalidated = false;
413         //update_times();       
414         
415         //draw grey rectangle
416         Gdk::Color      c("#7f7f7f");
417         gc->set_rgb_fg_color(c);
418         gc->set_background(c);
419         
420         //Get the data for the window and the params to draw it...
421         int w = get_width(), h = get_height();
422         
423         window->draw_rectangle(gc,true,0,0,w,h);                
424         
425         const double EPSILON = 1e-6;
426         if(!adj_timescale || w == 0) return true;
427
428         //Get the time information since we now know it's valid
429         double  start = adj_timescale->get_lower(),
430                         end = adj_timescale->get_upper(),
431                         current = adj_timescale->get_value();
432         
433         if(end-start < EPSILON) return true;
434         
435         //synfig::info("Drawing Lines");
436         
437         //draw all the time stuff
438         double dtdp = (end - start)/get_width();
439         double dpdt = 1/dtdp;
440         
441         //lines
442         
443         //Draw the time line...
444         double tpx = (current-start)*dpdt;
445         gc->set_rgb_fg_color(Gdk::Color("#ffaf00"));
446         window->draw_line(gc,round_to_int(tpx),0,round_to_int(tpx),fullheight);
447         
448         //normal line/text color
449         gc->set_rgb_fg_color(Gdk::Color("#333333"));    
450
451         //draw these lines... (always 5 between) maybe 6?
452         const int subdiv = 4;
453
454         //1h 45 30 20 10 5
455         //..., 3m, 2m, 1m30s, 1m, 30s, 20s, 10s, 5s, 3s, 2s, 1s, 0.5s
456         //frames... (how???)
457         double ranges[] = 
458         { 1.0/fps,subdiv/fps,0.25,0.5, 1, 2, 3, 5, 10, 20, 30, 60, 90, 120, 180, 300, 600, 1200, 1800, 2700, 3600 };
459         //{ 3600, 2700, 1800, 1200, 600, 300, 180, 120, 90, 60, 30, 20, 10, 5, 3, 2, 1, 0.5 };
460         const int ranges_size = sizeof(ranges)/sizeof(double);
461         
462         double lowerrange = dtdp*75, upperrange = dtdp*150;
463         double midrange = (lowerrange + upperrange)/2;
464         
465         //find most ideal scale
466         double scale = ranges[0];
467         {
468                 double *val = binary_find(ranges, ranges+ranges_size, midrange);
469                 double *after = val+1;
470                 
471                 if(val >= ranges+ranges_size)
472                 {
473                         val = ranges+ranges_size-1;
474                 }
475                         
476                 if(after >= ranges+ranges_size)
477                 {
478                         after = ranges+ranges_size-1;
479                 }
480                 
481                 scale = *val;
482                 
483                 double diff = abs(scale - midrange), diff2 = abs(*after - midrange);
484                 if(diff2 < diff)
485                         scale = *after;
486         }
487                 
488         //synfig::info("Range found: (l %.2lf,u %.2lf - m %.2lf) -> %.2lf",lowerrange,upperrange,midrange,scale);       
489         
490         //search around this area to get the right one          
491         
492         
493         //get first valid line and it's position in pixel space
494         double time = 0;
495         double pixel = 0;
496         
497         int sdindex = 0;
498
499         double subr = scale / subdiv;
500         
501         //get it's position inside...
502         time = ceil(start/subr)*subr - start;
503         pixel = time*dpdt;
504         
505         //absolute time of the line to be drawn
506         time += start;
507         
508         { //inside the big'n
509                 double t = (time/scale - floor(time/scale))*subdiv; // the difference from the big mark in 0:1
510                 //sdindex = (int)floor(t + 0.5); //get how far through the range it is...
511                 sdindex = round_to_int(t); //get how far through the range it is...
512                 
513                 //synfig::info("Extracted fr %.2lf -> %d", t, sdindex);
514         }
515         
516         //synfig::info("Initial values: %.4lf t, %.1lf pixels, %d i", time,pixel,sdindex);
517         
518         //loop to draw
519         const int heightbig = 12;
520         const int heightsmall = 4;
521         
522         int width = get_width();
523         while( pixel < width )
524         {
525                 int xpx = round_to_int(pixel);
526                 
527                 //draw big              
528                 if(sdindex == 0)
529                 {
530                         window->draw_line(gc,xpx,0,xpx,heightbig);
531                         //round the time to nearest frame and draw the text
532                         Time tm((double)time);
533                         if(get_global_fps()) tm.round(get_global_fps());
534                         Glib::ustring timecode(tm.get_string(get_global_fps(),App::get_time_format()));
535                         
536                         //gc->set_rgb_fg_color(Gdk::Color("#000000"));
537                         layout->set_text(timecode);
538                         window->draw_layout(gc,xpx+2,heightsmall,layout);
539                 }else
540                 {
541                         window->draw_line(gc,xpx,0,xpx,heightsmall);                    
542                 }
543                 
544                 //increment time and position
545                 pixel += subr / dtdp;
546                 time += subr;
547                 
548                 //increment index
549                 if(++sdindex >= subdiv) sdindex -= subdiv;
550         }
551         
552         return true;
553 }
554
555 bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for dragging
556 {       
557         if(!adj_timescale) return false;
558                 
559         Gdk::ModifierType mod = Gdk::ModifierType(event->state);
560         
561         //scrolling...
562         
563         //NOTE: we might want to address the possibility of dragging with both buttons held down
564         
565         if(mod & Gdk::BUTTON2_MASK)
566         {
567
568                 //we need this for scrolling by dragging
569                 double  curx = event->x;
570                 
571                 double  start = adj_timescale->get_lower(),
572                                 end = adj_timescale->get_upper();
573                 
574                 
575                 if(dragscroll)
576                 {
577                         if(event->time-last_event_time<30)
578                                 return false;
579                         else
580                                 last_event_time=event->time;
581
582                         if(abs(lastx - curx) < 1 && end != start) return true;
583                         //translate the window and correct it
584                         
585                         //update our stuff so we are operating correctly
586                         //invalidated = true;
587                         //update_times();
588                         
589                         //Note: Use inverse of mouse movement because of conceptual space relationship
590                         double diff = lastx - curx; //curx - lastx;
591                         
592                         //NOTE: This might be incorrect...
593                         //fraction to move...
594                         double dpx = (end - start)/get_width();
595                         lastx = curx;
596                         
597                         diff *= dpx;
598                         
599                         //Adjust...
600                         start += diff;
601                         end += diff;
602                         
603                         //But clamp to bounds if they exist...
604                         //HACK - bounds should not be required for this slider
605                         if(adj_bounds)
606                         {
607                                 if(start < adj_bounds->get_lower())
608                                 {
609                                         diff = adj_bounds->get_lower() - start;
610                                         start += diff;
611                                         end += diff;
612                                 }
613                                 
614                                 if(end > adj_bounds->get_upper())
615                                 {
616                                         diff = adj_bounds->get_upper() - end;
617                                         start += diff;
618                                         end += diff;
619                                 }
620                         }               
621                         
622                         //synfig::info("Scrolling timerange to (%.4f,%.4f)",start,end);
623                         
624                         adj_timescale->set_lower(start);
625                         adj_timescale->set_upper(end);
626
627                         adj_timescale->changed();
628                 }else
629                 {
630                         dragscroll = true;
631                         lastx = curx;
632                         //lasty = cury;
633                 }
634                 
635                 return true;                            
636         }
637         
638         if(mod & Gdk::BUTTON1_MASK)
639         {
640                 double curx = event->x;
641                 
642                 //get time from drag...
643                 double  start = adj_timescale->get_lower(),
644                                 end = adj_timescale->get_upper(),
645                                 current = adj_timescale->get_value();
646                 double t = start + curx*(end - start)/get_width();
647                 
648                 //snap it to fps - if they exist...
649                 if(fps)
650                 {
651                         t = floor(t*fps + 0.5)/fps;
652                 }
653                 
654                 //set time if needed
655                 if(current != t)
656                 {                       
657                         adj_timescale->set_value(t);
658                         
659                         //Fixed this to actually do what it's supposed to...
660                         if(event->time-last_event_time>50)
661                         {
662                                 adj_timescale->value_changed();
663                                 last_event_time = event->time;
664                         }
665                 }
666                 
667                 return true;
668         }
669         
670         return false;
671 }
672
673 bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming
674 {
675         if(!adj_timescale) return false;
676         
677         //Update so we are calculating based on current values
678         //update_times();       
679         
680         //figure out if we should center ourselves on the current time
681         bool center = false;
682
683         //we want to zoom in on the time value if control is held down
684         if(Gdk::ModifierType(event->state) & Gdk::CONTROL_MASK)
685         {
686                 center = true;
687         }
688         
689         switch(event->direction)
690         {
691                 case GDK_SCROLL_UP: //zoom in
692                 {
693                         zoom_in(center);
694                         
695                         return true;
696                 }
697                 case GDK_SCROLL_DOWN: //zoom out
698                 {
699                         zoom_out(center);
700                         
701                         return true;
702                 }
703                 
704                 default: 
705                 {
706                         return false;
707                 }
708         }
709 }
710
711 void Widget_Timeslider::zoom_in(bool centerontime)
712 {
713         if(!adj_timescale) return;
714                 
715         double  start = adj_timescale->get_lower(),
716                         end = adj_timescale->get_upper(),
717                         current = adj_timescale->get_value();
718         
719         double focuspoint = centerontime ? current : (start + end)/2;
720         
721         //calculate new beginning and end
722         end = focuspoint + (end-focuspoint)*zoominfactor;
723         start = focuspoint + (start-focuspoint)*zoominfactor;
724         
725         //synfig::info("Zooming in timerange to (%.4f,%.4f)",start,end);
726         if(adj_bounds)
727         {
728                 if(start < adj_bounds->get_lower())
729                 {
730                         start = adj_bounds->get_lower();
731                 }
732                 
733                 if(end > adj_bounds->get_upper())
734                 {
735                         end = adj_bounds->get_upper();
736                 }
737         }
738         
739         //reset values
740         adj_timescale->set_lower(start);
741         adj_timescale->set_upper(end);
742         
743         //call changed function
744         adj_timescale->changed();
745 }
746
747 void Widget_Timeslider::zoom_out(bool centerontime)
748 {
749         if(!adj_timescale) return;
750                 
751         double  start = adj_timescale->get_lower(),
752                         end = adj_timescale->get_upper(),
753                         current = adj_timescale->get_value();
754         
755         double focuspoint = centerontime ? current : (start + end)/2;
756         
757         //calculate new beginning and end
758         end = focuspoint + (end-focuspoint)*zoomoutfactor;
759         start = focuspoint + (start-focuspoint)*zoomoutfactor;
760         
761         //synfig::info("Zooming out timerange to (%.4f,%.4f)",start,end);
762         if(adj_bounds)
763         {
764                 if(start < adj_bounds->get_lower())
765                 {
766                         start = adj_bounds->get_lower();
767                 }
768                 
769                 if(end > adj_bounds->get_upper())
770                 {
771                         end = adj_bounds->get_upper();  
772                 }
773         }
774         
775         //reset values
776         adj_timescale->set_lower(start);
777         adj_timescale->set_upper(end);
778         
779         //call changed function
780         adj_timescale->changed();
781 }
782
783 bool Widget_Timeslider::on_button_press_event(GdkEventButton *event) //for clicking
784 {
785         switch(event->button)
786         {
787                 //time click...
788                 case 1:
789                 {
790                         double  start = adj_timescale->get_lower(),
791                                         end = adj_timescale->get_upper(),
792                                         current = adj_timescale->get_value();
793                         
794                         double w = get_width();
795                         double t = start + (end - start) * event->x / w;
796                         
797                         t = floor(t*fps + 0.5)/fps;
798                         
799                         /*synfig::info("Clicking time from %.3lf to %.3lf [(%.2lf,%.2lf) %.2lf / %.2lf ... %.2lf", 
800                                                 current, vt, start, end, event->x, w, fps);*/
801                         
802                         if(t != current)
803                         {                       
804                                 current = t;
805                                 
806                                 if(adj_timescale)
807                                 {
808                                         adj_timescale->set_value(current);
809                                         adj_timescale->value_changed();
810                                 }
811                         }
812                                 
813                         break;
814                 }
815                 
816                 //scroll click
817                 case 2:
818                 {
819                         //start dragging
820                         dragscroll = true;
821                         lastx = event->x;
822                         //lasty = event->y;
823                         
824                         return true;
825                 }
826                 
827                 default:
828                 {
829                         break;
830                 }
831         }
832         
833         return false;
834 }
835
836 bool Widget_Timeslider::on_button_release_event(GdkEventButton *event) //end drag
837 {       
838         switch(event->button)
839         {               
840                 case 2:
841                 {
842                         //start dragging
843                         dragscroll = false;
844                         return true;
845                 }
846                 
847                 default:
848                 {
849                         break;
850                 }
851         }
852         
853         return false;
854 }