/* === S Y N F I G ========================================================= */
/*! \file widget_canvastimeslider.cpp
** \brief Canvas Time Slider Widget Implementation File
**
** $Id$
**
** \legal
** ......... ... 2018 Ivan Mahonin
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
** published by the Free Software Foundation; either version 2 of
** the License, or (at your option) any later version.
**
** This package is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
** \endlegal
*/
/* ========================================================================= */
/* === H E A D E R S ======================================================= */
#ifdef USING_PCH
# include "pch.h"
#else
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <cmath>
#include <ETL/misc>
#include <synfig/general.h>
#include <gui/localization.h>
#include <gui/canvasview.h>
#include <gui/workarea.h>
#include <gui/workarearenderer/renderer_canvas.h>
#include "widget_canvastimeslider.h"
#include "gui/timeplotdata.h"
#endif
/* === U S I N G =========================================================== */
using namespace synfig;
using namespace studio;
/* === M A C R O S ========================================================= */
/* === G L O B A L S ======================================================= */
/* === P R O C E D U R E S ================================================= */
/* === M E T H O D S ======================================================= */
/* === E N T R Y P O I N T ================================================= */
Widget_CanvasTimeslider::Widget_CanvasTimeslider():
tooltip(Gtk::WINDOW_POPUP)
{
thumb.signal_draw().connect(sigc::mem_fun(*this, &Widget_CanvasTimeslider::draw_thumb));
thumb.show();
tooltip.set_type_hint(Gdk::WINDOW_TYPE_HINT_TOOLTIP);
tooltip.set_border_width(1);
tooltip.add(thumb);
add_events(Gdk::POINTER_MOTION_MASK | Gdk::LEAVE_NOTIFY_MASK);
time_plot_data->set_extra_time_margin(2.0);
}
Widget_CanvasTimeslider::~Widget_CanvasTimeslider()
{
rendering_change.disconnect();
}
void
Widget_CanvasTimeslider::set_canvas_view(const CanvasView::LooseHandle &x)
{
if (canvas_view == x) return;
rendering_change.disconnect();
lock_ducks.reset();
canvas_view = x;
set_time_model(canvas_view ? canvas_view->time_model() : etl::handle<TimeModel>());
if (canvas_view && canvas_view->get_work_area())
rendering_change = canvas_view->get_work_area()->signal_rendering().connect(
sigc::mem_fun(*this, &Widget_CanvasTimeslider::queue_draw) );
queue_draw();
}
void
Widget_CanvasTimeslider::show_tooltip(const synfig::Point &p, const synfig::Point &root)
{
thumb_background.clear();
thumb_surface.clear();
Cairo::RefPtr<Cairo::SurfacePattern> pattern;
Cairo::RefPtr<Cairo::ImageSurface> surface;
if ( get_width()
&& time_plot_data->time_model
&& canvas_view
&& canvas_view->get_canvas()
&& canvas_view->get_work_area()
&& canvas_view->get_work_area()->get_renderer_canvas() )
{
double x = p[0];
if (!time_plot_data->is_invalid()) {
synfig::Time time = time_plot_data->get_t_from_pixel_coord(x);
time = time_plot_data->time_model->round_time(time);
surface = canvas_view->get_work_area()->get_renderer_canvas()->get_thumb(time);
pattern = canvas_view->get_work_area()->get_background_pattern();
}
}
if (pattern && surface && get_screen() && get_width() > 0) {
const int space = 20;
int tooltip_w = surface->get_width();
int tooltip_h = surface->get_height();
int screen_h = get_screen()->get_height();
int w = get_width();
int h = get_height();
int left = (int)round(root[0] - p[0]);
int top = (int)round(root[1] - p[1]);
int x = left;
if (w > tooltip_w)
x += (int)round(p[0]/(double)w*(w - tooltip_w));
int y = 0;
bool visible = false;
if (top > 2*space + tooltip_h) {
y = top - space - tooltip_h;
visible = true;
} else
if (screen_h - top - h > 2*space + tooltip_h) {
y = top + h + space;
visible = true;
}
if (visible) {
thumb_background = pattern;
thumb_surface = surface;
thumb.set_size_request(thumb_surface->get_width(), thumb_surface->get_height());
thumb.queue_draw();
tooltip.set_screen(get_screen());
tooltip.move(x, y);
tooltip.show();
} else tooltip.hide();
} else tooltip.hide();
}
bool
Widget_CanvasTimeslider::on_button_press_event(GdkEventButton *event)
{
if (event->button == 1 && canvas_view && canvas_view->get_work_area()) {
lock_ducks = new LockDucks(etl::handle<CanvasView>(canvas_view));
canvas_view->get_work_area()->clear_ducks();
canvas_view->queue_rebuild_ducks();
}
//Clicking on the timeline while the animation is playing should stop the playback #415
if (canvas_view->is_playing()) canvas_view->stop_async();
if (event->button == 1 || event->button == 2)
tooltip.hide();
return Widget_Timeslider::on_button_press_event(event);
}
bool
Widget_CanvasTimeslider::on_button_release_event(GdkEventButton *event)
{
if (event->button == 1 && canvas_view)
lock_ducks.reset();
//if ( (event->button == 1 && !(event->state & Gdk::BUTTON2_MASK))
// || (event->button == 2 && !(event->state & Gdk::BUTTON1_MASK)) )
// show_tooltip(Point(event->x, event->y), Point(event->x_root, event->y_root));
return Widget_Timeslider::on_button_release_event(event);
}
bool
Widget_CanvasTimeslider::on_motion_notify_event(GdkEventMotion* event)
{
if ((event->state & (Gdk::BUTTON1_MASK | Gdk::BUTTON2_MASK)) == 0)
show_tooltip(Point(event->x, event->y), Point(event->x_root, event->y_root));
return Widget_Timeslider::on_motion_notify_event(event);
}
bool
Widget_CanvasTimeslider::on_leave_notify_event(GdkEventCrossing*)
{
tooltip.hide();
return true;
}
bool
Widget_CanvasTimeslider::draw_thumb(const Cairo::RefPtr<Cairo::Context> &cr)
{
if (!thumb_background || !thumb_surface)
return false;
cr->save();
cr->translate(tooltip.get_width()/2, tooltip.get_height()/2);
cr->set_source(thumb_background);
cr->paint();
cr->restore();
cr->save();
cr->set_source(thumb_surface, 0, 0);
cr->paint();
cr->restore();
return true;
}
void
Widget_CanvasTimeslider::draw_background(const Cairo::RefPtr<Cairo::Context> &cr)
{
Widget_Timeslider::draw_background(cr);
if (!time_plot_data->time_model || !canvas_view || !canvas_view->get_work_area()) return;
Renderer_Canvas::Handle renderer_canvas = canvas_view->get_work_area()->get_renderer_canvas();
if (!renderer_canvas) return;
int w = get_width(), height = get_height();
if (w <= 0 || height <= 0) return;
Time frame_duration = time_plot_data->time_model->get_frame_duration();
if (time_plot_data->is_invalid()) return;
double top = 0.0;
double width = frame_duration*time_plot_data->k + 1.0;
Renderer_Canvas::StatusMap status_map;
renderer_canvas->get_render_status(status_map);
for(Renderer_Canvas::StatusMap::const_iterator i = status_map.begin(); i != status_map.end(); ++i) {
if (i->first < time_plot_data->lower_ex - frame_duration|| i->first > time_plot_data->upper_ex) continue;
cr->save();
switch(i->second) {
case Renderer_Canvas::FS_PartiallyDone : cr->set_source_rgba(0.4, 0.4, 0.4, 1.0); break;
case Renderer_Canvas::FS_InProcess : cr->set_source_rgba(1.0, 1.0, 0.0, 1.0); break;
case Renderer_Canvas::FS_Done : cr->set_source_rgba(0.0, 0.8, 0.0, 1.0); break;
default : cr->set_source_rgba(0.0, 0.0, 0.0, 0.0); break;
}
cr->rectangle(time_plot_data->get_pixel_t_coord(i->first), top, width, height);
cr->fill();
cr->restore();
}
}