/* veejay - Linux VeeJay * (C) 2002-2004 Niels Elburg * * * This program 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 program 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Implements a new slider type widget with selection markers */ #include #include #include #include #include #include "gtktimeselection.h" enum { POS_CHANGED, IN_CHANGED, OUT_CHANGED, BIND_CHANGED, CLEAR_CHANGED, SELECTION_CHANGED_SIGNAL, LAST_SIGNAL }; enum { MIN = 1, MAX = 2, POS = 3, LENGTH = 4, IN_POINT = 5, OUT_POINT = 6, SEL = 7, BIND = 8, CLEARED = 9, }; typedef enum { MOUSE_OUTSIDE, MOUSE_STEPPER, MOUSE_SELECTION, MOUSE_WIDGET /* inside widget but not in any of the above */ } mouse_location; /* Slider with 2 bars */ typedef enum TimelineAction { action_none = 0, action_in_point, action_out_point, action_pos, action_atomic, } TimelineAction; #define POINT_IN_RECT(xcoord, ycoord, rect) \ ((xcoord) >= (rect).x && \ (xcoord) < ((rect).x + (rect).width) && \ (ycoord) >= (rect).y && \ (ycoord) < ((rect).y + (rect).height)) struct _TimelineSelection { GtkCairo cr; GtkWidget *widget; gdouble min; gdouble max; gdouble value; gdouble frame_num; gdouble num_video_frames; gdouble in; gdouble out; gboolean bind; gint grab_button; TimelineAction action; mouse_location grab_location; mouse_location current_location; GdkRectangle stepper; GdkRectangle selection; gboolean has_stepper; gboolean clear; gdouble stepper_size; /* size of triangle */ gdouble stepper_draw_size; gdouble stepper_length; /* length from top to bottom */ gint step_size; /* step frames 1,2,4,8,16, ... */ gdouble frame_width; gdouble frame_height; gdouble font_line; gboolean has_selection; /* use in/out points for selection */ gdouble move_x; }; static void get_property( GObject *object, guint id, GValue *value , GParamSpec *pspec ); static void set_property (GObject *object, guint id, GValue * value, GParamSpec *pspec); static gboolean event_press (GtkWidget *widget, GdkEventButton *bev, gpointer user_data); static gboolean event_release (GtkWidget *widget, GdkEventButton *bev, gpointer user_data); static gboolean event_motion (GtkWidget *widget, GdkEventMotion *mev, gpointer user_data); static void timeline_class_init( TimelineSelectionClass *class ); static void timeline_init(TimelineSelection *te ); static void paint(GtkWidget *widget, cairo_t *cr, gpointer user_data); static GObjectClass *parent_class = NULL; static GtkActionGroup *action_group = NULL; static gint timeline_signals[LAST_SIGNAL] = { 0 }; struct _TimelineSelectionClass { GtkCairoClass parent_class; void (*pos_changed) (TimelineSelection *te); void (*in_point_changed) (TimelineSelection *te); void (*out_point_changed) (TimelineSelection *te); void (*bind_toggled) (TimelineSelection *te); void (*cleared) (TimelineSelection *te); }; static void set_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { TimelineSelection *te = TIMELINE_SELECTION(object); switch(id) { case MIN: if(te->min != g_value_get_double(value)) { te->min = g_value_get_double(value); } break; case MAX: if(te->max != g_value_get_double(value)) { te->min = g_value_get_double(value); } break; case POS: if(te->frame_num != g_value_get_double(value)) { te->frame_num = g_value_get_double(value); } break; case LENGTH: if(te->num_video_frames != g_value_get_double(value)) { te->num_video_frames = g_value_get_double(value); } break; case IN_POINT: if(te->in != g_value_get_double(value)) { te->in = g_value_get_double(value); } break; case OUT_POINT: if(te->out != g_value_get_double(value)) { te->out = g_value_get_double(value); } break; case SEL: if(te->has_selection != g_value_get_boolean(value)) { te->has_selection = g_value_get_boolean(value); } break; case BIND: if(te->bind != g_value_get_boolean(value)) { te->bind = g_value_get_boolean(value); } break; case CLEARED: if(te->clear != g_value_get_boolean(value)) { te->clear = g_value_get_boolean(value); } break; default: g_assert(FALSE); break; } gtk_widget_queue_draw( GTK_WIDGET( te )); } static void get_property( GObject *object, guint id, GValue *value , GParamSpec *pspec ) { TimelineSelection *te = TIMELINE_SELECTION(object); switch( id ) { case MIN: g_value_set_double( value, te->min );break; case MAX: g_value_set_double( value, te->max );break; case POS: g_value_set_double( value, te->frame_num ); break; case LENGTH: g_value_set_double( value, te->num_video_frames ); break; case IN_POINT: g_value_set_double( value, te->in ); break; case OUT_POINT: g_value_set_double( value, te->out ); break; case SEL: g_value_set_boolean(value, te->has_selection) ; break; case BIND: g_value_set_boolean(value, te->bind ); break; case CLEARED: g_value_set_boolean(value,te->clear );break; } } static void finalize (GObject *object) { parent_class->finalize( object ); } static void timeline_class_init( TimelineSelectionClass *class ) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS( class ); parent_class = g_type_class_peek( GTK_TYPE_CAIRO ); gobject_class->finalize = finalize; gobject_class->get_property = get_property; gobject_class->set_property = set_property; g_object_class_install_property( gobject_class, MIN, g_param_spec_double( "min", "left", "left", 0.0, 1.0, 0.0, G_PARAM_READWRITE )); g_object_class_install_property( gobject_class, MAX, g_param_spec_double( "max", "right", "right", 0.0, 1.0, 1.0, G_PARAM_READWRITE )); g_object_class_install_property( gobject_class, POS, g_param_spec_double( "pos", "current position", "current position", 0.0,9999999.0, 0.0, G_PARAM_READWRITE )); g_object_class_install_property( gobject_class, LENGTH, g_param_spec_double( "length", "Length (in frames)", "Length (in frames) ",0.0,9999999.0, 1.0, G_PARAM_READWRITE )); g_object_class_install_property( gobject_class, IN_POINT, g_param_spec_double( "in", "In point", "(in frames) ",0.0,1.0, 0.0, G_PARAM_READWRITE )); g_object_class_install_property( gobject_class, OUT_POINT, g_param_spec_double( "out", "Out point", "(in frames) ",0.0,1.0, 1.0, G_PARAM_READWRITE )); g_object_class_install_property( gobject_class, SEL, g_param_spec_boolean( "selection", "Marker", "(in frames) ",FALSE, G_PARAM_READWRITE )); g_object_class_install_property( gobject_class, BIND, g_param_spec_boolean( "bind", "Bind marker", "Bind In/Out points", FALSE, G_PARAM_READWRITE)); g_object_class_install_property( gobject_class, CLEARED, g_param_spec_boolean( "clear", "Clear marker", "Clear in/out points", FALSE, G_PARAM_READWRITE )); timeline_signals[ SELECTION_CHANGED_SIGNAL ] = g_signal_new( "selection_changed", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_FIRST,0,NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 , NULL ); timeline_signals[ POS_CHANGED ] = g_signal_new( "pos_changed", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( TimelineSelectionClass, pos_changed ), NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); timeline_signals[ IN_CHANGED ] = g_signal_new( "in_point_changed", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( TimelineSelectionClass, in_point_changed ), NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); timeline_signals[ OUT_CHANGED ] = g_signal_new( "out_point_changed", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( TimelineSelectionClass, out_point_changed ), NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); timeline_signals[ CLEAR_CHANGED ] = g_signal_new( "cleared", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( TimelineSelectionClass, cleared ), NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL ); timeline_signals[ BIND_CHANGED ] = g_signal_new( "bind_toggled", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( TimelineSelectionClass, bind_toggled ), NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL ); } static int default_theme_ = 1; void timeline_theme_colors( int inverse ) { default_theme_ = inverse; } static void timeline_init( TimelineSelection *te ) { te->min = 0.0; te->max = 0.0; te->action = action_none; te->in = 0.0; te->out = 1.0; te->num_video_frames = 1.0; te->frame_num = 0.0; te->grab_location = MOUSE_OUTSIDE; te->current_location = MOUSE_OUTSIDE; te->grab_button = 0; te->has_stepper = TRUE; te->has_selection = FALSE; te->stepper_size = 16; // 8 x 8 pixels te->stepper_draw_size = 12; te->stepper_length = 0; te->frame_height = 10; te->font_line = 12; te->move_x = 0; } GType timeline_get_type(void) { static GType gtype = 0; if(!gtype) { static const GTypeInfo ginfo = { sizeof( TimelineSelectionClass), NULL, NULL, (GClassInitFunc) timeline_class_init, NULL, NULL, sizeof(TimelineSelection), 0, (GInstanceInitFunc) timeline_init, NULL }; gtype = g_type_register_static( GTK_TYPE_CAIRO, "Timeline", &ginfo, 0 ); } return gtype; } gdouble timeline_get_in_point( TimelineSelection *te ) { gdouble result = 0.0; g_object_get( G_OBJECT(te), "in", &result, NULL ); return result; } gdouble timeline_get_out_point( TimelineSelection *te ) { gdouble result = 0.0; g_object_get( G_OBJECT(te), "out", &result, NULL ); return result; } gboolean timeline_get_selection( TimelineSelection *te ) { gboolean result = FALSE; g_object_get( G_OBJECT(te), "selection", &result, NULL ); return result; } gboolean timeline_get_bind( TimelineSelection *te ) { gboolean result = FALSE; g_object_get( G_OBJECT(te), "bind", &result, NULL ); return result; } void timeline_set_bind(GtkWidget *widget, gboolean active) { TimelineSelection *te = TIMELINE_SELECTION(widget); g_object_set( G_OBJECT(te), "bind", active, NULL ); g_signal_emit( te->widget, timeline_signals[BIND_CHANGED], 0); } void timeline_set_out_point( GtkWidget *widget, gdouble pos ) { TimelineSelection *te = TIMELINE_SELECTION(widget); g_object_set( G_OBJECT(te), "out", pos, NULL ); g_signal_emit(te->widget, timeline_signals[OUT_CHANGED], 0); gtk_widget_queue_draw( GTK_WIDGET(te->widget) ); } void timeline_clear_points( GtkWidget *widget ) { gboolean cleared = TRUE; gdouble pos = 0.0; gdouble pos2 = 1.0; TimelineSelection *te = TIMELINE_SELECTION(widget); g_object_set( G_OBJECT(te), "clear", cleared, NULL ); g_object_set( G_OBJECT(te), "in", pos, NULL ); g_object_set( G_OBJECT(te), "out", pos2, NULL ); g_signal_emit(te->widget, timeline_signals[CLEAR_CHANGED], 0 ); gtk_widget_queue_draw(GTK_WIDGET(te->widget) ); } void timeline_set_in_point( GtkWidget *widget, gdouble pos ) { TimelineSelection *te = TIMELINE_SELECTION(widget); g_object_set( G_OBJECT(te), "in", pos, NULL ); g_signal_emit(te->widget, timeline_signals[IN_CHANGED], 0); gtk_widget_queue_draw( GTK_WIDGET(te->widget) ); } void timeline_set_selection( GtkWidget *widget, gboolean active) { TimelineSelection *te = TIMELINE_SELECTION(widget); g_object_set( G_OBJECT(te), "selection", active, NULL ); gtk_widget_queue_draw( GTK_WIDGET(te->widget) ); } void timeline_set_length( GtkWidget *widget, gdouble length, gdouble pos) { TimelineSelection *te = TIMELINE_SELECTION( widget ); g_object_set( G_OBJECT(te), "length", length, NULL ); timeline_set_pos( GTK_WIDGET(te->widget), pos ); } void timeline_set_pos( GtkWidget *widget,gdouble pos) { TimelineSelection *te = TIMELINE_SELECTION( widget ); g_object_set( G_OBJECT(te), "pos", pos, NULL ); g_signal_emit( te->widget, timeline_signals[POS_CHANGED], 0); gtk_widget_queue_draw( GTK_WIDGET(te->widget) ); } gdouble timeline_get_pos( TimelineSelection *te ) { gdouble result = 0.0; g_object_get( G_OBJECT(te), "pos", &result, NULL ); return result; } gdouble timeline_get_length( TimelineSelection *te ) { gdouble result = 0.0; g_object_get( G_OBJECT(te), "length", &result, NULL ); return result; } static void move_selection( GtkWidget *widget, gdouble x, gdouble width ) { TimelineSelection *te = TIMELINE_SELECTION( widget ); gdouble dx3 = (0.5 * (te->out - te->in)) * width; gdouble dx1 = x - dx3; gdouble dx2 = x + dx3; te->in = (1.0/width) * dx1; te->out = (1.0/width ) * dx2; if(te->in < 0.0 ) te->in = 0.0; else if (te->in > 1.0) te->in = 1.0; if(te->out < 0.0 ) te->out = 0.0; else if (te->out > 1.0) te->out = 1.0; timeline_set_out_point(widget, te->out ); timeline_set_in_point(widget, te->in ); te->move_x = x; } static gboolean event_press(GtkWidget *widget, GdkEventButton *ev, gpointer user_data) { TimelineSelection *te = TIMELINE_SELECTION( widget ); gdouble width = widget->allocation.width; te->grab_button = ev->button; te->current_location = MOUSE_WIDGET; if( ev->type == GDK_2BUTTON_PRESS && te->grab_button == 1 ) { timeline_clear_points( widget ); return FALSE; } if(te->grab_button == 1 && POINT_IN_RECT( ev->x, ev->y, te->stepper ) ) { if(te->has_stepper) { te->current_location = MOUSE_STEPPER; te->action = action_pos; } return FALSE; } if(te->grab_button == 1 && te->has_selection) { if( POINT_IN_RECT( ev->x, ev->y, te->selection ) && te->bind ) { te->current_location = MOUSE_SELECTION; } if(!te->bind) { gdouble val = (1.0 / width) * ev->x; timeline_set_in_point( widget, val ); } } else if(te->grab_button == 3 && te->has_selection ) { if( POINT_IN_RECT( ev->x, ev->y, te->selection ) && te->bind ) { te->current_location = MOUSE_SELECTION; } if(!te->bind) { gdouble val = (1.0/width) * ev->x; timeline_set_out_point( widget, val ); } } else if(te->grab_button == 2 && te->has_selection) { gint dx = ev->x; gint dy = ev->y; if( POINT_IN_RECT( dx, dy, te->selection ) ) { timeline_set_bind( widget, (te->bind ? FALSE: TRUE )); te->move_x = (gdouble) ev->x; } } gtk_widget_queue_draw( widget ); return FALSE; } static gboolean event_release (GtkWidget *widget, GdkEventButton *ev, gpointer user_data) { TimelineSelection *te = TIMELINE_SELECTION (widget); te->action = action_none; te->current_location = MOUSE_WIDGET; // te->grab_button = 0; // te->move_x = 0; return FALSE; } static gboolean event_motion (GtkWidget *widget, GdkEventMotion *ev, gpointer user_data) { TimelineSelection *te = TIMELINE_SELECTION (widget); gdouble width = (gdouble) widget->allocation.width; gint x,y; GdkModifierType state; gdk_window_get_pointer( ev->window, &x,&y,&state ); if( te->has_stepper && te->current_location == MOUSE_STEPPER && ev->state & GDK_BUTTON1_MASK) { gdouble rel_pos = ((gdouble)ev->x / width) * te->num_video_frames; gdouble new_pos = (gdouble) ((gint) rel_pos ); timeline_set_pos( widget, new_pos ); return FALSE; } if( te->has_selection && te->current_location != MOUSE_STEPPER) { if(!te->bind) { gdouble gx = (1.0 / width) * x; if(te->grab_button == 1 && ev->state & GDK_BUTTON1_MASK) timeline_set_in_point(widget, gx ); else if(te->grab_button == 3 && ev->state & GDK_BUTTON3_MASK) timeline_set_out_point( widget, gx ); } } if(te->has_selection && te->bind && te->grab_button == 2 ) move_selection( widget, x, width ); gtk_widget_queue_draw( widget ); return FALSE; } /* draw a rounded rectangle */ void cairo_rectangle_round (cairo_t * cr, double x0, double y0, double width, double height, double radius) { double x1, y1; x1 = x0 + width; y1 = y0 + height; if (width <= 0.001 || height <= 0.001) return; if (width / 2 < radius) { if (height / 2 < radius) { cairo_move_to (cr, x0, (y0 + y1) / 2); cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0); cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); } else { cairo_move_to (cr, x0, y0 + radius); cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0); cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); cairo_line_to (cr, x1, y1 - radius); cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); } } else { if (height / 2 < radius) { cairo_move_to (cr, x0, (y0 + y1) / 2); cairo_curve_to (cr, x0, y0, x0, y0, x0 + radius, y0); cairo_line_to (cr, x1 - radius, y0); cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); cairo_line_to (cr, x0 + radius, y1); cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); } else { cairo_move_to (cr, x0, y0 + radius); cairo_curve_to (cr, x0, y0, x0, y0, x0 + radius, y0); cairo_line_to (cr, x1 - radius, y0); cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); cairo_line_to (cr, x1, y1 - radius); cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); cairo_line_to (cr, x0 + radius, y1); cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); } } cairo_close_path (cr); } static void paint (GtkWidget *widget, cairo_t * cr, gpointer user_data) { GtkStyle *style = widget->style; TimelineSelection *te = TIMELINE_SELECTION( widget ); double width = widget->allocation.width; double height = widget->allocation.height; gdouble marker_width = width/ te->num_video_frames; gdouble frame_width = marker_width * 0.9; // gdouble marker_height = height / te->num_video_frames; gdouble marker_height = te->frame_height; gint i; te->frame_width = marker_width; cairo_save(cr); cairo_identity_matrix(cr); /* Draw frames*/ /* Drawing many boxes cause CPU hog .. */ /* cairo_set_source_rgba( cr, 0.0, 0.0, 0.0, 0.2 ); for(i =0; i < te->num_video_frames; i ++ ) { double x1 = marker_width * i; double x2 = x1 + frame_width; cairo_rectangle_round(cr, x1, height - te->stepper_size, frame_width, marker_height, 4); } cairo_stroke(cr); */ // cairo_fill(cr); /* Draw stepper */ if( te->has_stepper ) { cairo_set_source_rgba( cr, 1.0,0.0,0.0,1.0); double x1 = marker_width * te->frame_num; double x2 = x1 + frame_width; te->stepper.x = x1 - 8; te->stepper.y = 0; te->stepper.width = te->stepper_size + 8; te->stepper.height = te->stepper_size + 2; // cairo_set_line_width( cr, 0.16 ); cairo_move_to( cr, x1 - te->stepper_draw_size, 0.0 * height ); cairo_rel_line_to( cr, te->stepper_draw_size, te->stepper_draw_size ); cairo_rel_line_to( cr, te->stepper_draw_size, -te->stepper_draw_size ); cairo_rel_line_to( cr, -2.0 * te->stepper_draw_size, 0 ); cairo_set_line_join( cr, CAIRO_LINE_JOIN_MITER); cairo_move_to(cr, x1, te->stepper_draw_size ); cairo_rel_line_to( cr, 0.0, te->stepper_length ); cairo_stroke(cr); //cairo_fill_preserve(cr); if( te->grab_button == 1 && te->current_location == MOUSE_STEPPER ) { cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD ); cairo_move_to(cr, x1 + (te->stepper_size * 0.5),te->font_line ); gchar text[40]; sprintf(text, "%d", (gint)te->frame_num ); cairo_text_path( cr, text ); cairo_set_font_size( cr, 0.2 ); double v = ( default_theme_ ? 1.0 : 0.0 ); cairo_set_source_rgba( cr, v,v,v,0.7 ); cairo_fill(cr); } //cairo_fill_preserve(cr); } /* Draw selection */ if( te->has_selection ) { gdouble in = te->in * width; gdouble out = te->out * width; gdouble v = (default_theme_ ? 1.0: 0.0); /* If user is editing in_point */ if( te->grab_button == 1 && te->current_location != MOUSE_STEPPER ) { gdouble f = te->in * te->num_video_frames; cairo_set_source_rgba( cr, 0.0, v,v,0.3 ); cairo_move_to( cr, in, 0.0 ); cairo_rel_line_to( cr, 0.0 , te->stepper_length ); cairo_stroke(cr); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD ); cairo_move_to(cr, in , te->font_line ); gchar text[40]; sprintf(text, "%d",(gint) f ); cairo_text_path( cr, text ); cairo_set_font_size( cr, 0.2 ); cairo_set_source_rgba( cr, v,v,v,0.7 ); cairo_fill(cr); } if( te->grab_button == 3 && te->current_location != MOUSE_STEPPER ) { gdouble f = te->out * te->num_video_frames; cairo_set_source_rgba( cr, 0.0, v,v,0.3 ); cairo_move_to( cr, out , 0.0 ); cairo_rel_line_to( cr, 0.0 , te->stepper_length ); cairo_stroke(cr); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD ); cairo_move_to(cr, out ,te->font_line ); gchar text[40]; sprintf(text, "%d", (gint) f ); cairo_text_path( cr, text ); cairo_set_font_size( cr, 0.2 ); cairo_set_source_rgba( cr, v,v,v,0.7 ); cairo_fill(cr); } cairo_set_source_rgba( cr, v, v, v, 0.3 ); cairo_rectangle_round(cr, in, 0.095 * height, (out - in), marker_height, 10); te->selection.x = in; te->selection.y = 0; te->selection.width = out; te->selection.height = te->font_line; cairo_fill_preserve(cr); } cairo_restore(cr); } GtkWidget *timeline_new(void) { GtkWidget *widget = GTK_WIDGET( g_object_new( timeline_get_type(), NULL )); TimelineSelection *te = TIMELINE_SELECTION( widget ); gtk_widget_set_size_request(widget, 200,16 ); gtk_widget_set_events( widget, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON3_MOTION_MASK | GDK_2BUTTON_PRESS ); g_signal_connect( G_OBJECT(widget), "paint", G_CALLBACK(paint), NULL ); g_signal_connect( G_OBJECT(widget), "motion_notify_event", G_CALLBACK(event_motion), NULL ); g_signal_connect( G_OBJECT(widget), "button_press_event", G_CALLBACK(event_press), NULL ); g_signal_connect( G_OBJECT(widget), "button_release_event", G_CALLBACK(event_release), NULL ); te->widget = widget; return widget; }