Remove .gitignore do nothing is ignored.
[synfig.git] / synfig-studio / trunk / src / gtkmm / widget_timeslider.cpp
index 651cbe0..ed5d63c 100644 (file)
@@ -6,7 +6,7 @@
 **
 **     \legal
 **     Copyright (c) 2004 Adrian Bentley
-**     Copyright (c) 2007 Chris Moore
+**     Copyright (c) 2007, 2008 Chris Moore
 **
 **     This package is free software; you can redistribute it and/or
 **     modify it under the terms of the GNU General Public License as
@@ -221,7 +221,6 @@ studio::render_time_point_to_window(
        if(selected)color=color_darken(color,1.3f);
        gc->set_rgb_fg_color(color);
 
-
        switch(tp.get_after())
        {
        case INTERPOLATION_TCB:
@@ -455,47 +454,81 @@ bool Widget_Timeslider::redraw(bool /*doublebuffer*/)
        //normal line/text color
        gc->set_rgb_fg_color(Gdk::Color("#333333"));
 
-       //draw these lines... (always 5 between) maybe 6?
-       const int subdiv = 4;
-
-       //1h 45 30 20 10 5
-       //..., 3m, 2m, 1m30s, 1m, 30s, 20s, 10s, 5s, 3s, 2s, 1s, 0.5s
-       //frames... (how???)
-       double ranges[] =
-       { 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 };
-       //{ 3600, 2700, 1800, 1200, 600, 300, 180, 120, 90, 60, 30, 20, 10, 5, 3, 2, 1, 0.5 };
-       const int ranges_size = sizeof(ranges)/sizeof(double);
+       int ifps = round_to_int(fps);
+       if (ifps < 1) ifps = 1;
 
-       double lowerrange = dtdp*75, upperrange = dtdp*150;
-       double midrange = (lowerrange + upperrange)/2;
+       std::vector<double> ranges;
 
-       //find most ideal scale
-       double scale = ranges[0];
-       {
-               double *val = binary_find(ranges, ranges+ranges_size, midrange);
-               double *after = val+1;
+       unsigned int pos = 0;
 
-               if(val >= ranges+ranges_size)
+       // build a list of all the factors of the frame rate
+       for (int i = 1; i*i <= ifps; i++)
+               if ((ifps%i) == 0)
                {
-                       val = ranges+ranges_size-1;
+                       ranges.insert(ranges.begin()+pos, i/fps);
+                       if (i*i != ifps)
+                               ranges.insert(ranges.begin()+pos+1, ifps/i/fps);
+                       pos++;
                }
 
-               if(after >= ranges+ranges_size)
-               {
-                       after = ranges+ranges_size-1;
-               }
+       // fill in any gaps where one factor is more than 2 times the previous
+       std::vector<double>::iterator iter, next;
+       pos = 0;
+       for (pos = 0; pos < ranges.size()-1; pos++)
+       {
+               iter = ranges.begin()+pos;
+               next = iter+1;
+               if (*iter*2 < *next)
+                       ranges.insert(next, *iter*2);
+       }
 
-               scale = *val;
+       double more_ranges[] = {
+               2, 3, 5, 10, 20, 30, 60, 90, 120, 180,
+               300, 600, 1200, 1800, 2700, 3600, 3600*2,
+               3600*4, 3600*8, 3600*16, 3600*32, 3600*64,
+               3600*128, 3600*256, 3600*512, 3600*1024 };
 
-               double diff = abs(scale - midrange), diff2 = abs(*after - midrange);
-               if(diff2 < diff)
-                       scale = *after;
-       }
+       ranges.insert(ranges.end(), more_ranges, more_ranges + sizeof(more_ranges)/sizeof(double));
+
+       double lowerrange = dtdp*140, upperrange = dtdp*280;
+       double midrange = (lowerrange + upperrange)/2;
+
+       //find most ideal scale
+       double scale;
+       next = binary_find(ranges.begin(), ranges.end(), midrange);
+       iter = next++;
 
-       //synfig::info("Range found: (l %.2lf,u %.2lf - m %.2lf) -> %.2lf",lowerrange,upperrange,midrange,scale);
+       if (iter == ranges.end()) iter--;
+       if (next == ranges.end()) next--;
 
-       //search around this area to get the right one
+       if (abs(*next - midrange) < abs(*iter - midrange))
+               iter = next;
 
+       scale = *iter;
+
+       // subdivide into this many tick marks (8 or less)
+       int subdiv = round_to_int(scale * ifps);
+
+       if (subdiv > 8)
+       {
+               const int ideal = subdiv;
+
+               // find a number of tick marks that nicely divides the scale
+               // (5 minutes divided by 6 is 50s, but that's not 'nice' -
+               //  5 ticks of 1m each is much simpler than 6 ticks of 50s)
+               for (subdiv = 8; subdiv > 0; subdiv--)
+                       if ((ideal <= ifps*2       && (ideal % (subdiv           )) == 0) ||
+                               (ideal <= ifps*2*60    && (ideal % (subdiv*ifps      )) == 0) ||
+                               (ideal <= ifps*2*60*60 && (ideal % (subdiv*ifps*60   )) == 0) ||
+                               (true                  && (ideal % (subdiv*ifps*60*60)) == 0))
+                               break;
+
+               // if we didn't find anything, use 4 ticks
+               if (!subdiv)
+                       subdiv = 4;
+       }
+
+       time_per_tickmark = scale / subdiv;
 
        //get first valid line and its position in pixel space
        double time = 0;
@@ -516,6 +549,7 @@ bool Widget_Timeslider::redraw(bool /*doublebuffer*/)
                double t = (time/scale - floor(time/scale))*subdiv; // the difference from the big mark in 0:1
                //sdindex = (int)floor(t + 0.5); //get how far through the range it is...
                sdindex = round_to_int(t); //get how far through the range it is...
+               if (sdindex == subdiv) sdindex = 0;
 
                //synfig::info("Extracted fr %.2lf -> %d", t, sdindex);
        }
@@ -542,7 +576,17 @@ bool Widget_Timeslider::redraw(bool /*doublebuffer*/)
 
                        //gc->set_rgb_fg_color(Gdk::Color("#000000"));
                        layout->set_text(timecode);
-                       window->draw_layout(gc,xpx+2,heightsmall,layout);
+                       Pango::AttrList attr_list;
+                       // Aproximately a font size of 8 pixels.
+                       // Pango::SCALE = 1024
+                       // create_attr_size waits a number in 1000th of pixels.
+                       // Should be user customizable in the future. Now it is fixed to 10
+                       Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE*10));
+                       pango_size.set_start_index(0);
+                       pango_size.set_end_index(64);
+                       attr_list.change(pango_size);
+                       layout->set_attributes(attr_list);
+                       window->draw_layout(gc,xpx+2,0,layout);
                }else
                {
                        window->draw_line(gc,xpx,0,xpx,heightsmall);
@@ -578,7 +622,6 @@ bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for drag
                double  start = adj_timescale->get_lower(),
                                end = adj_timescale->get_upper();
 
-
                if(dragscroll)
                {
                        if(event->time-last_event_time<30)
@@ -689,50 +732,80 @@ bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming
 
        //we want to zoom in on the time value if control is held down
        if(Gdk::ModifierType(event->state) & Gdk::CONTROL_MASK)
-       {
                center = true;
-       }
 
        switch(event->direction)
        {
                case GDK_SCROLL_UP: //zoom in
-               {
                        zoom_in(center);
-
                        return true;
-               }
+
                case GDK_SCROLL_DOWN: //zoom out
-               {
                        zoom_out(center);
-
                        return true;
-               }
-               
+
                case GDK_SCROLL_RIGHT:
                case GDK_SCROLL_LEFT:
-               {       
+               {
                        double t = adj_timescale->get_value();
+                       double orig_t = t;
                        double start = adj_timescale->get_lower();
                        double end = adj_timescale->get_upper();
-                       /*
-                       FIXME: be more intelligent about how far to scroll
-                       Perhaps it should be based on the tickmarks?
-                       for e.g. 1/4 of a tick mark per scroll event
-                       Obviously this  would need post-rounding to 1/fps
-                       */
-                       double adj = 1.0/fps;
+                       double lower = adj_bounds->get_lower();
+                       double upper = adj_bounds->get_upper();
+                       double adj = time_per_tickmark;
 
                        if( event->direction == GDK_SCROLL_RIGHT )
+                       {
+                               // step forward one tick
                                t += adj;
+
+                               // don't go past the end of time
+                               if (t > upper)
+                                       t = upper;
+
+                               // if we are already in the right half of the slider
+                               if ((t-start)*2 > (end-start))
+                               {
+                                       // if we can't scroll the background left one whole tick, scroll it to the end
+                                       if (end > upper - (t-orig_t))
+                                       {
+                                               adj_timescale->set_lower(upper - (end-start));
+                                               adj_timescale->set_upper(upper);
+                                       }
+                                       // else scroll the background left
+                                       else
+                                       {
+                                               adj_timescale->set_lower(start + (t-orig_t));
+                                               adj_timescale->set_upper(start + (t-orig_t) + (end-start));
+                                       }
+                               }
+                       }
                        else
+                       {
+                               // step backwards one tick
                                t -= adj;
 
-                       if( t < start ){
-                               adj_timescale->set_lower(t);
-                               adj_timescale->set_upper(t+end-start);
-                       } else if( t > end ){ 
-                               adj_timescale->set_upper(t);
-                               adj_timescale->set_lower(t-end+start);
+                               // don't go past the start of time
+                               if (t < lower)
+                                       t = lower;
+
+                               // if we are already in the left half of the slider
+                               if ((t-start)*2 < (end-start))
+                               {
+                                       // if we can't scroll the background right one whole tick, scroll it to the beginning
+                                       if (start < lower + (orig_t-t))
+                                       {
+                                               adj_timescale->set_lower(lower);
+                                               adj_timescale->set_upper(lower + (end-start));
+                                       }
+                                       // else scroll the background right
+                                       else
+                                       {
+                                               adj_timescale->set_lower(start - (orig_t-t));
+                                               adj_timescale->set_upper(start - (orig_t-t) + (end-start));
+                                       }
+                               }
                        }
 
                        if(adj_timescale)
@@ -742,11 +815,8 @@ bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming
                        }
                        return true;
                }
-               
                default:
-               {
                        return false;
-               }
        }
 }