Fix a bug that was causing the timeslider to not be displayed in certain circumstances.
[synfig.git] / synfig-studio / trunk / src / gtkmm / widget_timeslider.cpp
blobcdf5ea7d06c11ae8f27dd2cc93f36989acf9674b
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, 2008 Chris Moore
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.
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
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
26 #ifdef USING_PCH
27 # include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
33 #include "widget_timeslider.h"
35 #include <ETL/misc>
37 #include <cmath>
39 #include "general.h"
41 #endif
43 /* === U S I N G =========================================================== */
45 using namespace std;
46 using namespace etl;
47 using namespace synfig;
49 using studio::Widget_Timeslider;
51 /* === M A C R O S ========================================================= */
53 /* === G L O B A L S ======================================================= */
54 const double zoominfactor = 0.75;
55 const double zoomoutfactor = 1/zoominfactor;
57 /* === P R O C E D U R E S ================================================= */
59 Gdk::Color get_interp_color(synfig::Interpolation x)
61 switch(x)
63 case INTERPOLATION_TCB:
64 return Gdk::Color("#00B000");
66 break;
68 case INTERPOLATION_LINEAR:
69 return Gdk::Color("#B0B000");
70 break;
72 case INTERPOLATION_CONSTANT:
73 return Gdk::Color("#C70000");
74 break;
76 case INTERPOLATION_HALT:
77 return Gdk::Color("#00b0b0");
78 break;
80 case INTERPOLATION_MANUAL:
81 return Gdk::Color("#B000B0");
82 break;
84 case INTERPOLATION_UNDEFINED: default:
85 return Gdk::Color("#808080");
86 break;
90 static Gdk::Color
91 color_darken(Gdk::Color x, float amount)
93 double red = x.get_red_p() * amount;
94 double green = x.get_green_p() * amount;
95 double blue = x.get_blue_p() * amount;
97 x.set_rgb_p( red > 1 ? 1 : red,
98 green > 1 ? 1 : green,
99 blue > 1 ? 1 : blue);
101 return x;
104 void
105 studio::render_time_point_to_window(
106 const Glib::RefPtr<Gdk::Drawable>& window,
107 const Gdk::Rectangle& area,
108 const synfig::TimePoint &tp,
109 bool selected
112 Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
113 const Gdk::Color black("#000000");
115 if(selected)
116 gc->set_line_attributes(2,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
117 else
118 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
120 Gdk::Color color;
121 std::vector<Gdk::Point> points;
123 /*- BEFORE ------------------------------------- */
125 color=get_interp_color(tp.get_before());
126 color=color_darken(color,1.0f);
127 if(selected)color=color_darken(color,1.3f);
128 gc->set_rgb_fg_color(color);
130 switch(tp.get_before())
132 case INTERPOLATION_TCB:
133 window->draw_arc(
135 true,
136 area.get_x(),
137 area.get_y(),
138 area.get_width(),
139 area.get_height(),
140 64*90,
141 64*180
143 gc->set_rgb_fg_color(black);
144 window->draw_arc(
146 false,
147 area.get_x(),
148 area.get_y(),
149 area.get_width(),
150 area.get_height(),
151 64*90,
152 64*180
154 break;
156 case INTERPOLATION_HALT:
157 window->draw_arc(
159 true,
160 area.get_x(),
161 area.get_y(),
162 area.get_width(),
163 area.get_height()*2,
164 64*90,
165 64*90
167 gc->set_rgb_fg_color(black);
168 window->draw_arc(
170 false,
171 area.get_x(),
172 area.get_y(),
173 area.get_width(),
174 area.get_height()*2,
175 64*90,
176 64*90
178 break;
180 case INTERPOLATION_LINEAR:
181 points.clear();
182 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
183 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()));
184 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
185 window->draw_polygon(gc,true,points);
186 gc->set_rgb_fg_color(black);
187 window->draw_lines(gc,points);
188 break;
190 case INTERPOLATION_CONSTANT:
191 points.clear();
192 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
193 points.push_back(Gdk::Point(area.get_x()+area.get_width()/4,area.get_y()));
194 points.push_back(Gdk::Point(area.get_x()+area.get_width()/4,area.get_y()+area.get_height()/2));
195 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()/2));
196 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()));
197 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
198 window->draw_polygon(gc,true,points);
199 gc->set_rgb_fg_color(black);
200 window->draw_lines(gc,points);
201 break;
203 case INTERPOLATION_UNDEFINED: default:
204 points.clear();
205 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
206 points.push_back(Gdk::Point(area.get_x()+area.get_width()/3,area.get_y()));
207 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()/3));
208 points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()-area.get_height()/3));
209 points.push_back(Gdk::Point(area.get_x()+area.get_width()/3,area.get_y()+area.get_height()));
210 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
211 window->draw_polygon(gc,true,points);
212 gc->set_rgb_fg_color(black);
213 window->draw_lines(gc,points);
214 break;
217 /*- AFTER -------------------------------------- */
219 color=get_interp_color(tp.get_after());
220 color=color_darken(color,0.8f);
221 if(selected)color=color_darken(color,1.3f);
222 gc->set_rgb_fg_color(color);
224 switch(tp.get_after())
226 case INTERPOLATION_TCB:
227 window->draw_arc(
229 true,
230 area.get_x(),
231 area.get_y(),
232 area.get_width(),
233 area.get_height(),
234 64*270,
235 64*180
237 gc->set_rgb_fg_color(black);
238 window->draw_arc(
240 false,
241 area.get_x(),
242 area.get_y(),
243 area.get_width(),
244 area.get_height(),
245 64*270,
246 64*180
248 break;
250 case INTERPOLATION_HALT:
251 window->draw_arc(
253 true,
254 area.get_x(),
255 area.get_y()-area.get_height(),
256 area.get_width(),
257 area.get_height()*2,
258 64*270,
259 64*90
261 gc->set_rgb_fg_color(black);
262 window->draw_arc(
264 false,
265 area.get_x(),
266 area.get_y()-area.get_height(),
267 area.get_width(),
268 area.get_height()*2,
269 64*270,
270 64*90
272 break;
274 case INTERPOLATION_LINEAR:
275 points.clear();
276 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
277 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()));
278 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
279 window->draw_polygon(gc,true,points);
280 gc->set_rgb_fg_color(black);
281 window->draw_lines(gc,points);
282 break;
284 case INTERPOLATION_CONSTANT:
285 points.clear();
286 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
287 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()));
288 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()/2));
289 points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/4,area.get_y()+area.get_height()/2));
290 points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/4,area.get_y()+area.get_height()));
291 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
292 window->draw_polygon(gc,true,points);
293 gc->set_rgb_fg_color(black);
294 window->draw_lines(gc,points);
295 break;
297 case INTERPOLATION_UNDEFINED: default:
298 points.clear();
299 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()));
300 points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/3,area.get_y()));
301 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()/3));
302 points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()-area.get_height()/3));
303 points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/3,area.get_y()+area.get_height()));
304 points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height()));
305 window->draw_polygon(gc,true,points);
306 gc->set_rgb_fg_color(black);
307 window->draw_lines(gc,points);
308 break;
313 /* === M E T H O D S ======================================================= */
315 /* === E N T R Y P O I N T ================================================= */
316 double defaultfps = 24;
317 const int fullheight = 20;
319 Widget_Timeslider::Widget_Timeslider()
320 :layout(Pango::Layout::create(get_pango_context())),
321 adj_default(0,0,2,1/defaultfps,10/defaultfps),
322 adj_timescale(0),
323 //invalidated(false),
324 last_event_time(0),
325 fps(defaultfps),
326 dragscroll(false)
328 set_size_request(-1,fullheight);
330 // click scroll zoom
331 add_events( Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK
332 | Gdk::BUTTON_MOTION_MASK | Gdk::SCROLL_MASK );
334 set_time_adjustment(&adj_default);
335 //update_times();
338 Widget_Timeslider::~Widget_Timeslider()
342 void Widget_Timeslider::set_time_adjustment(Gtk::Adjustment *x)
344 //disconnect old connections
345 time_value_change.disconnect();
346 time_other_change.disconnect();
348 //connect update function to new adjustment
349 adj_timescale = x;
351 if(x)
353 time_value_change = x->signal_value_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw));
354 time_other_change = x->signal_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw));
355 //invalidated = true;
356 //refresh();
360 void Widget_Timeslider::set_global_fps(float d)
362 if(fps != d)
364 fps = d;
366 //update everything since we need to redraw already
367 //invalidated = true;
368 //refresh();
369 queue_draw();
373 /*void Widget_Timeslider::update_times()
375 if(adj_timescale)
377 start = adj_timescale->get_lower();
378 end = adj_timescale->get_upper();
379 current = adj_timescale->get_value();
383 void Widget_Timeslider::refresh()
388 if(invalidated)
390 queue_draw();
391 }else if(adj_timescale)
393 double l = adj_timescale->get_lower(),
394 u = adj_timescale->get_upper(),
395 v = adj_timescale->get_value();
397 bool invalid = (l != start) || (u != end) || (v != current);
399 start = l;
400 end = u;
401 current = v;
403 if(invalid) queue_draw();
407 bool Widget_Timeslider::redraw(bool /*doublebuffer*/)
409 Glib::RefPtr<Gdk::Window> window = get_window();
411 if(!window) return false;
413 Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(window);
414 if(!gc) return false;
416 //synfig::info("Drawing Timeslider");
417 //clear and update to current values
418 //invalidated = false;
419 //update_times();
421 //draw grey rectangle
422 Gdk::Color c("#7f7f7f");
423 gc->set_rgb_fg_color(c);
424 gc->set_background(c);
426 //Get the data for the window and the params to draw it...
427 int w = get_width(), h = get_height();
429 window->draw_rectangle(gc,true,0,0,w,h);
431 const double EPSILON = 1e-6;
432 if(!adj_timescale || w == 0) return true;
434 //Get the time information since we now know it's valid
435 double start = adj_timescale->get_lower(),
436 end = adj_timescale->get_upper(),
437 current = adj_timescale->get_value();
439 if(end-start < EPSILON) return true;
441 //synfig::info("Drawing Lines");
443 //draw all the time stuff
444 double dtdp = (end - start)/get_width();
445 double dpdt = 1/dtdp;
447 //lines
449 //Draw the time line...
450 double tpx = (current-start)*dpdt;
451 gc->set_rgb_fg_color(Gdk::Color("#ffaf00"));
452 window->draw_line(gc,round_to_int(tpx),0,round_to_int(tpx),fullheight);
454 //normal line/text color
455 gc->set_rgb_fg_color(Gdk::Color("#333333"));
457 int ifps = round_to_int(fps);
458 if (ifps < 1) ifps = 1;
460 std::vector<double> ranges;
462 unsigned int pos = 0;
464 // build a list of all the factors of the frame rate
465 for (int i = 1; i*i <= ifps; i++)
466 if ((ifps%i) == 0)
468 ranges.insert(ranges.begin()+pos, i/fps);
469 if (i*i != ifps)
470 ranges.insert(ranges.begin()+pos+1, ifps/i/fps);
471 pos++;
474 // fill in any gaps where one factor is more than 2 times the previous
475 std::vector<double>::iterator iter, next;
476 pos = 0;
477 for (pos = 0; pos < ranges.size()-1; pos++)
479 iter = ranges.begin()+pos;
480 next = iter+1;
481 if (*iter*2 < *next)
482 ranges.insert(next, *iter*2);
485 double more_ranges[] = {
486 2, 3, 5, 10, 20, 30, 60, 90, 120, 180,
487 300, 600, 1200, 1800, 2700, 3600, 3600*2,
488 3600*4, 3600*8, 3600*16, 3600*32, 3600*64,
489 3600*128, 3600*256, 3600*512, 3600*1024 };
491 ranges.insert(ranges.end(), more_ranges, more_ranges + sizeof(more_ranges)/sizeof(double));
493 double lowerrange = dtdp*140, upperrange = dtdp*280;
494 double midrange = (lowerrange + upperrange)/2;
496 //find most ideal scale
497 double scale;
498 next = binary_find(ranges.begin(), ranges.end(), midrange);
499 iter = next++;
501 if (iter == ranges.end()) iter--;
502 if (next == ranges.end()) next--;
504 if (abs(*next - midrange) < abs(*iter - midrange))
505 iter = next;
507 scale = *iter;
509 // subdivide into this many tick marks (8 or less)
510 int subdiv = round_to_int(scale * ifps);
512 if (subdiv > 8)
514 const int ideal = subdiv;
516 // find a number of tick marks that nicely divides the scale
517 // (5 minutes divided by 6 is 50s, but that's not 'nice' -
518 // 5 ticks of 1m each is much simpler than 6 ticks of 50s)
519 for (subdiv = 8; subdiv > 0; subdiv--)
520 if ((ideal <= ifps*2 && (ideal % (subdiv )) == 0) ||
521 (ideal <= ifps*2*60 && (ideal % (subdiv*ifps )) == 0) ||
522 (ideal <= ifps*2*60*60 && (ideal % (subdiv*ifps*60 )) == 0) ||
523 (true && (ideal % (subdiv*ifps*60*60)) == 0))
524 break;
526 // if we didn't find anything, use 4 ticks
527 if (!subdiv)
528 subdiv = 4;
531 time_per_tickmark = scale / subdiv;
533 //get first valid line and its position in pixel space
534 double time = 0;
535 double pixel = 0;
537 int sdindex = 0;
539 double subr = scale / subdiv;
541 //get its position inside...
542 time = ceil(start/subr)*subr - start;
543 pixel = time*dpdt;
545 //absolute time of the line to be drawn
546 time += start;
548 { //inside the big'n
549 double t = (time/scale - floor(time/scale))*subdiv; // the difference from the big mark in 0:1
550 //sdindex = (int)floor(t + 0.5); //get how far through the range it is...
551 sdindex = round_to_int(t); //get how far through the range it is...
552 if (sdindex == subdiv) sdindex = 0;
554 //synfig::info("Extracted fr %.2lf -> %d", t, sdindex);
557 //synfig::info("Initial values: %.4lf t, %.1lf pixels, %d i", time,pixel,sdindex);
559 //loop to draw
560 const int heightbig = 12;
561 const int heightsmall = 4;
563 int width = get_width();
564 while( pixel < width )
566 int xpx = round_to_int(pixel);
568 //draw big
569 if(sdindex == 0)
571 window->draw_line(gc,xpx,0,xpx,heightbig);
572 //round the time to nearest frame and draw the text
573 Time tm((double)time);
574 if(get_global_fps()) tm.round(get_global_fps());
575 Glib::ustring timecode(tm.get_string(get_global_fps(),App::get_time_format()));
577 //gc->set_rgb_fg_color(Gdk::Color("#000000"));
578 layout->set_text(timecode);
579 window->draw_layout(gc,xpx+2,heightsmall,layout);
580 }else
582 window->draw_line(gc,xpx,0,xpx,heightsmall);
585 //increment time and position
586 pixel += subr / dtdp;
587 time += subr;
589 //increment index
590 if(++sdindex >= subdiv) sdindex -= subdiv;
593 return true;
596 bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for dragging
598 if(!adj_timescale) return false;
600 Gdk::ModifierType mod = Gdk::ModifierType(event->state);
602 //scrolling...
604 //NOTE: we might want to address the possibility of dragging with both buttons held down
606 if(mod & Gdk::BUTTON2_MASK)
609 //we need this for scrolling by dragging
610 double curx = event->x;
612 double start = adj_timescale->get_lower(),
613 end = adj_timescale->get_upper();
615 if(dragscroll)
617 if(event->time-last_event_time<30)
618 return false;
619 else
620 last_event_time=event->time;
622 if(abs(lastx - curx) < 1 && end != start) return true;
623 //translate the window and correct it
625 //update our stuff so we are operating correctly
626 //invalidated = true;
627 //update_times();
629 //Note: Use inverse of mouse movement because of conceptual space relationship
630 double diff = lastx - curx; //curx - lastx;
632 //NOTE: This might be incorrect...
633 //fraction to move...
634 double dpx = (end - start)/get_width();
635 lastx = curx;
637 diff *= dpx;
639 //Adjust...
640 start += diff;
641 end += diff;
643 //But clamp to bounds if they exist...
644 //HACK - bounds should not be required for this slider
645 if(adj_bounds)
647 if(start < adj_bounds->get_lower())
649 diff = adj_bounds->get_lower() - start;
650 start += diff;
651 end += diff;
654 if(end > adj_bounds->get_upper())
656 diff = adj_bounds->get_upper() - end;
657 start += diff;
658 end += diff;
662 //synfig::info("Scrolling timerange to (%.4f,%.4f)",start,end);
664 adj_timescale->set_lower(start);
665 adj_timescale->set_upper(end);
667 adj_timescale->changed();
668 }else
670 dragscroll = true;
671 lastx = curx;
672 //lasty = cury;
675 return true;
678 if(mod & Gdk::BUTTON1_MASK)
680 double curx = event->x;
682 //get time from drag...
683 double start = adj_timescale->get_lower(),
684 end = adj_timescale->get_upper(),
685 current = adj_timescale->get_value();
686 double t = start + curx*(end - start)/get_width();
688 //snap it to fps - if they exist...
689 if(fps)
691 t = floor(t*fps + 0.5)/fps;
694 //set time if needed
695 if(current != t)
697 adj_timescale->set_value(t);
699 //Fixed this to actually do what it's supposed to...
700 if(event->time-last_event_time>50)
702 adj_timescale->value_changed();
703 last_event_time = event->time;
707 return true;
710 return false;
713 bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming
715 if(!adj_timescale) return false;
717 //Update so we are calculating based on current values
718 //update_times();
720 //figure out if we should center ourselves on the current time
721 bool center = false;
723 //we want to zoom in on the time value if control is held down
724 if(Gdk::ModifierType(event->state) & Gdk::CONTROL_MASK)
725 center = true;
727 switch(event->direction)
729 case GDK_SCROLL_UP: //zoom in
730 zoom_in(center);
731 return true;
733 case GDK_SCROLL_DOWN: //zoom out
734 zoom_out(center);
735 return true;
737 case GDK_SCROLL_RIGHT:
738 case GDK_SCROLL_LEFT:
740 double t = adj_timescale->get_value();
741 double orig_t = t;
742 double start = adj_timescale->get_lower();
743 double end = adj_timescale->get_upper();
744 double lower = adj_bounds->get_lower();
745 double upper = adj_bounds->get_upper();
746 double adj = time_per_tickmark;
748 if( event->direction == GDK_SCROLL_RIGHT )
750 // step forward one tick
751 t += adj;
753 // don't go past the end of time
754 if (t > upper)
755 t = upper;
757 // if we are already in the right half of the slider
758 if ((t-start)*2 > (end-start))
760 // if we can't scroll the background left one whole tick, scroll it to the end
761 if (end > upper - (t-orig_t))
763 adj_timescale->set_lower(upper - (end-start));
764 adj_timescale->set_upper(upper);
766 // else scroll the background left
767 else
769 adj_timescale->set_lower(start + (t-orig_t));
770 adj_timescale->set_upper(start + (t-orig_t) + (end-start));
774 else
776 // step backwards one tick
777 t -= adj;
779 // don't go past the start of time
780 if (t < lower)
781 t = lower;
783 // if we are already in the left half of the slider
784 if ((t-start)*2 < (end-start))
786 // if we can't scroll the background right one whole tick, scroll it to the beginning
787 if (start < lower + (orig_t-t))
789 adj_timescale->set_lower(lower);
790 adj_timescale->set_upper(lower + (end-start));
792 // else scroll the background right
793 else
795 adj_timescale->set_lower(start - (orig_t-t));
796 adj_timescale->set_upper(start - (orig_t-t) + (end-start));
801 if(adj_timescale)
803 adj_timescale->set_value(t);
804 adj_timescale->value_changed();
806 return true;
808 default:
809 return false;
813 void Widget_Timeslider::zoom_in(bool centerontime)
815 if(!adj_timescale) return;
817 double start = adj_timescale->get_lower(),
818 end = adj_timescale->get_upper(),
819 current = adj_timescale->get_value();
821 double focuspoint = centerontime ? current : (start + end)/2;
823 //calculate new beginning and end
824 end = focuspoint + (end-focuspoint)*zoominfactor;
825 start = focuspoint + (start-focuspoint)*zoominfactor;
827 //synfig::info("Zooming in timerange to (%.4f,%.4f)",start,end);
828 if(adj_bounds)
830 if(start < adj_bounds->get_lower())
832 start = adj_bounds->get_lower();
835 if(end > adj_bounds->get_upper())
837 end = adj_bounds->get_upper();
841 //reset values
842 adj_timescale->set_lower(start);
843 adj_timescale->set_upper(end);
845 //call changed function
846 adj_timescale->changed();
849 void Widget_Timeslider::zoom_out(bool centerontime)
851 if(!adj_timescale) return;
853 double start = adj_timescale->get_lower(),
854 end = adj_timescale->get_upper(),
855 current = adj_timescale->get_value();
857 double focuspoint = centerontime ? current : (start + end)/2;
859 //calculate new beginning and end
860 end = focuspoint + (end-focuspoint)*zoomoutfactor;
861 start = focuspoint + (start-focuspoint)*zoomoutfactor;
863 //synfig::info("Zooming out timerange to (%.4f,%.4f)",start,end);
864 if(adj_bounds)
866 if(start < adj_bounds->get_lower())
868 start = adj_bounds->get_lower();
871 if(end > adj_bounds->get_upper())
873 end = adj_bounds->get_upper();
877 //reset values
878 adj_timescale->set_lower(start);
879 adj_timescale->set_upper(end);
881 //call changed function
882 adj_timescale->changed();
885 bool Widget_Timeslider::on_button_press_event(GdkEventButton *event) //for clicking
887 switch(event->button)
889 //time click...
890 case 1:
892 double start = adj_timescale->get_lower(),
893 end = adj_timescale->get_upper(),
894 current = adj_timescale->get_value();
896 double w = get_width();
897 double t = start + (end - start) * event->x / w;
899 t = floor(t*fps + 0.5)/fps;
901 /*synfig::info("Clicking time from %.3lf to %.3lf [(%.2lf,%.2lf) %.2lf / %.2lf ... %.2lf",
902 current, vt, start, end, event->x, w, fps);*/
904 if(t != current)
906 current = t;
908 if(adj_timescale)
910 adj_timescale->set_value(current);
911 adj_timescale->value_changed();
915 break;
918 //scroll click
919 case 2:
921 //start dragging
922 dragscroll = true;
923 lastx = event->x;
924 //lasty = event->y;
926 return true;
929 default:
931 break;
935 return false;
938 bool Widget_Timeslider::on_button_release_event(GdkEventButton *event) //end drag
940 switch(event->button)
942 case 2:
944 //start dragging
945 dragscroll = false;
946 return true;
949 default:
951 break;
955 return false;