X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-studio%2Ftrunk%2Fsrc%2Fgtkmm%2Fwidget_timeslider.cpp;h=d935b54ad91d71c6050dd612898078e08b60ab5b;hb=756c0d29ac1742f231e6615f9a577e574e35a4af;hp=9f8891aff71e007654dead3b1a957eec52e56621;hpb=75635270584b5775982adbd70250a0ebc7e3e76b;p=synfig.git diff --git a/synfig-studio/trunk/src/gtkmm/widget_timeslider.cpp b/synfig-studio/trunk/src/gtkmm/widget_timeslider.cpp index 9f8891a..d935b54 100644 --- a/synfig-studio/trunk/src/gtkmm/widget_timeslider.cpp +++ b/synfig-studio/trunk/src/gtkmm/widget_timeslider.cpp @@ -2,10 +2,11 @@ /*! \file widget_timeslider.cpp ** \brief Time Slider Widget Implementation File ** -** $Id: widget_timeslider.cpp,v 1.1.1.1 2005/01/07 03:34:37 darco Exp $ +** $Id$ ** ** \legal ** Copyright (c) 2004 Adrian Bentley +** 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 @@ -35,6 +36,8 @@ #include +#include "general.h" + #endif /* === U S I N G =========================================================== */ @@ -87,11 +90,14 @@ Gdk::Color get_interp_color(synfig::Interpolation x) static Gdk::Color color_darken(Gdk::Color x, float amount) { - x.set_rgb_p( - x.get_red_p()*amount, - x.get_green_p()*amount, - x.get_blue_p()*amount - ); + double red = x.get_red_p() * amount; + double green = x.get_green_p() * amount; + double blue = x.get_blue_p() * amount; + + x.set_rgb_p( red > 1 ? 1 : red, + green > 1 ? 1 : green, + blue > 1 ? 1 : blue); + return x; } @@ -105,7 +111,7 @@ studio::render_time_point_to_window( { Glib::RefPtr gc(Gdk::GC::create(window)); const Gdk::Color black("#000000"); - + if(selected) gc->set_line_attributes(2,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER); else @@ -113,7 +119,7 @@ studio::render_time_point_to_window( Gdk::Color color; std::vector points; - + /*- BEFORE ------------------------------------- */ color=get_interp_color(tp.get_before()); @@ -178,7 +184,7 @@ studio::render_time_point_to_window( points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height())); window->draw_polygon(gc,true,points); gc->set_rgb_fg_color(black); - window->draw_lines(gc,points); + window->draw_lines(gc,points); break; case INTERPOLATION_CONSTANT: @@ -191,7 +197,7 @@ studio::render_time_point_to_window( points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height())); window->draw_polygon(gc,true,points); gc->set_rgb_fg_color(black); - window->draw_lines(gc,points); + window->draw_lines(gc,points); break; case INTERPOLATION_UNDEFINED: default: @@ -199,15 +205,15 @@ studio::render_time_point_to_window( points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y())); points.push_back(Gdk::Point(area.get_x()+area.get_width()/3,area.get_y())); points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()/3)); - points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()*2/3)); + points.push_back(Gdk::Point(area.get_x(),area.get_y()+area.get_height()-area.get_height()/3)); points.push_back(Gdk::Point(area.get_x()+area.get_width()/3,area.get_y()+area.get_height())); points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height())); window->draw_polygon(gc,true,points); gc->set_rgb_fg_color(black); - window->draw_lines(gc,points); + window->draw_lines(gc,points); break; } - + /*- AFTER -------------------------------------- */ color=get_interp_color(tp.get_after()); @@ -215,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: @@ -273,7 +278,7 @@ studio::render_time_point_to_window( points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height())); window->draw_polygon(gc,true,points); gc->set_rgb_fg_color(black); - window->draw_lines(gc,points); + window->draw_lines(gc,points); break; case INTERPOLATION_CONSTANT: @@ -281,25 +286,25 @@ studio::render_time_point_to_window( points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y())); points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y())); points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()/2)); - points.push_back(Gdk::Point(area.get_x()+area.get_width()*3/4,area.get_y()+area.get_height()/2)); - points.push_back(Gdk::Point(area.get_x()+area.get_width()*3/4,area.get_y()+area.get_height())); + points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/4,area.get_y()+area.get_height()/2)); + points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/4,area.get_y()+area.get_height())); points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height())); window->draw_polygon(gc,true,points); gc->set_rgb_fg_color(black); - window->draw_lines(gc,points); + window->draw_lines(gc,points); break; case INTERPOLATION_UNDEFINED: default: points.clear(); points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y())); - points.push_back(Gdk::Point(area.get_x()+area.get_width()*2/3,area.get_y())); + points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/3,area.get_y())); points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()/3)); - points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()*2/3)); - points.push_back(Gdk::Point(area.get_x()+area.get_width()*2/3,area.get_y()+area.get_height())); + points.push_back(Gdk::Point(area.get_x()+area.get_width(),area.get_y()+area.get_height()-area.get_height()/3)); + points.push_back(Gdk::Point(area.get_x()+area.get_width()-area.get_width()/3,area.get_y()+area.get_height())); points.push_back(Gdk::Point(area.get_x()+area.get_width()/2,area.get_y()+area.get_height())); window->draw_polygon(gc,true,points); gc->set_rgb_fg_color(black); - window->draw_lines(gc,points); + window->draw_lines(gc,points); break; } @@ -308,7 +313,7 @@ studio::render_time_point_to_window( /* === M E T H O D S ======================================================= */ /* === E N T R Y P O I N T ================================================= */ -double defaultfps = 0; +double defaultfps = 24; const int fullheight = 20; Widget_Timeslider::Widget_Timeslider() @@ -321,11 +326,11 @@ fps(defaultfps), dragscroll(false) { set_size_request(-1,fullheight); - + // click scroll zoom - add_events( Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK + add_events( Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON_MOTION_MASK | Gdk::SCROLL_MASK ); - + set_time_adjustment(&adj_default); //update_times(); } @@ -339,14 +344,14 @@ void Widget_Timeslider::set_time_adjustment(Gtk::Adjustment *x) //disconnect old connections time_value_change.disconnect(); time_other_change.disconnect(); - + //connect update function to new adjustment adj_timescale = x; - + if(x) { time_value_change = x->signal_value_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw)); - time_other_change = x->signal_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw)); + time_other_change = x->signal_changed().connect(sigc::mem_fun(*this,&Widget_Timeslider::queue_draw)); //invalidated = true; //refresh(); } @@ -357,7 +362,7 @@ void Widget_Timeslider::set_global_fps(float d) if(fps != d) { fps = d; - + //update everything since we need to redraw already //invalidated = true; //refresh(); @@ -375,10 +380,10 @@ void Widget_Timeslider::set_global_fps(float d) } }*/ -void Widget_Timeslider::refresh() +void Widget_Timeslider::refresh() { } -/* +/* { if(invalidated) { @@ -388,41 +393,41 @@ void Widget_Timeslider::refresh() double l = adj_timescale->get_lower(), u = adj_timescale->get_upper(), v = adj_timescale->get_value(); - + bool invalid = (l != start) || (u != end) || (v != current); - + start = l; end = u; current = v; - + if(invalid) queue_draw(); } }*/ -bool Widget_Timeslider::redraw(bool doublebuffer) +bool Widget_Timeslider::redraw(bool /*doublebuffer*/) { Glib::RefPtr window = get_window(); - + if(!window) return false; - - Glib::RefPtr gc = Gdk::GC::create(window); + + Glib::RefPtr gc = Gdk::GC::create(window); if(!gc) return false; - + //synfig::info("Drawing Timeslider"); //clear and update to current values //invalidated = false; - //update_times(); - + //update_times(); + //draw grey rectangle Gdk::Color c("#7f7f7f"); gc->set_rgb_fg_color(c); gc->set_background(c); - + //Get the data for the window and the params to draw it... int w = get_width(), h = get_height(); - - window->draw_rectangle(gc,true,0,0,w,h); - + + window->draw_rectangle(gc,true,0,0,w,h); + const double EPSILON = 1e-6; if(!adj_timescale || w == 0) return true; @@ -430,102 +435,118 @@ bool Widget_Timeslider::redraw(bool doublebuffer) double start = adj_timescale->get_lower(), end = adj_timescale->get_upper(), current = adj_timescale->get_value(); - + if(end-start < EPSILON) return true; - + //synfig::info("Drawing Lines"); - + //draw all the time stuff double dtdp = (end - start)/get_width(); double dpdt = 1/dtdp; - + //lines - + //Draw the time line... double tpx = (current-start)*dpdt; gc->set_rgb_fg_color(Gdk::Color("#ffaf00")); window->draw_line(gc,round_to_int(tpx),0,round_to_int(tpx),fullheight); - + //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); - - double lowerrange = dtdp*75, upperrange = dtdp*150; - double midrange = (lowerrange + upperrange)/2; - - //find most ideal scale - double scale = ranges[0]; - { - double *val = binary_find(ranges, ranges+ranges_size, midrange); - double *after = val+1; - - if(val >= ranges+ranges_size) - { - val = ranges+ranges_size-1; - } - - if(after >= ranges+ranges_size) + gc->set_rgb_fg_color(Gdk::Color("#333333")); + + int ifps = round_to_int(fps); + if (ifps < 1) ifps = 1; + + std::vector ranges; + + unsigned int pos = 0; + + // build a list of all the factors of the frame rate + for (int i = 1; i*i <= ifps; i++) + if ((ifps%i) == 0) { - after = 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++; } - - scale = *val; - - double diff = abs(scale - midrange), diff2 = abs(*after - midrange); - if(diff2 < diff) - scale = *after; + + // fill in any gaps where one factor is more than 2 times the previous + std::vector::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); } - - //synfig::info("Range found: (l %.2lf,u %.2lf - m %.2lf) -> %.2lf",lowerrange,upperrange,midrange,scale); - - //search around this area to get the right one - - - //get first valid line and it's position in pixel space + + 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 }; + + 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++; + + if (iter == ranges.end()) iter--; + if (next == ranges.end()) next--; + + if (abs(*next - midrange) < abs(*iter - midrange)) + iter = next; + + scale = *iter; + if (iter != ranges.begin()) iter--; + if (iter != ranges.begin()) iter--; + if (iter != ranges.begin()) iter--; + + // subdivide into this many tick marks (8 or less) + const int subdiv = round_to_int(scale / *iter); + + //get first valid line and its position in pixel space double time = 0; double pixel = 0; - + int sdindex = 0; double subr = scale / subdiv; - - //get it's position inside... + + //get its position inside... time = ceil(start/subr)*subr - start; pixel = time*dpdt; - + //absolute time of the line to be drawn time += start; - + { //inside the big'n 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... - + //synfig::info("Extracted fr %.2lf -> %d", t, sdindex); } - + //synfig::info("Initial values: %.4lf t, %.1lf pixels, %d i", time,pixel,sdindex); - + //loop to draw const int heightbig = 12; const int heightsmall = 4; - + int width = get_width(); while( pixel < width ) { int xpx = round_to_int(pixel); - - //draw big + + //draw big if(sdindex == 0) { window->draw_line(gc,xpx,0,xpx,heightbig); @@ -533,46 +554,45 @@ bool Widget_Timeslider::redraw(bool doublebuffer) Time tm((double)time); if(get_global_fps()) tm.round(get_global_fps()); Glib::ustring timecode(tm.get_string(get_global_fps(),App::get_time_format())); - + //gc->set_rgb_fg_color(Gdk::Color("#000000")); layout->set_text(timecode); window->draw_layout(gc,xpx+2,heightsmall,layout); }else { - window->draw_line(gc,xpx,0,xpx,heightsmall); + window->draw_line(gc,xpx,0,xpx,heightsmall); } - + //increment time and position pixel += subr / dtdp; time += subr; - + //increment index if(++sdindex >= subdiv) sdindex -= subdiv; } - + return true; } bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for dragging -{ +{ if(!adj_timescale) return false; - + Gdk::ModifierType mod = Gdk::ModifierType(event->state); - + //scrolling... - + //NOTE: we might want to address the possibility of dragging with both buttons held down - + if(mod & Gdk::BUTTON2_MASK) { //we need this for scrolling by dragging double curx = event->x; - + double start = adj_timescale->get_lower(), end = adj_timescale->get_upper(); - - + if(dragscroll) { if(event->time-last_event_time<30) @@ -582,25 +602,25 @@ bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for drag if(abs(lastx - curx) < 1 && end != start) return true; //translate the window and correct it - + //update our stuff so we are operating correctly //invalidated = true; //update_times(); - + //Note: Use inverse of mouse movement because of conceptual space relationship double diff = lastx - curx; //curx - lastx; - + //NOTE: This might be incorrect... //fraction to move... double dpx = (end - start)/get_width(); lastx = curx; - + diff *= dpx; - + //Adjust... start += diff; end += diff; - + //But clamp to bounds if they exist... //HACK - bounds should not be required for this slider if(adj_bounds) @@ -611,17 +631,17 @@ bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for drag start += diff; end += diff; } - + if(end > adj_bounds->get_upper()) { diff = adj_bounds->get_upper() - end; start += diff; end += diff; } - } - + } + //synfig::info("Scrolling timerange to (%.4f,%.4f)",start,end); - + adj_timescale->set_lower(start); adj_timescale->set_upper(end); @@ -632,31 +652,31 @@ bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for drag lastx = curx; //lasty = cury; } - - return true; + + return true; } - + if(mod & Gdk::BUTTON1_MASK) { double curx = event->x; - + //get time from drag... double start = adj_timescale->get_lower(), end = adj_timescale->get_upper(), current = adj_timescale->get_value(); double t = start + curx*(end - start)/get_width(); - + //snap it to fps - if they exist... if(fps) { t = floor(t*fps + 0.5)/fps; } - + //set time if needed if(current != t) - { + { adj_timescale->set_value(t); - + //Fixed this to actually do what it's supposed to... if(event->time-last_event_time>50) { @@ -664,20 +684,20 @@ bool Widget_Timeslider::on_motion_notify_event(GdkEventMotion* event) //for drag last_event_time = event->time; } } - + return true; } - + return false; } bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming { if(!adj_timescale) return false; - + //Update so we are calculating based on current values - //update_times(); - + //update_times(); + //figure out if we should center ourselves on the current time bool center = false; @@ -686,23 +706,58 @@ bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming { 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 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; + + if( event->direction == GDK_SCROLL_RIGHT ) + t += adj; + else + 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); + } + + if(adj_timescale) + { + adj_timescale->set_value(t); + adj_timescale->value_changed(); + } return true; } - default: + default: { return false; } @@ -712,17 +767,17 @@ bool Widget_Timeslider::on_scroll_event(GdkEventScroll* event) //for zooming void Widget_Timeslider::zoom_in(bool centerontime) { if(!adj_timescale) return; - + double start = adj_timescale->get_lower(), end = adj_timescale->get_upper(), current = adj_timescale->get_value(); - + double focuspoint = centerontime ? current : (start + end)/2; - + //calculate new beginning and end end = focuspoint + (end-focuspoint)*zoominfactor; start = focuspoint + (start-focuspoint)*zoominfactor; - + //synfig::info("Zooming in timerange to (%.4f,%.4f)",start,end); if(adj_bounds) { @@ -730,17 +785,17 @@ void Widget_Timeslider::zoom_in(bool centerontime) { start = adj_bounds->get_lower(); } - + if(end > adj_bounds->get_upper()) { end = adj_bounds->get_upper(); } } - + //reset values adj_timescale->set_lower(start); adj_timescale->set_upper(end); - + //call changed function adj_timescale->changed(); } @@ -748,17 +803,17 @@ void Widget_Timeslider::zoom_in(bool centerontime) void Widget_Timeslider::zoom_out(bool centerontime) { if(!adj_timescale) return; - + double start = adj_timescale->get_lower(), end = adj_timescale->get_upper(), current = adj_timescale->get_value(); - + double focuspoint = centerontime ? current : (start + end)/2; - + //calculate new beginning and end end = focuspoint + (end-focuspoint)*zoomoutfactor; start = focuspoint + (start-focuspoint)*zoomoutfactor; - + //synfig::info("Zooming out timerange to (%.4f,%.4f)",start,end); if(adj_bounds) { @@ -766,17 +821,17 @@ void Widget_Timeslider::zoom_out(bool centerontime) { start = adj_bounds->get_lower(); } - + if(end > adj_bounds->get_upper()) { - end = adj_bounds->get_upper(); + end = adj_bounds->get_upper(); } } - + //reset values adj_timescale->set_lower(start); adj_timescale->set_upper(end); - + //call changed function adj_timescale->changed(); } @@ -791,29 +846,29 @@ bool Widget_Timeslider::on_button_press_event(GdkEventButton *event) //for click double start = adj_timescale->get_lower(), end = adj_timescale->get_upper(), current = adj_timescale->get_value(); - + double w = get_width(); double t = start + (end - start) * event->x / w; - + t = floor(t*fps + 0.5)/fps; - - /*synfig::info("Clicking time from %.3lf to %.3lf [(%.2lf,%.2lf) %.2lf / %.2lf ... %.2lf", + + /*synfig::info("Clicking time from %.3lf to %.3lf [(%.2lf,%.2lf) %.2lf / %.2lf ... %.2lf", current, vt, start, end, event->x, w, fps);*/ - + if(t != current) - { + { current = t; - + if(adj_timescale) { adj_timescale->set_value(current); adj_timescale->value_changed(); } } - + break; } - + //scroll click case 2: { @@ -821,35 +876,35 @@ bool Widget_Timeslider::on_button_press_event(GdkEventButton *event) //for click dragscroll = true; lastx = event->x; //lasty = event->y; - + return true; } - + default: { break; } } - + return false; } bool Widget_Timeslider::on_button_release_event(GdkEventButton *event) //end drag -{ +{ switch(event->button) - { + { case 2: { //start dragging dragscroll = false; return true; } - + default: { break; } } - + return false; }