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