2 * Barely started curve editor widget. Standard GtkCurve is
3 * unreliable and deprecated, so I need to make my own.
5 * Copyright (C) 2008 Krzysztof Foltman
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
22 #include <calf/ctl_curve.h>
27 static gpointer parent_class
= NULL
;
30 calf_curve_new(unsigned int point_limit
)
32 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_CURVE
, NULL
));
33 g_assert(CALF_IS_CURVE(widget
));
35 CalfCurve
*self
= CALF_CURVE(widget
);
36 self
->point_limit
= point_limit
;
41 calf_curve_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
43 g_assert(CALF_IS_CURVE(widget
));
45 CalfCurve
*self
= CALF_CURVE(widget
);
46 GdkWindow
*window
= widget
->window
;
47 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(window
));
48 GdkColor scHot
= { 0, 65535, 0, 0 };
49 GdkColor scPoint
= { 0, 65535, 65535, 65535 };
50 GdkColor scLine
= { 0, 32767, 32767, 32767 };
51 if (self
->points
->size())
53 gdk_cairo_set_source_color(c
, &scLine
);
54 for (size_t i
= 0; i
< self
->points
->size(); i
++)
56 const CalfCurve::point
&pt
= (*self
->points
)[i
];
57 if (i
== (size_t)self
->cur_pt
&& self
->hide_current
)
59 float x
= pt
.first
, y
= pt
.second
;
62 cairo_move_to(c
, x
, y
);
64 cairo_line_to(c
, x
, y
);
68 for (size_t i
= 0; i
< self
->points
->size(); i
++)
70 if (i
== (size_t)self
->cur_pt
&& self
->hide_current
)
72 const CalfCurve::point
&pt
= (*self
->points
)[i
];
73 float x
= pt
.first
, y
= pt
.second
;
75 gdk_cairo_set_source_color(c
, (i
== (size_t)self
->cur_pt
) ? &scHot
: &scPoint
);
76 cairo_rectangle(c
, x
- 2, y
- 2, 5, 5);
85 calf_curve_realize(GtkWidget
*widget
)
87 GTK_WIDGET_SET_FLAGS(widget
, GTK_REALIZED
);
89 GdkWindowAttr attributes
;
90 attributes
.event_mask
= GDK_EXPOSURE_MASK
| GDK_BUTTON1_MOTION_MASK
|
91 GDK_KEY_PRESS_MASK
| GDK_KEY_RELEASE_MASK
|
92 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
93 GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
;
94 attributes
.x
= widget
->allocation
.x
;
95 attributes
.y
= widget
->allocation
.y
;
96 attributes
.width
= widget
->allocation
.width
;
97 attributes
.height
= widget
->allocation
.height
;
98 attributes
.wclass
= GDK_INPUT_OUTPUT
;
99 attributes
.window_type
= GDK_WINDOW_CHILD
;
101 widget
->window
= gdk_window_new(gtk_widget_get_parent_window (widget
), &attributes
, GDK_WA_X
| GDK_WA_Y
);
103 gdk_window_set_user_data(widget
->window
, widget
);
104 widget
->style
= gtk_style_attach(widget
->style
, widget
->window
);
108 calf_curve_size_request (GtkWidget
*widget
,
109 GtkRequisition
*requisition
)
111 g_assert(CALF_IS_CURVE(widget
));
113 requisition
->width
= 64;
114 requisition
->height
= 32;
118 calf_curve_size_allocate (GtkWidget
*widget
,
119 GtkAllocation
*allocation
)
121 g_assert(CALF_IS_CURVE(widget
));
123 widget
->allocation
= *allocation
;
125 if (GTK_WIDGET_REALIZED(widget
))
126 gdk_window_move_resize(widget
->window
, allocation
->x
, allocation
->y
, allocation
->width
, allocation
->height
);
130 find_nearest(CalfCurve
*self
, int ex
, int ey
, int &insert_pt
)
134 for (int i
= 0; i
< (int)self
->points
->size(); i
++)
136 float x
= (*self
->points
)[i
].first
, y
= (*self
->points
)[i
].second
;
137 self
->log2phys(x
, y
);
138 float thisdist
= std::max(fabs(ex
- x
), fabs(ey
- y
));
151 calf_curve_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
153 g_assert(CALF_IS_CURVE(widget
));
154 CalfCurve
*self
= CALF_CURVE(widget
);
155 int found_pt
, insert_pt
= -1;
156 found_pt
= find_nearest(self
, event
->x
, event
->y
, insert_pt
);
157 if (found_pt
== -1 && insert_pt
!= -1)
159 // if at point limit, do not start anything
160 if (self
->points
->size() >= self
->point_limit
)
162 float x
= event
->x
, y
= event
->y
;
164 self
->phys2log(x
, y
);
165 self
->points
->insert(self
->points
->begin() + insert_pt
, CalfCurve::point(x
, y
));
166 self
->clip(insert_pt
, x
, y
, hide
);
170 self
->points
->erase(self
->points
->begin() + insert_pt
);
173 (*self
->points
)[insert_pt
] = CalfCurve::point(x
, y
);
174 found_pt
= insert_pt
;
176 gtk_widget_grab_focus(widget
);
177 self
->cur_pt
= found_pt
;
178 gtk_widget_queue_draw(widget
);
180 self
->sink
->curve_changed(self
, *self
->points
);
181 gdk_window_set_cursor(widget
->window
, self
->hand_cursor
);
186 calf_curve_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
188 g_assert(CALF_IS_CURVE(widget
));
189 CalfCurve
*self
= CALF_CURVE(widget
);
190 if (self
->cur_pt
!= -1 && self
->hide_current
)
191 self
->points
->erase(self
->points
->begin() + self
->cur_pt
);
193 self
->hide_current
= false;
195 self
->sink
->curve_changed(self
, *self
->points
);
196 gtk_widget_queue_draw(widget
);
197 gdk_window_set_cursor(widget
->window
, self
->points
->size() >= self
->point_limit
? self
->arrow_cursor
: self
->pencil_cursor
);
202 calf_curve_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
204 g_assert(CALF_IS_CURVE(widget
));
206 gdk_event_request_motions(event
);
207 CalfCurve
*self
= CALF_CURVE(widget
);
208 if (self
->cur_pt
!= -1)
210 float x
= event
->x
, y
= event
->y
;
211 self
->phys2log(x
, y
);
212 self
->clip(self
->cur_pt
, x
, y
, self
->hide_current
);
213 (*self
->points
)[self
->cur_pt
] = CalfCurve::point(x
, y
);
215 self
->sink
->curve_changed(self
, *self
->points
);
216 gtk_widget_queue_draw(widget
);
221 if (find_nearest(self
, event
->x
, event
->y
, insert_pt
) == -1)
222 gdk_window_set_cursor(widget
->window
, self
->points
->size() >= self
->point_limit
? self
->arrow_cursor
: self
->pencil_cursor
);
224 gdk_window_set_cursor(widget
->window
, self
->hand_cursor
);
230 calf_curve_finalize (GObject
*obj
)
232 g_assert(CALF_IS_CURVE(obj
));
233 CalfCurve
*self
= CALF_CURVE(obj
);
238 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
242 calf_curve_class_init (CalfCurveClass
*klass
)
244 parent_class
= g_type_class_peek_parent (klass
);
246 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
247 widget_class
->realize
= calf_curve_realize
;
248 widget_class
->expose_event
= calf_curve_expose
;
249 widget_class
->size_request
= calf_curve_size_request
;
250 widget_class
->size_allocate
= calf_curve_size_allocate
;
251 widget_class
->button_press_event
= calf_curve_button_press
;
252 widget_class
->button_release_event
= calf_curve_button_release
;
253 widget_class
->motion_notify_event
= calf_curve_pointer_motion
;
254 // widget_class->key_press_event = calf_curve_key_press;
255 // widget_class->scroll_event = calf_curve_scroll;
257 G_OBJECT_CLASS(klass
)->finalize
= calf_curve_finalize
;
261 calf_curve_init (CalfCurve
*self
)
263 GtkWidget
*widget
= GTK_WIDGET(self
);
264 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
);
265 self
->points
= new CalfCurve::point_vector
;
267 self
->points
->push_back(CalfCurve::point(0.f
, 1.f
));
268 self
->points
->push_back(CalfCurve::point(1.f
, 1.f
));
274 self
->hide_current
= false;
275 self
->pencil_cursor
= gdk_cursor_new(GDK_PENCIL
);
276 self
->hand_cursor
= gdk_cursor_new(GDK_FLEUR
);
277 self
->arrow_cursor
= gdk_cursor_new(GDK_ARROW
);
280 void CalfCurve::log2phys(float &x
, float &y
) {
281 x
= (x
- x0
) / (x1
- x0
) * (parent
.allocation
.width
- 2) + 1;
282 y
= (y
- y0
) / (y1
- y0
) * (parent
.allocation
.height
- 2) + 1;
285 void CalfCurve::phys2log(float &x
, float &y
) {
286 x
= x0
+ (x
- 1) * (x1
- x0
) / (parent
.allocation
.width
- 2);
287 y
= y0
+ (y
- 1) * (y1
- y0
) / (parent
.allocation
.height
- 2);
290 void CalfCurve::clip(int pt
, float &x
, float &y
, bool &hide
)
293 sink
->clip(this, pt
, x
, y
, hide
);
295 float ymin
= std::min(y0
, y1
), ymax
= std::max(y0
, y1
);
296 float yamp
= ymax
- ymin
;
297 if (pt
!= 0 && pt
!= (int)(points
->size() - 1))
299 if (y
< ymin
- yamp
|| y
> ymax
+ yamp
)
303 if (y
< ymin
) y
= ymin
;
305 if (y
> ymax
) y
= ymax
;
307 if (pt
== (int)(points
->size() - 1))
308 x
= (*points
)[pt
].first
;
309 if (pt
> 0 && x
< (*points
)[pt
- 1].first
)
310 x
= (*points
)[pt
- 1].first
;
311 if (pt
< (int)(points
->size() - 1) && x
> (*points
)[pt
+ 1].first
)
312 x
= (*points
)[pt
+ 1].first
;
315 void calf_curve_set_points(GtkWidget
*widget
, const CalfCurve::point_vector
&src
)
317 g_assert(CALF_IS_CURVE(widget
));
318 CalfCurve
*self
= CALF_CURVE(widget
);
319 if (self
->points
->size() != src
.size())
323 gtk_widget_queue_draw(widget
);
327 calf_curve_get_type (void)
329 static GType type
= 0;
332 static const GTypeInfo type_info
= {
333 sizeof(CalfCurveClass
),
334 NULL
, /* base_init */
335 NULL
, /* base_finalize */
336 (GClassInitFunc
)calf_curve_class_init
,
337 NULL
, /* class_finalize */
338 NULL
, /* class_data */
341 (GInstanceInitFunc
)calf_curve_init
344 for (int i
= 0; ; i
++) {
345 char *name
= g_strdup_printf("CalfCurve%u%d",
346 ((unsigned int)(intptr_t)calf_curve_class_init
) >> 16, i
);
347 if (g_type_from_name(name
)) {
351 type
= g_type_register_static(GTK_TYPE_WIDGET
,