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