Mercurial > hg > xmms-sid
comparison src/xs_curve.c @ 502:54d86ee98b98
Alpha/preliminary curve widget code merged.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 27 Jan 2007 02:52:02 +0000 |
parents | 0f255a575720 |
children | 982cec405ef0 |
comparison
equal
deleted
inserted
replaced
501:d753e545f9fa | 502:54d86ee98b98 |
---|---|
1 #include <stdlib.h> | 1 #include <stdlib.h> |
2 #include <string.h> | 2 #include <string.h> |
3 #include <math.h> | 3 #include <math.h> |
4 | |
5 #include <stdio.h> | |
4 | 6 |
5 #include "xs_curve.h" | 7 #include "xs_curve.h" |
6 #include <gtk/gtkdrawingarea.h> | 8 #include <gtk/gtkdrawingarea.h> |
7 #include <gtk/gtkmain.h> | 9 #include <gtk/gtkmain.h> |
8 #include <gtk/gtksignal.h> | 10 #include <gtk/gtksignal.h> |
9 | 11 |
10 | 12 |
13 #define GET_X(i) curve->ctlpoints[i].x | |
14 #define GET_Y(i) curve->ctlpoints[i].y | |
15 | |
16 | |
11 #define RADIUS 3 /* radius of the control points */ | 17 #define RADIUS 3 /* radius of the control points */ |
18 #define RADIUS2 (RADIUS * 2) | |
19 | |
12 #define MIN_DISTANCE 8 /* min distance between control points */ | 20 #define MIN_DISTANCE 8 /* min distance between control points */ |
13 | 21 |
14 #define GRAPH_MASK (GDK_EXPOSURE_MASK | \ | 22 #define GRAPH_MASK (GDK_EXPOSURE_MASK | \ |
15 GDK_POINTER_MOTION_MASK | \ | 23 GDK_POINTER_MOTION_MASK | \ |
16 GDK_POINTER_MOTION_HINT_MASK | \ | 24 GDK_POINTER_MOTION_HINT_MASK | \ |
17 GDK_ENTER_NOTIFY_MASK | \ | 25 GDK_ENTER_NOTIFY_MASK | \ |
18 GDK_BUTTON_PRESS_MASK | \ | 26 GDK_BUTTON_PRESS_MASK | \ |
19 GDK_BUTTON_RELEASE_MASK | \ | 27 GDK_BUTTON_RELEASE_MASK | \ |
20 GDK_BUTTON1_MOTION_MASK) | 28 GDK_BUTTON1_MOTION_MASK) |
21 | 29 |
22 enum | 30 enum { |
23 { | |
24 ARG_0, | 31 ARG_0, |
25 ARG_CURVE_TYPE, | 32 ARG_CURVE_TYPE, |
26 ARG_MIN_X, | 33 ARG_MIN_X, |
27 ARG_MAX_X, | 34 ARG_MAX_X, |
28 ARG_MIN_Y, | 35 ARG_MIN_Y, |
29 ARG_MAX_Y | 36 ARG_MAX_Y |
30 }; | 37 }; |
31 | 38 |
32 static GtkDrawingAreaClass *parent_class = NULL; | 39 static GtkDrawingAreaClass *parent_class = NULL; |
33 | 40 |
34 | |
35 /* forward declarations: */ | |
36 static void xs_curve_class_init(XSCurveClass * class); | 41 static void xs_curve_class_init(XSCurveClass * class); |
37 static void xs_curve_init(XSCurve * curve); | 42 static void xs_curve_init(XSCurve * curve); |
38 static void xs_curve_set_arg(GtkObject * object, GtkArg * arg, guint arg_id); | 43 static void xs_curve_set_arg(GtkObject * object, GtkArg * arg, guint arg_id); |
39 static void xs_curve_get_arg(GtkObject * object, GtkArg * arg, guint arg_id); | 44 static void xs_curve_get_arg(GtkObject * object, GtkArg * arg, guint arg_id); |
40 static void xs_curve_finalize(GtkObject * object); | 45 static void xs_curve_finalize(GtkObject * object); |
41 static gint xs_curve_graph_events(GtkWidget * widget, GdkEvent * event, XSCurve * c); | 46 static gint xs_curve_graph_events(GtkWidget * widget, GdkEvent * event, XSCurve * c); |
42 static void xs_curve_size_graph(XSCurve * curve); | 47 static void xs_curve_size_graph(XSCurve * curve); |
48 | |
43 | 49 |
44 GtkType xs_curve_get_type(void) | 50 GtkType xs_curve_get_type(void) |
45 { | 51 { |
46 static GtkType curve_type = 0; | 52 static GtkType curve_type = 0; |
47 | 53 |
60 curve_type = gtk_type_unique(GTK_TYPE_DRAWING_AREA, &curve_info); | 66 curve_type = gtk_type_unique(GTK_TYPE_DRAWING_AREA, &curve_info); |
61 } | 67 } |
62 return curve_type; | 68 return curve_type; |
63 } | 69 } |
64 | 70 |
65 static void xs_curve_class_init(XSCurveClass * class) | 71 |
72 static void xs_curve_class_init(XSCurveClass *class) | |
66 { | 73 { |
67 GtkObjectClass *object_class; | 74 GtkObjectClass *object_class; |
68 | 75 |
69 parent_class = gtk_type_class(GTK_TYPE_DRAWING_AREA); | 76 parent_class = gtk_type_class(GTK_TYPE_DRAWING_AREA); |
70 | 77 |
78 gtk_object_add_arg_type("XSCurve::max_x", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_X); | 85 gtk_object_add_arg_type("XSCurve::max_x", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_X); |
79 gtk_object_add_arg_type("XSCurve::min_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MIN_Y); | 86 gtk_object_add_arg_type("XSCurve::min_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MIN_Y); |
80 gtk_object_add_arg_type("XSCurve::max_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_Y); | 87 gtk_object_add_arg_type("XSCurve::max_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_Y); |
81 } | 88 } |
82 | 89 |
83 static void xs_curve_init(XSCurve * curve) | 90 |
91 static void xs_curve_init(XSCurve *curve) | |
84 { | 92 { |
85 gint old_mask; | 93 gint old_mask; |
86 | 94 |
87 curve->pixmap = NULL; | 95 curve->pixmap = NULL; |
88 curve->height = 0; | 96 curve->height = 0; |
89 curve->grab_point = -1; | 97 curve->grab_point = -1; |
90 | 98 |
91 curve->num_points = 0; | |
92 curve->point = 0; | |
93 | |
94 curve->num_ctlpoints = 0; | 99 curve->num_ctlpoints = 0; |
95 curve->ctlpoint = NULL; | 100 curve->ctlpoints = NULL; |
96 | 101 |
97 curve->min_x = 0.0; | 102 curve->min_x = 0.0; |
98 curve->max_x = 1.0; | 103 curve->max_x = 2048.0; |
99 curve->min_y = 0.0; | 104 curve->min_y = 0.0; |
100 curve->max_y = 1.0; | 105 curve->max_y = 22000.0; |
101 | 106 |
102 old_mask = gtk_widget_get_events(GTK_WIDGET(curve)); | 107 old_mask = gtk_widget_get_events(GTK_WIDGET(curve)); |
103 gtk_widget_set_events(GTK_WIDGET(curve), old_mask | GRAPH_MASK); | 108 gtk_widget_set_events(GTK_WIDGET(curve), old_mask | GRAPH_MASK); |
104 gtk_signal_connect(GTK_OBJECT(curve), "event", (GtkSignalFunc) xs_curve_graph_events, curve); | 109 gtk_signal_connect(GTK_OBJECT(curve), "event", (GtkSignalFunc) xs_curve_graph_events, curve); |
105 xs_curve_size_graph(curve); | 110 xs_curve_size_graph(curve); |
106 } | 111 } |
107 | 112 |
108 static void xs_curve_set_arg(GtkObject * object, GtkArg * arg, guint arg_id) | 113 |
114 static void xs_curve_set_arg(GtkObject *object, GtkArg *arg, guint arg_id) | |
109 { | 115 { |
110 XSCurve *curve = XS_CURVE(object); | 116 XSCurve *curve = XS_CURVE(object); |
111 | 117 |
112 switch (arg_id) { | 118 switch (arg_id) { |
113 case ARG_MIN_X: | 119 case ARG_MIN_X: |
123 xs_curve_set_range(curve, curve->min_x, curve->max_x, curve->min_y, GTK_VALUE_FLOAT(*arg)); | 129 xs_curve_set_range(curve, curve->min_x, curve->max_x, curve->min_y, GTK_VALUE_FLOAT(*arg)); |
124 break; | 130 break; |
125 } | 131 } |
126 } | 132 } |
127 | 133 |
128 static void xs_curve_get_arg(GtkObject * object, GtkArg * arg, guint arg_id) | 134 |
135 static void xs_curve_get_arg(GtkObject *object, GtkArg *arg, guint arg_id) | |
129 { | 136 { |
130 XSCurve *curve = XS_CURVE(object); | 137 XSCurve *curve = XS_CURVE(object); |
131 | 138 |
132 switch (arg_id) { | 139 switch (arg_id) { |
133 case ARG_MIN_X: | 140 case ARG_MIN_X: |
146 arg->type = GTK_TYPE_INVALID; | 153 arg->type = GTK_TYPE_INVALID; |
147 break; | 154 break; |
148 } | 155 } |
149 } | 156 } |
150 | 157 |
158 | |
151 static int xs_project(gfloat value, gfloat min, gfloat max, int norm) | 159 static int xs_project(gfloat value, gfloat min, gfloat max, int norm) |
152 { | 160 { |
153 return (norm - 1) * ((value - min) / (max - min)) + 0.5; | 161 return (norm - 1) * ((value - min) / (max - min)) + 0.5; |
154 } | 162 } |
155 | 163 |
164 | |
156 static gfloat xs_unproject(gint value, gfloat min, gfloat max, int norm) | 165 static gfloat xs_unproject(gint value, gfloat min, gfloat max, int norm) |
157 { | 166 { |
158 return value / (gfloat) (norm - 1) * (max - min) + min; | 167 return value / (gfloat) (norm - 1) * (max - min) + min; |
159 } | 168 } |
160 | 169 |
161 /* Solve the tridiagonal equation system that determines the second | 170 |
162 derivatives for the interpolation points. (Based on Numerical | 171 static inline void xs_cubic_coeff(gfloat x1, gfloat y1, |
163 Recipies 2nd Edition.) */ | 172 gfloat x2, gfloat y2, |
164 static void spline_solve(int n, gfloat x[], gfloat y[], gfloat y2[]) | 173 gfloat k1, gfloat k2, |
165 { | 174 gfloat *a, gfloat *b, |
166 gfloat p, sig, *u; | 175 gfloat *c, gfloat *d) |
167 gint i, k; | 176 { |
168 | 177 gfloat dx = x2 - x1, dy = y2 - y1; |
169 u = g_malloc((n - 1) * sizeof(u[0])); | 178 |
170 | 179 *a = ((k1 + k2) - 2 * dy / dx) / (dx * dx); |
171 y2[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */ | 180 *b = ((k2 - k1) / dx - 3 * (x1 + x2) * (*a)) / 2; |
172 | 181 *c = k1 - (3 * x1 * (*a) + 2 * (*b)) * x1; |
173 for (i = 1; i < n - 1; ++i) { | 182 *d = y1 - ((x1 * (*a) + (*b)) * x1 + (*c)) * x1; |
174 sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]); | 183 } |
175 p = sig * y2[i - 1] + 2.0; | 184 |
176 y2[i] = (sig - 1.0) / p; | 185 #define x(val) val->x |
177 u[i] = ((y[i + 1] - y[i]) | 186 #define y(val) val->y |
178 / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1])); | 187 |
179 u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p; | 188 static void xs_curve_draw(XSCurve *curve, gint width, gint height) |
180 } | 189 { |
181 | 190 gfloat res = 10.0f; |
182 y2[n - 1] = 0.0; | |
183 for (k = n - 2; k >= 0; --k) | |
184 y2[k] = y2[k] * y2[k + 1] + u[k]; | |
185 | |
186 g_free(u); | |
187 } | |
188 | |
189 static gfloat spline_eval(int n, gfloat x[], gfloat y[], gfloat y2[], gfloat val) | |
190 { | |
191 gint k_lo, k_hi, k; | |
192 gfloat h, b, a; | |
193 | |
194 /* do a binary search for the right interval: */ | |
195 k_lo = 0; | |
196 k_hi = n - 1; | |
197 while (k_hi - k_lo > 1) { | |
198 k = (k_hi + k_lo) / 2; | |
199 if (x[k] > val) | |
200 k_hi = k; | |
201 else | |
202 k_lo = k; | |
203 } | |
204 | |
205 h = x[k_hi] - x[k_lo]; | |
206 g_assert(h > 0.0); | |
207 | |
208 a = (x[k_hi] - val) / h; | |
209 b = (val - x[k_lo]) / h; | |
210 return a * y[k_lo] + b * y[k_hi] + ((a * a * a - a) * y2[k_lo] + (b * b * b - b) * y2[k_hi]) * (h * h) / 6.0; | |
211 } | |
212 | |
213 static void xs_curve_interpolate(XSCurve * c, gint width, gint height) | |
214 { | |
215 gfloat *vector; | |
216 gint i; | |
217 | |
218 vector = g_malloc(width * sizeof(vector[0])); | |
219 | |
220 xs_curve_get_vector(c, width, vector); | |
221 | |
222 c->height = height; | |
223 if (c->num_points != width) { | |
224 c->num_points = width; | |
225 if (c->point) | |
226 g_free(c->point); | |
227 c->point = g_malloc(c->num_points * sizeof(c->point[0])); | |
228 } | |
229 | |
230 for (i = 0; i < width; ++i) { | |
231 c->point[i].x = RADIUS + i; | |
232 c->point[i].y = RADIUS + height - project(vector[i], c->min_y, c->max_y, height); | |
233 } | |
234 | |
235 g_free(vector); | |
236 } | |
237 | |
238 static void xs_curve_draw(XSCurve * c, gint width, gint height) | |
239 { | |
240 GtkStateType state; | 191 GtkStateType state; |
241 GtkStyle *style; | 192 GtkStyle *style; |
242 gint i; | 193 gint i; |
243 | 194 t_xs_point *p0, *p1, *p2, *p3; |
244 if (!c->pixmap) | 195 |
196 if (!curve->pixmap) | |
245 return; | 197 return; |
246 | 198 |
247 if (c->height != height || c->num_points != width) | |
248 xs_curve_interpolate(c, width, height); | |
249 | |
250 state = GTK_STATE_NORMAL; | 199 state = GTK_STATE_NORMAL; |
251 if (!GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(c))) | 200 if (!GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(curve))) |
252 state = GTK_STATE_INSENSITIVE; | 201 state = GTK_STATE_INSENSITIVE; |
253 | 202 |
254 style = GTK_WIDGET(c)->style; | 203 style = GTK_WIDGET(curve)->style; |
255 | 204 |
256 /* clear the pixmap: */ | 205 /* Clear the pixmap */ |
257 gtk_paint_flat_box(style, c->pixmap, | 206 gtk_paint_flat_box(style, curve->pixmap, |
258 GTK_STATE_NORMAL, GTK_SHADOW_NONE, | 207 GTK_STATE_NORMAL, GTK_SHADOW_NONE, |
259 NULL, GTK_WIDGET(c), "curve_bg", | 208 NULL, GTK_WIDGET(curve), "curve_bg", |
260 0, 0, width + RADIUS * 2, height + RADIUS * 2); | 209 0, 0, |
261 | 210 width + RADIUS2, |
262 /* draw the grid lines: (XXX make more meaningful) */ | 211 height + RADIUS2); |
212 | |
213 | |
214 /* Draw the grid */ | |
263 for (i = 0; i < 5; i++) { | 215 for (i = 0; i < 5; i++) { |
264 gdk_draw_line(c->pixmap, style->dark_gc[state], | 216 gdk_draw_line(curve->pixmap, style->dark_gc[state], |
265 RADIUS, i * (height / 4.0) + RADIUS, | 217 RADIUS, i * (height / 4.0) + RADIUS, |
266 width + RADIUS, i * (height / 4.0) + RADIUS); | 218 width + RADIUS, i * (height / 4.0) + RADIUS); |
267 | 219 |
268 gdk_draw_line(c->pixmap, style->dark_gc[state], | 220 gdk_draw_line(curve->pixmap, style->dark_gc[state], |
269 i * (width / 4.0) + RADIUS, RADIUS, | 221 i * (width / 4.0) + RADIUS, RADIUS, |
270 i * (width / 4.0) + RADIUS, height + RADIUS); | 222 i * (width / 4.0) + RADIUS, height + RADIUS); |
271 } | 223 } |
272 | 224 |
273 gdk_draw_points(c->pixmap, style->fg_gc[state], c->point, c->num_points); | 225 |
274 | 226 /* Draw the spline/curve itself */ |
275 gdk_draw_pixmap(GTK_WIDGET(c)->window, style->fg_gc[state], c->pixmap, | 227 p0 = curve->ctlpoints; |
276 0, 0, 0, 0, width + RADIUS * 2, height + RADIUS * 2); | 228 p1 = p0; p1++; |
277 } | 229 p2 = p1; p2++; |
278 | 230 p3 = p2; p3++; |
279 static gint xs_curve_graph_events(GtkWidget * widget, GdkEvent * event, XSCurve * c) | 231 |
280 { | 232 /* Draw each curve segment */ |
281 gint i, src, dst, leftbound, rightbound; | 233 fprintf(stderr, "npoints = %d\n", curve->num_ctlpoints); |
234 for (i = 0; i < curve->num_ctlpoints; i++, ++p0, ++p1, ++p2, ++p3) { | |
235 gfloat k1, k2, a, b, c, d, x; | |
236 | |
237 /* p1 and p2 equal; single point */ | |
238 if (x(p1) == x(p2)) | |
239 continue; | |
240 | |
241 /* Both end points repeated; straight line */ | |
242 if (x(p0) == x(p1) && x(p2) == x(p3)) { | |
243 k1 = k2 = (y(p2) - y(p1)) / (x(p2) - x(p1)); | |
244 } | |
245 /* p0 and p1 equal; use f''(x1) = 0 */ | |
246 else if (x(p0) == x(p1)) { | |
247 k2 = (y(p3) - y(p1)) / (x(p3) - x(p1)); | |
248 k1 = (3 * (y(p2) - y(p1)) / (x(p2) - x(p1)) - k2) / 2; | |
249 } | |
250 /* p2 and p3 equal; use f''(x2) = 0 */ | |
251 else if (x(p2) == x(p3)) { | |
252 k1 = (y(p2) - y(p0)) / (x(p2) - x(p0)); | |
253 k2 = (3 * (y(p2) - y(p1)) / (x(p2) - x(p1)) - k1) / 2; | |
254 } | |
255 /* Normal curve */ | |
256 else { | |
257 k1 = (y(p2) - y(p0)) / (x(p2) - x(p0)); | |
258 k2 = (y(p3) - y(p1)) / (x(p3) - x(p1)); | |
259 } | |
260 | |
261 xs_cubic_coeff(x(p1), y(p1), x(p2), y(p2), k1, k2, &a, &b, &c, &d); | |
262 | |
263 fprintf(stderr, "--seg[%1.3f, %1.3f]=>[%1.3f, %1.3f]--\n", | |
264 x(p1), y(p1), x(p2), y(p2)); | |
265 | |
266 for (x = x(p1); x <= x(p2); x += res) { | |
267 gfloat y = ((a * x + b) * x + c) * x + d; | |
268 gint qx, qy; | |
269 qx = RADIUS + xs_project(x, curve->min_x, curve->max_x, width); | |
270 qy = RADIUS + xs_project(y, curve->min_y, curve->max_y, height); | |
271 | |
272 fprintf(stderr, "[%1.3f, %1.3f] -> %d, %d\n", x, y, qx, qy); | |
273 | |
274 gdk_draw_point(curve->pixmap, style->fg_gc[state], | |
275 RADIUS + xs_project(x, curve->min_x, curve->max_x, width), | |
276 RADIUS + xs_project(y, curve->min_y, curve->max_y, height) | |
277 ); | |
278 | |
279 } | |
280 | |
281 fprintf(stderr, "-------\n"); | |
282 } | |
283 | |
284 /* Draw control points */ | |
285 for (i = 0; i < curve->num_ctlpoints; ++i) { | |
286 gint x, y; | |
287 | |
288 if (GET_X(i) < curve->min_x || GET_Y(i) < curve->min_y || | |
289 GET_X(i) >= curve->max_x || GET_Y(i) >= curve->max_y) | |
290 continue; | |
291 | |
292 x = xs_project(GET_X(i), curve->min_x, curve->max_x, width); | |
293 y = xs_project(GET_Y(i), curve->min_y, curve->max_y, height); | |
294 | |
295 gdk_draw_arc(curve->pixmap, style->fg_gc[state], TRUE, | |
296 x, y, RADIUS2, RADIUS2, 0, 360 * 64); | |
297 } | |
298 | |
299 /* Draw pixmap in the widget */ | |
300 gdk_draw_pixmap(GTK_WIDGET(curve)->window, | |
301 style->fg_gc[state], curve->pixmap, | |
302 0, 0, 0, 0, | |
303 width + RADIUS2, | |
304 height + RADIUS2); | |
305 } | |
306 | |
307 #undef x | |
308 #undef y | |
309 | |
310 | |
311 static gint xs_curve_graph_events(GtkWidget *widget, GdkEvent *event, XSCurve *curve) | |
312 { | |
313 GdkCursorType new_type = curve->cursor_type; | |
282 GdkEventButton *bevent; | 314 GdkEventButton *bevent; |
283 GdkEventMotion *mevent; | 315 GdkEventMotion *mevent; |
284 GtkWidget *w; | 316 GtkWidget *w; |
285 gint tx, ty; | 317 gint i, width, height, x, y, tx, ty, cx, closest_point = 0, min_x; |
286 gint cx, x, y, width, height; | |
287 gint closest_point = 0; | |
288 gfloat rx, ry, min_x; | |
289 guint distance; | 318 guint distance; |
290 gint x1, x2, y1, y2; | 319 |
291 | 320 w = GTK_WIDGET(curve); |
292 w = GTK_WIDGET(c); | 321 width = w->allocation.width - RADIUS2; |
293 width = w->allocation.width - RADIUS * 2; | 322 height = w->allocation.height - RADIUS2; |
294 height = w->allocation.height - RADIUS * 2; | |
295 | 323 |
296 if ((width < 0) || (height < 0)) | 324 if ((width < 0) || (height < 0)) |
297 return FALSE; | 325 return FALSE; |
326 | |
327 /* get the pointer position */ | |
328 gdk_window_get_pointer(w->window, &tx, &ty, NULL); | |
329 x = CLAMP((tx - RADIUS), 0, width - 1); | |
330 y = CLAMP((ty - RADIUS), 0, height - 1); | |
331 | |
332 min_x = curve->min_x; | |
333 | |
334 distance = ~0U; | |
335 for (i = 0; i < curve->num_ctlpoints; ++i) { | |
336 cx = xs_project(GET_X(i), min_x, curve->max_x, width); | |
337 if ((guint) abs(x - cx) < distance) { | |
338 distance = abs(x - cx); | |
339 closest_point = i; | |
340 } | |
341 } | |
342 | |
343 /* Act based on event type */ | |
344 switch (event->type) { | |
345 case GDK_CONFIGURE: | |
346 if (curve->pixmap) | |
347 gdk_pixmap_unref(curve->pixmap); | |
348 curve->pixmap = 0; | |
349 | |
350 /* fall through */ | |
351 | |
352 case GDK_EXPOSE: | |
353 if (!curve->pixmap) { | |
354 curve->pixmap = gdk_pixmap_new(w->window, | |
355 w->allocation.width, w->allocation.height, -1); | |
356 } | |
357 xs_curve_draw(curve, width, height); | |
358 break; | |
359 | |
360 case GDK_BUTTON_PRESS: | |
361 gtk_grab_add(widget); | |
362 | |
363 bevent = (GdkEventButton *) event; | |
364 new_type = GDK_TCROSS; | |
365 | |
366 if (distance > MIN_DISTANCE) { | |
367 /* insert a new control point */ | |
368 if (curve->num_ctlpoints > 0) { | |
369 cx = xs_project(GET_X(closest_point), min_x, curve->max_x, width); | |
370 if (x > cx) closest_point++; | |
371 } | |
372 | |
373 curve->num_ctlpoints++; | |
374 | |
375 curve->ctlpoints = g_realloc(curve->ctlpoints, curve->num_ctlpoints * sizeof(*curve->ctlpoints)); | |
376 for (i = curve->num_ctlpoints - 1; i > closest_point; --i) { | |
377 memcpy(curve->ctlpoints + i, curve->ctlpoints + i - 1, sizeof(*curve->ctlpoints)); | |
378 } | |
379 } | |
380 | |
381 curve->grab_point = closest_point; | |
382 GET_X(curve->grab_point) = xs_unproject(x, min_x, curve->max_x, width); | |
383 GET_Y(curve->grab_point) = xs_unproject(y, curve->min_y, curve->max_y, height); | |
384 | |
385 xs_curve_draw(curve, width, height); | |
386 break; | |
387 | |
388 case GDK_BUTTON_RELEASE: | |
389 { | |
390 gint src, dst; | |
391 | |
392 gtk_grab_remove(widget); | |
393 | |
394 /* delete inactive points: */ | |
395 for (src = dst = 0; src < curve->num_ctlpoints; ++src) | |
396 if (GET_X(src) >= min_x) { | |
397 memcpy(curve->ctlpoints + dst, curve->ctlpoints + src, sizeof(*curve->ctlpoints)); | |
398 dst++; | |
399 } | |
400 | |
401 if (dst < src) { | |
402 curve->num_ctlpoints -= (src - dst); | |
403 if (curve->num_ctlpoints <= 0) { | |
404 curve->num_ctlpoints = 1; | |
405 GET_X(0) = min_x; | |
406 GET_Y(0) = curve->min_y; | |
407 xs_curve_draw(curve, width, height); | |
408 } | |
409 curve->ctlpoints = g_realloc(curve->ctlpoints, curve->num_ctlpoints * sizeof(*curve->ctlpoints)); | |
410 } | |
411 | |
412 new_type = GDK_FLEUR; | |
413 curve->grab_point = -1; | |
414 } | |
415 break; | |
416 | |
417 case GDK_MOTION_NOTIFY: | |
418 mevent = (GdkEventMotion *) event; | |
419 | |
420 if (curve->grab_point == -1) { | |
421 /* if no point is grabbed... */ | |
422 if (distance <= MIN_DISTANCE) | |
423 new_type = GDK_FLEUR; | |
424 else | |
425 new_type = GDK_TCROSS; | |
426 } else { | |
427 gint leftbound, rightbound; | |
428 | |
429 /* drag the grabbed point */ | |
430 new_type = GDK_TCROSS; | |
431 | |
432 leftbound = -MIN_DISTANCE; | |
433 if (curve->grab_point > 0) { | |
434 leftbound = xs_project( | |
435 GET_X(curve->grab_point-1), | |
436 min_x, curve->max_x, width); | |
437 } | |
438 | |
439 rightbound = width + RADIUS2 + MIN_DISTANCE; | |
440 if (curve->grab_point + 1 < curve->num_ctlpoints) { | |
441 rightbound = xs_project( | |
442 GET_X(curve->grab_point+1), | |
443 min_x, curve->max_x, width); | |
444 } | |
445 | |
446 if ((tx <= leftbound) || (tx >= rightbound) || | |
447 (ty > height + RADIUS2 + MIN_DISTANCE) || (ty < -MIN_DISTANCE)) { | |
448 GET_X(curve->grab_point) = min_x - 1.0; | |
449 } else { | |
450 GET_X(curve->grab_point) = | |
451 xs_unproject(x, min_x, curve->max_x, width); | |
452 GET_Y(curve->grab_point) = | |
453 xs_unproject(y, curve->min_y, curve->max_y, height); | |
454 } | |
455 | |
456 xs_curve_draw(curve, width, height); | |
457 } | |
458 | |
459 if (new_type != (GdkCursorType) curve->cursor_type) { | |
460 GdkCursor *cursor; | |
461 | |
462 curve->cursor_type = new_type; | |
463 | |
464 cursor = gdk_cursor_new(curve->cursor_type); | |
465 gdk_window_set_cursor(w->window, cursor); | |
466 gdk_cursor_destroy(cursor); | |
467 } | |
468 break; | |
469 | |
470 default: | |
471 break; | |
472 } | |
298 | 473 |
299 return FALSE; | 474 return FALSE; |
300 } | 475 } |
301 | 476 |
302 static void xs_curve_size_graph(XSCurve * curve) | 477 |
478 static void xs_curve_size_graph(XSCurve *curve) | |
303 { | 479 { |
304 gint width, height; | 480 gint width, height; |
305 gfloat aspect; | 481 gfloat aspect; |
306 | 482 |
307 width = (curve->max_x - curve->min_x) + 1; | 483 width = (curve->max_x - curve->min_x) + 1; |
308 height = (curve->max_y - curve->min_y) + 1; | 484 height = (curve->max_y - curve->min_y) + 1; |
309 aspect = width / (gfloat) height; | 485 aspect = width / (gfloat) height; |
486 | |
310 if (width > gdk_screen_width() / 4) | 487 if (width > gdk_screen_width() / 4) |
311 width = gdk_screen_width() / 4; | 488 width = gdk_screen_width() / 4; |
489 | |
312 if (height > gdk_screen_height() / 4) | 490 if (height > gdk_screen_height() / 4) |
313 height = gdk_screen_height() / 4; | 491 height = gdk_screen_height() / 4; |
314 | 492 |
315 if (aspect < 1.0) | 493 if (aspect < 1.0) |
316 width = height * aspect; | 494 width = height * aspect; |
317 else | 495 else |
318 height = width / aspect; | 496 height = width / aspect; |
319 | 497 |
320 gtk_drawing_area_size(GTK_DRAWING_AREA(curve), width + RADIUS * 2, height + RADIUS * 2); | 498 gtk_drawing_area_size(GTK_DRAWING_AREA(curve), width + RADIUS2, height + RADIUS2); |
321 } | 499 } |
322 | 500 |
323 static void xs_curve_reset_vector(XSCurve * curve) | 501 |
324 { | 502 void xs_curve_reset(XSCurve *curve) |
325 } | 503 { |
326 | 504 if (curve->ctlpoints) |
327 void xs_curve_reset(XSCurve * c) | 505 g_free(curve->ctlpoints); |
328 { | 506 |
329 } | 507 curve->num_ctlpoints = 4; |
330 | 508 curve->ctlpoints = g_malloc(curve->num_ctlpoints * sizeof(curve->ctlpoints[0])); |
331 | 509 |
332 void xs_curve_set_range(XSCurve * curve, gfloat min_x, gfloat max_x, gfloat min_y, gfloat max_y) | 510 GET_X(0) = curve->min_x; |
511 GET_Y(0) = curve->min_y; | |
512 | |
513 GET_X(1) = curve->min_x; | |
514 GET_Y(1) = curve->min_y; | |
515 | |
516 GET_X(2) = curve->max_x; | |
517 GET_Y(2) = curve->max_y; | |
518 | |
519 GET_X(3) = curve->max_x; | |
520 GET_Y(3) = curve->max_y; | |
521 | |
522 if (curve->pixmap) { | |
523 gint width, height; | |
524 | |
525 width = GTK_WIDGET(curve)->allocation.width - RADIUS2; | |
526 height = GTK_WIDGET(curve)->allocation.height - RADIUS2; | |
527 xs_curve_draw(curve, width, height); | |
528 } | |
529 } | |
530 | |
531 | |
532 void xs_curve_set_range(XSCurve *curve, gfloat min_x, gfloat max_x, gfloat min_y, gfloat max_y) | |
333 { | 533 { |
334 curve->min_x = min_x; | 534 curve->min_x = min_x; |
335 curve->max_x = max_x; | 535 curve->max_x = max_x; |
336 curve->min_y = min_y; | 536 curve->min_y = min_y; |
337 curve->max_y = max_y; | 537 curve->max_y = max_y; |
338 | 538 |
339 xs_curve_size_graph(curve); | 539 xs_curve_size_graph(curve); |
340 xs_curve_reset_vector(curve); | 540 xs_curve_reset(curve); |
341 } | 541 } |
342 | 542 |
343 | 543 |
344 GtkWidget *xs_curve_new(void) | 544 GtkWidget *xs_curve_new(void) |
345 { | 545 { |
346 return gtk_type_new(xs_curve_get_type()); | 546 return gtk_type_new(xs_curve_get_type()); |
347 } | 547 } |
348 | 548 |
349 | 549 |
350 static void xs_curve_finalize(GtkObject * object) | 550 static void xs_curve_finalize(GtkObject *object) |
351 { | 551 { |
352 XSCurve *curve; | 552 XSCurve *curve; |
353 | 553 |
354 g_return_if_fail(object != NULL); | 554 g_return_if_fail(object != NULL); |
355 g_return_if_fail(XS_IS_CURVE(object)); | 555 g_return_if_fail(XS_IS_CURVE(object)); |
356 | 556 |
357 curve = XS_CURVE(object); | 557 curve = XS_CURVE(object); |
558 | |
358 if (curve->pixmap) | 559 if (curve->pixmap) |
359 gdk_pixmap_unref(curve->pixmap); | 560 gdk_pixmap_unref(curve->pixmap); |
360 if (curve->point) | 561 |
361 g_free(curve->point); | 562 if (curve->ctlpoints) |
362 if (curve->ctlpoint) | 563 g_free(curve->ctlpoints); |
363 g_free(curve->ctlpoint); | |
364 | 564 |
365 (*GTK_OBJECT_CLASS(parent_class)->finalize) (object); | 565 (*GTK_OBJECT_CLASS(parent_class)->finalize) (object); |
366 } | 566 } |
367 | 567 |
368 | |
369 void xs_curve_get_vector(XSCurve *curve, int veclen, gfloat vector[]) | |
370 { | |
371 } | |
372 | |
373 void xs_curve_set_vector(XSCurve *curve, int veclen, gfloat vector[]) | |
374 { | |
375 } | |
376 |