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