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., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301 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
));
207 #if GTK_CHECK_VERSION(2,12,0)
208 gdk_event_request_motions(event
);
212 gdk_window_get_pointer (event
->window
, &a
, &b
, (GdkModifierType
*)&mod
);
215 CalfCurve
*self
= CALF_CURVE(widget
);
216 if (self
->cur_pt
!= -1)
218 float x
= event
->x
, y
= event
->y
;
219 self
->phys2log(x
, y
);
220 self
->clip(self
->cur_pt
, x
, y
, self
->hide_current
);
221 (*self
->points
)[self
->cur_pt
] = CalfCurve::point(x
, y
);
223 self
->sink
->curve_changed(self
, *self
->points
);
224 gtk_widget_queue_draw(widget
);
229 if (find_nearest(self
, event
->x
, event
->y
, insert_pt
) == -1)
230 gdk_window_set_cursor(widget
->window
, self
->points
->size() >= self
->point_limit
? self
->arrow_cursor
: self
->pencil_cursor
);
232 gdk_window_set_cursor(widget
->window
, self
->hand_cursor
);
238 calf_curve_finalize (GObject
*obj
)
240 g_assert(CALF_IS_CURVE(obj
));
241 CalfCurve
*self
= CALF_CURVE(obj
);
246 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
250 calf_curve_class_init (CalfCurveClass
*klass
)
252 parent_class
= g_type_class_peek_parent (klass
);
254 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
255 widget_class
->realize
= calf_curve_realize
;
256 widget_class
->expose_event
= calf_curve_expose
;
257 widget_class
->size_request
= calf_curve_size_request
;
258 widget_class
->size_allocate
= calf_curve_size_allocate
;
259 widget_class
->button_press_event
= calf_curve_button_press
;
260 widget_class
->button_release_event
= calf_curve_button_release
;
261 widget_class
->motion_notify_event
= calf_curve_pointer_motion
;
262 // widget_class->key_press_event = calf_curve_key_press;
263 // widget_class->scroll_event = calf_curve_scroll;
265 G_OBJECT_CLASS(klass
)->finalize
= calf_curve_finalize
;
269 calf_curve_init (CalfCurve
*self
)
271 GtkWidget
*widget
= GTK_WIDGET(self
);
272 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
);
273 self
->points
= new CalfCurve::point_vector
;
275 self
->points
->push_back(CalfCurve::point(0.f
, 1.f
));
276 self
->points
->push_back(CalfCurve::point(1.f
, 1.f
));
282 self
->hide_current
= false;
283 self
->pencil_cursor
= gdk_cursor_new(GDK_PENCIL
);
284 self
->hand_cursor
= gdk_cursor_new(GDK_FLEUR
);
285 self
->arrow_cursor
= gdk_cursor_new(GDK_ARROW
);
288 void CalfCurve::log2phys(float &x
, float &y
) {
289 x
= (x
- x0
) / (x1
- x0
) * (parent
.allocation
.width
- 2) + 1;
290 y
= (y
- y0
) / (y1
- y0
) * (parent
.allocation
.height
- 2) + 1;
293 void CalfCurve::phys2log(float &x
, float &y
) {
294 x
= x0
+ (x
- 1) * (x1
- x0
) / (parent
.allocation
.width
- 2);
295 y
= y0
+ (y
- 1) * (y1
- y0
) / (parent
.allocation
.height
- 2);
298 void CalfCurve::clip(int pt
, float &x
, float &y
, bool &hide
)
301 sink
->clip(this, pt
, x
, y
, hide
);
303 float ymin
= std::min(y0
, y1
), ymax
= std::max(y0
, y1
);
304 float yamp
= ymax
- ymin
;
305 if (pt
!= 0 && pt
!= (int)(points
->size() - 1))
307 if (y
< ymin
- yamp
|| y
> ymax
+ yamp
)
311 if (y
< ymin
) y
= ymin
;
313 if (y
> ymax
) y
= ymax
;
315 if (pt
== (int)(points
->size() - 1))
316 x
= (*points
)[pt
].first
;
317 if (pt
> 0 && x
< (*points
)[pt
- 1].first
)
318 x
= (*points
)[pt
- 1].first
;
319 if (pt
< (int)(points
->size() - 1) && x
> (*points
)[pt
+ 1].first
)
320 x
= (*points
)[pt
+ 1].first
;
323 void calf_curve_set_points(GtkWidget
*widget
, const CalfCurve::point_vector
&src
)
325 g_assert(CALF_IS_CURVE(widget
));
326 CalfCurve
*self
= CALF_CURVE(widget
);
327 if (self
->points
->size() != src
.size())
331 gtk_widget_queue_draw(widget
);
335 calf_curve_get_type (void)
337 static GType type
= 0;
340 static const GTypeInfo type_info
= {
341 sizeof(CalfCurveClass
),
342 NULL
, /* base_init */
343 NULL
, /* base_finalize */
344 (GClassInitFunc
)calf_curve_class_init
,
345 NULL
, /* class_finalize */
346 NULL
, /* class_data */
349 (GInstanceInitFunc
)calf_curve_init
352 for (int i
= 0; ; i
++) {
353 char *name
= g_strdup_printf("CalfCurve%u%d",
354 ((unsigned int)(intptr_t)calf_curve_class_init
) >> 16, i
);
355 if (g_type_from_name(name
)) {
359 type
= g_type_register_static(GTK_TYPE_WIDGET
,