source: synfigstudio/trunk/fuentes/src/gui/widgets/widget_timeslider.cpp @ 481

Last change on this file since 481 was 481, checked in by jrpelegrina, 4 years ago

First release to xenial

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