Enable $Id$ expansion.
[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$
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 last_event_time(0),
320 fps(defaultfps),
321 dragscroll(false)
322 {
323         set_size_request(-1,fullheight);
324
325         //                click                    scroll                     zoom
326         add_events( Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK
327                                 | Gdk::BUTTON_MOTION_MASK | Gdk::SCROLL_MASK );
328
329         set_time_adjustment(&adj_default);
330         //update_times();
331 }
332
333 Widget_Timeslider::~Widget_Timeslider()
334 {
335 }
336
337 void Widget_Timeslider::set_time_adjustment(Gtk::Adjustment *x)
338 {
339         //disconnect old connections
340         time_value_change.disconnect();
341         time_other_change.disconnect();
342
343         //connect update function to new adjustment
344         adj_timescale = x;
345
346         if(x)
347         {
348                 time_value_change = x->signal_value_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw));
349                 time_other_change = x->signal_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw));
350                 //invalidated = true;
351                 //refresh();
352         }
353 }
354
355 void Widget_Timeslider::set_global_fps(float d)
356 {
357         if(fps != d)
358         {
359                 fps = d;
360
361                 //update everything since we need to redraw already
362                 //invalidated = true;
363                 //refresh();
364                 queue_draw();
365         }
366 }
367
368 /*void Widget_Timeslider::update_times()
369 {
370         if(adj_timescale)
371         {
372                 start = adj_timescale->get_lower();
373                 end = adj_timescale->get_upper();
374                 current = adj_timescale->get_value();
375         }
376 }*/
377
378 void Widget_Timeslider::refresh()
379 {
380 }
381 /*
382 {
383         if(invalidated)
384         {
385                 queue_draw();
386         }else if(adj_timescale)
387         {
388                 double  l = adj_timescale->get_lower(),
389                                 u = adj_timescale->get_upper(),
390                                 v = adj_timescale->get_value();
391
392                 bool invalid = (l != start) || (u != end) || (v != current);
393
394                 start = l;
395                 end = u;
396                 current = v;
397
398                 if(invalid) queue_draw();
399         }
400 }*/
401
402 bool Widget_Timeslider::redraw(bool doublebuffer)
403 {
404         Glib::RefPtr<Gdk::Window> window = get_window();
405
406         if(!window) return false;
407
408         Glib::RefPtr<Gdk::GC>   gc = Gdk::GC::create(window);
409         if(!gc) return false;
410
411         //synfig::info("Drawing Timeslider");
412         //clear and update to current values
413         //invalidated = false;
414         //update_times();
415
416         //draw grey rectangle
417         Gdk::Color      c("#7f7f7f");
418         gc->set_rgb_fg_color(c);
419         gc->set_background(c);
420
421         //Get the data for the window and the params to draw it...
422         int w = get_width(), h = get_height();
423
424         window->draw_rectangle(gc,true,0,0,w,h);
425
426         const double EPSILON = 1e-6;
427         if(!adj_timescale || w == 0) return true;
428
429         //Get the time information since we now know it's valid
430         double  start = adj_timescale->get_lower(),
431                         end = adj_timescale->get_upper(),
432                         current = adj_timescale->get_value();
433
434         if(end-start < EPSILON) return true;
435
436         //synfig::info("Drawing Lines");
437
438         //draw all the time stuff
439         double dtdp = (end - start)/get_width();
440         double dpdt = 1/dtdp;
441
442         //lines
443
444         //Draw the time line...
445         double tpx = (current-start)*dpdt;
446         gc->set_rgb_fg_color(Gdk::Color("#ffaf00"));
447         window->draw_line(gc,round_to_int(tpx),0,round_to_int(tpx),fullheight);
448
449         //normal line/text color
450         gc->set_rgb_fg_color(Gdk::Color("#333333"));
451
452         //draw these lines... (always 5 between) maybe 6?
453         const int subdiv = 4;
454
455         //1h 45 30 20 10 5
456         //..., 3m, 2m, 1m30s, 1m, 30s, 20s, 10s, 5s, 3s, 2s, 1s, 0.5s
457         //frames... (how???)
458         double ranges[] =
459         { 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 };
460         //{ 3600, 2700, 1800, 1200, 600, 300, 180, 120, 90, 60, 30, 20, 10, 5, 3, 2, 1, 0.5 };
461         const int ranges_size = sizeof(ranges)/sizeof(double);
462
463         double lowerrange = dtdp*75, upperrange = dtdp*150;
464         double midrange = (lowerrange + upperrange)/2;
465
466         //find most ideal scale
467         double scale = ranges[0];
468         {
469                 double *val = binary_find(ranges, ranges+ranges_size, midrange);
470                 double *after = val+1;
471
472                 if(val >= ranges+ranges_size)
473                 {
474                         val = ranges+ranges_size-1;
475                 }
476
477                 if(after >= ranges+ranges_size)
478                 {
479                         after = ranges+ranges_size-1;
480                 }
481
482                 scale = *val;
483
484                 double diff = abs(scale - midrange), diff2 = abs(*after - midrange);
485                 if(diff2 < diff)
486                         scale = *after;
487         }
488
489         //synfig::info("Range found: (l %.2lf,u %.2lf - m %.2lf) -> %.2lf",lowerrange,upperrange,midrange,scale);
490
491         //search around this area to get the right one
492
493
494         //get first valid line and it's position in pixel space
495         double time = 0;
496         double pixel = 0;
497
498         int sdindex = 0;
499
500         double subr = scale / subdiv;
501
502         //get it's position inside...
503         time = ceil(start/subr)*subr - start;
504         pixel = time*dpdt;
505
506         //absolute time of the line to be drawn
507         time += start;
508
509         { //inside the big'n
510                 double t = (time/scale - floor(time/scale))*subdiv; // the difference from the big mark in 0:1
511                 //sdindex = (int)floor(t + 0.5); //get how far through the range it is...
512                 sdindex = round_to_int(t); //get how far through the range it is...
513
514                 //synfig::info("Extracted fr %.2lf -> %d", t, sdindex);
515         }
516
517         //synfig::info("Initial values: %.4lf t, %.1lf pixels, %d i", time,pixel,sdindex);
518
519         //loop to draw
520         const int heightbig = 12;
521         const int heightsmall = 4;
522
523         int width = get_width();
524         while( pixel < width )
525         {
526                 int xpx = round_to_int(pixel);
527
528                 //draw big
529                 if(sdindex == 0)
530                 {
531                         window->draw_line(gc,xpx,0,xpx,heightbig);
532                         //round the time to nearest frame and draw the text
533                         Time tm((double)time);
534                         if(get_global_fps()) tm.round(get_global_fps());
535                         Glib::ustring timecode(tm.get_string(get_global_fps(),App::get_time_format()));
536
537                         //gc->set_rgb_fg_color(Gdk::Color("#000000"));
538                         layout->set_text(timecode);
539                         window->draw_layout(gc,xpx+2,heightsmall,layout);
540                 }else
541                 {
542                         window->draw_line(gc,xpx,0,xpx,heightsmall);
543                 }
544
545                 //increment time and position
546                 pixel += subr / dtdp;
547                 time += subr;
548
549                 //increment index
550                 if(++sdindex >= subdiv) sdindex -= subdiv;
551         }
552
553         return true;
554 }
555
556 bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for dragging
557 {
558         if(!adj_timescale) return false;
559
560         Gdk::ModifierType mod = Gdk::ModifierType(event->state);
561
562         //scrolling...
563
564         //NOTE: we might want to address the possibility of dragging with both buttons held down
565
566         if(mod & Gdk::BUTTON2_MASK)
567         {
568
569                 //we need this for scrolling by dragging
570                 double  curx = event->x;
571
572                 double  start = adj_timescale->get_lower(),
573                                 end = adj_timescale->get_upper();
574
575
576                 if(dragscroll)
577                 {
578                         if(event->time-last_event_time<30)
579                                 return false;
580                         else
581                                 last_event_time=event->time;
582
583                         if(abs(lastx - curx) < 1 && end != start) return true;
584                         //translate the window and correct it
585
586                         //update our stuff so we are operating correctly
587                         //invalidated = true;
588                         //update_times();
589
590                         //Note: Use inverse of mouse movement because of conceptual space relationship
591                         double diff = lastx - curx; //curx - lastx;
592
593                         //NOTE: This might be incorrect...
594                         //fraction to move...
595                         double dpx = (end - start)/get_width();
596                         lastx = curx;
597
598                         diff *= dpx;
599
600                         //Adjust...
601                         start += diff;
602                         end += diff;
603
604                         //But clamp to bounds if they exist...
605                         //HACK - bounds should not be required for this slider
606                         if(adj_bounds)
607                         {
608                                 if(start < adj_bounds->get_lower())
609                                 {
610                                         diff = adj_bounds->get_lower() - start;
611                                         start += diff;
612                                         end += diff;
613                                 }
614
615                                 if(end > adj_bounds->get_upper())
616                                 {
617                                         diff = adj_bounds->get_upper() - end;
618                                         start += diff;
619                                         end += diff;
620                                 }
621                         }
622
623                         //synfig::info("Scrolling timerange to (%.4f,%.4f)",start,end);
624
625                         adj_timescale->set_lower(start);
626                         adj_timescale->set_upper(end);
627
628                         adj_timescale->changed();
629                 }else
630                 {
631                         dragscroll = true;
632                         lastx = curx;
633                         //lasty = cury;
634                 }
635
636                 return true;
637         }
638
639         if(mod & Gdk::BUTTON1_MASK)
640         {
641                 double curx = event->x;
642
643                 //get time from drag...
644                 double  start = adj_timescale->get_lower(),
645                                 end = adj_timescale->get_upper(),
646                                 current = adj_timescale->get_value();
647                 double t = start + curx*(end - start)/get_width();
648
649                 //snap it to fps - if they exist...
650                 if(fps)
651                 {
652                         t = floor(t*fps + 0.5)/fps;
653                 }
654
655                 //set time if needed
656                 if(current != t)
657                 {
658                         adj_timescale->set_value(t);
659
660                         //Fixed this to actually do what it's supposed to...
661                         if(event->time-last_event_time>50)
662                         {
663                                 adj_timescale->value_changed();
664                                 last_event_time = event->time;
665                         }
666                 }
667
668                 return true;
669         }
670
671         return false;
672 }
673
674 bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming
675 {
676         if(!adj_timescale) return false;
677
678         //Update so we are calculating based on current values
679         //update_times();
680
681         //figure out if we should center ourselves on the current time
682         bool center = false;
683
684         //we want to zoom in on the time value if control is held down
685         if(Gdk::ModifierType(event->state) & Gdk::CONTROL_MASK)
686         {
687                 center = true;
688         }
689
690         switch(event->direction)
691         {
692                 case GDK_SCROLL_UP: //zoom in
693                 {
694                         zoom_in(center);
695
696                         return true;
697                 }
698                 case GDK_SCROLL_DOWN: //zoom out
699                 {
700                         zoom_out(center);
701
702                         return true;
703                 }
704
705                 default:
706                 {
707                         return false;
708                 }
709         }
710 }
711
712 void Widget_Timeslider::zoom_in(bool centerontime)
713 {
714         if(!adj_timescale) return;
715
716         double  start = adj_timescale->get_lower(),
717                         end = adj_timescale->get_upper(),
718                         current = adj_timescale->get_value();
719
720         double focuspoint = centerontime ? current : (start + end)/2;
721
722         //calculate new beginning and end
723         end = focuspoint + (end-focuspoint)*zoominfactor;
724         start = focuspoint + (start-focuspoint)*zoominfactor;
725
726         //synfig::info("Zooming in timerange to (%.4f,%.4f)",start,end);
727         if(adj_bounds)
728         {
729                 if(start < adj_bounds->get_lower())
730                 {
731                         start = adj_bounds->get_lower();
732                 }
733
734                 if(end > adj_bounds->get_upper())
735                 {
736                         end = adj_bounds->get_upper();
737                 }
738         }
739
740         //reset values
741         adj_timescale->set_lower(start);
742         adj_timescale->set_upper(end);
743
744         //call changed function
745         adj_timescale->changed();
746 }
747
748 void Widget_Timeslider::zoom_out(bool centerontime)
749 {
750         if(!adj_timescale) return;
751
752         double  start = adj_timescale->get_lower(),
753                         end = adj_timescale->get_upper(),
754                         current = adj_timescale->get_value();
755
756         double focuspoint = centerontime ? current : (start + end)/2;
757
758         //calculate new beginning and end
759         end = focuspoint + (end-focuspoint)*zoomoutfactor;
760         start = focuspoint + (start-focuspoint)*zoomoutfactor;
761
762         //synfig::info("Zooming out timerange to (%.4f,%.4f)",start,end);
763         if(adj_bounds)
764         {
765                 if(start < adj_bounds->get_lower())
766                 {
767                         start = adj_bounds->get_lower();
768                 }
769
770                 if(end > adj_bounds->get_upper())
771                 {
772                         end = adj_bounds->get_upper();
773                 }
774         }
775
776         //reset values
777         adj_timescale->set_lower(start);
778         adj_timescale->set_upper(end);
779
780         //call changed function
781         adj_timescale->changed();
782 }
783
784 bool Widget_Timeslider::on_button_press_event(GdkEventButton *event) //for clicking
785 {
786         switch(event->button)
787         {
788                 //time click...
789                 case 1:
790                 {
791                         double  start = adj_timescale->get_lower(),
792                                         end = adj_timescale->get_upper(),
793                                         current = adj_timescale->get_value();
794
795                         double w = get_width();
796                         double t = start + (end - start) * event->x / w;
797
798                         t = floor(t*fps + 0.5)/fps;
799
800                         /*synfig::info("Clicking time from %.3lf to %.3lf [(%.2lf,%.2lf) %.2lf / %.2lf ... %.2lf",
801                                                 current, vt, start, end, event->x, w, fps);*/
802
803                         if(t != current)
804                         {
805                                 current = t;
806
807                                 if(adj_timescale)
808                                 {
809                                         adj_timescale->set_value(current);
810                                         adj_timescale->value_changed();
811                                 }
812                         }
813
814                         break;
815                 }
816
817                 //scroll click
818                 case 2:
819                 {
820                         //start dragging
821                         dragscroll = true;
822                         lastx = event->x;
823                         //lasty = event->y;
824
825                         return true;
826                 }
827
828                 default:
829                 {
830                         break;
831                 }
832         }
833
834         return false;
835 }
836
837 bool Widget_Timeslider::on_button_release_event(GdkEventButton *event) //end drag
838 {
839         switch(event->button)
840         {
841                 case 2:
842                 {
843                         //start dragging
844                         dragscroll = false;
845                         return true;
846                 }
847
848                 default:
849                 {
850                         break;
851                 }
852         }
853
854         return false;
855 }