mirror of https://github.com/aseprite/aseprite.git
514 lines
13 KiB
C
514 lines
13 KiB
C
/* ASE - Allegro Sprite Editor
|
|
* Copyright (C) 2001-2005, 2007, 2008 David A. Capello
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifndef USE_PRECOMPILED_HEADER
|
|
|
|
#include <allegro.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "jinete/jalert.h"
|
|
#include "jinete/jentry.h"
|
|
#include "jinete/jlist.h"
|
|
#include "jinete/jmanager.h"
|
|
#include "jinete/jmessage.h"
|
|
#include "jinete/jrect.h"
|
|
#include "jinete/jsystem.h"
|
|
#include "jinete/jview.h"
|
|
#include "jinete/jwidget.h"
|
|
#include "jinete/jwindow.h"
|
|
|
|
#include "effect/colcurve.h"
|
|
#include "modules/gui.h"
|
|
#include "widgets/curvedit.h"
|
|
|
|
#endif
|
|
|
|
#define SCR2EDIT_X(xpos) \
|
|
(curve_editor->x1 + \
|
|
((curve_editor->x2 - curve_editor->x1 + 1) \
|
|
* ((xpos) - widget->rc->x1 - widget->border_width.l) \
|
|
/ (jrect_w(widget->rc) - widget->border_width.l - widget->border_width.r)))
|
|
|
|
#define SCR2EDIT_Y(ypos) \
|
|
(curve_editor->y1 + \
|
|
((curve_editor->y2 - curve_editor->y1 + 1) \
|
|
* ((jrect_h(widget->rc) - widget->border_width.t - widget->border_width.b) \
|
|
- ((ypos) - widget->rc->y1 - widget->border_width.t)) \
|
|
/ (jrect_h(widget->rc) - widget->border_width.t - widget->border_width.b)))
|
|
|
|
#define EDIT2SCR_X(xpos) \
|
|
(widget->rc->x1 + widget->border_width.l \
|
|
+ ((jrect_w(widget->rc) - widget->border_width.l - widget->border_width.r) \
|
|
* ((xpos) - curve_editor->x1) \
|
|
/ (curve_editor->x2 - curve_editor->x1 + 1)))
|
|
|
|
#define EDIT2SCR_Y(ypos) \
|
|
(widget->rc->y1 \
|
|
+ (jrect_h(widget->rc) - widget->border_width.t - widget->border_width.b) \
|
|
- ((jrect_h(widget->rc) - widget->border_width.t - widget->border_width.b) \
|
|
* ((ypos) - curve_editor->y1) \
|
|
/ (curve_editor->y2 - curve_editor->y1 + 1)))
|
|
|
|
enum {
|
|
STATUS_STANDBY,
|
|
STATUS_MOVING_POINT,
|
|
STATUS_SCROLLING,
|
|
STATUS_SCALING,
|
|
};
|
|
|
|
typedef struct CurveEditor
|
|
{
|
|
Curve *curve;
|
|
int x1, y1;
|
|
int x2, y2;
|
|
int status;
|
|
CurvePoint *edit_point;
|
|
int *edit_x;
|
|
int *edit_y;
|
|
} CurveEditor;
|
|
|
|
static CurveEditor *curve_editor_data(JWidget widget);
|
|
static bool curve_editor_msg_proc(JWidget widget, JMessage msg);
|
|
|
|
static CurvePoint *curve_editor_get_more_close_point(JWidget widget, int x, int y, int **edit_x, int **edit_y);
|
|
|
|
static int edit_node_manual(CurvePoint *point);
|
|
|
|
JWidget curve_editor_new(Curve *curve, int x1, int y1, int x2, int y2)
|
|
{
|
|
JWidget widget = jwidget_new(curve_editor_type());
|
|
CurveEditor *curve_editor = jnew(CurveEditor, 1);
|
|
|
|
jwidget_add_hook(widget, curve_editor_type(),
|
|
curve_editor_msg_proc, curve_editor);
|
|
jwidget_focusrest(widget, TRUE);
|
|
|
|
widget->border_width.l = widget->border_width.r = 1;
|
|
widget->border_width.t = widget->border_width.b = 1;
|
|
widget->child_spacing = 0;
|
|
|
|
curve_editor->curve = curve;
|
|
curve_editor->x1 = x1;
|
|
curve_editor->y1 = y1;
|
|
curve_editor->x2 = x2;
|
|
curve_editor->y2 = y2;
|
|
curve_editor->status = STATUS_STANDBY;
|
|
curve_editor->edit_point = NULL;
|
|
|
|
/* TODO */
|
|
/* curve_editor->curve->type = CURVE_SPLINE; */
|
|
|
|
return widget;
|
|
}
|
|
|
|
int curve_editor_type(void)
|
|
{
|
|
static int type = 0;
|
|
if (!type)
|
|
type = ji_register_widget_type();
|
|
return type;
|
|
}
|
|
|
|
Curve *curve_editor_get_curve(JWidget widget)
|
|
{
|
|
CurveEditor *curve_editor = curve_editor_data(widget);
|
|
|
|
return curve_editor->curve;
|
|
}
|
|
|
|
static CurveEditor *curve_editor_data(JWidget widget)
|
|
{
|
|
return jwidget_get_data(widget, curve_editor_type());
|
|
}
|
|
|
|
static bool curve_editor_msg_proc(JWidget widget, JMessage msg)
|
|
{
|
|
CurveEditor *curve_editor = curve_editor_data(widget);
|
|
|
|
switch (msg->type) {
|
|
|
|
case JM_DESTROY:
|
|
jfree(curve_editor);
|
|
break;
|
|
|
|
case JM_REQSIZE: {
|
|
#if 0
|
|
msg->reqsize.w =
|
|
+ widget->border_width.l
|
|
+ ((curve_editor->x2 - curve_editor->x1 + 1))
|
|
+ widget->border_width.r;
|
|
|
|
msg->reqsize.h =
|
|
+ widget->border_width.t
|
|
+ ((curve_editor->y2 - curve_editor->y1 + 1))
|
|
+ widget->border_width.b;
|
|
#else
|
|
msg->reqsize.w = widget->border_width.l + 1 + widget->border_width.r;
|
|
msg->reqsize.h = widget->border_width.t + 1 + widget->border_width.b;
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
case JM_CHAR: {
|
|
switch (msg->key.scancode) {
|
|
|
|
case KEY_INSERT: {
|
|
int x = SCR2EDIT_X(jmouse_x(0));
|
|
int y = SCR2EDIT_Y(jmouse_y(0));
|
|
CurvePoint *point = curve_point_new(x, y);
|
|
|
|
/* TODO undo? */
|
|
curve_add_point(curve_editor->curve, point);
|
|
|
|
jwidget_dirty(widget);
|
|
jwidget_emit_signal(widget, SIGNAL_CURVE_EDITOR_CHANGE);
|
|
break;
|
|
}
|
|
|
|
case KEY_DEL: {
|
|
CurvePoint *point = curve_editor_get_more_close_point
|
|
(widget,
|
|
SCR2EDIT_X(jmouse_x(0)),
|
|
SCR2EDIT_Y(jmouse_y(0)),
|
|
NULL, NULL);
|
|
|
|
/* TODO undo? */
|
|
curve_remove_point(curve_editor->curve, point);
|
|
|
|
jwidget_dirty(widget);
|
|
jwidget_emit_signal(widget, SIGNAL_CURVE_EDITOR_CHANGE);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case JM_DRAW: {
|
|
BITMAP *bmp;
|
|
JLink link;
|
|
CurvePoint *point;
|
|
int *values;
|
|
int x, y, u;
|
|
|
|
bmp = create_bitmap(jrect_w(widget->rc), jrect_h(widget->rc));
|
|
clear_to_color(bmp, makecol (0, 0, 0));
|
|
|
|
/* draw border */
|
|
rect(bmp, 0, 0, bmp->w-1, bmp->h-1, makecol (255, 255, 0));
|
|
|
|
/* draw guides */
|
|
for (x=1; x<=3; x++)
|
|
vline(bmp, x*bmp->w/4, 1, bmp->h-2, makecol (128, 128, 0));
|
|
|
|
for (y=1; y<=3; y++)
|
|
hline(bmp, 1, y*bmp->h/4, bmp->w-2, makecol (128, 128, 0));
|
|
|
|
/* get curve values */
|
|
values = jmalloc(sizeof(int) * (curve_editor->x2-curve_editor->x1+1));
|
|
curve_get_values(curve_editor->curve,
|
|
curve_editor->x1,
|
|
curve_editor->x2, values);
|
|
|
|
/* draw curve */
|
|
for (x=widget->border_width.l;
|
|
x<jrect_w(widget->rc)-widget->border_width.r; x++) {
|
|
u = SCR2EDIT_X(widget->rc->x1+x);
|
|
u = MID(curve_editor->x1, u, curve_editor->x2);
|
|
|
|
y = values[u - curve_editor->x1];
|
|
y = MID(curve_editor->y1, y, curve_editor->y2);
|
|
|
|
putpixel(bmp, x, EDIT2SCR_Y(y)-widget->rc->y1,
|
|
makecol(255, 255, 255));
|
|
}
|
|
|
|
jfree(values);
|
|
|
|
/* draw nodes */
|
|
JI_LIST_FOR_EACH(curve_editor->curve->points, link) {
|
|
point = link->data;
|
|
|
|
x = EDIT2SCR_X(point->x) - widget->rc->x1;
|
|
y = EDIT2SCR_Y(point->y) - widget->rc->y1;
|
|
|
|
rect(bmp, x-2, y-2, x+2, y+2,
|
|
curve_editor->edit_point == point ?
|
|
makecol(255, 255, 0): makecol(0, 0, 255));
|
|
}
|
|
|
|
/* blit to screen */
|
|
blit(bmp, ji_screen,
|
|
0, 0, widget->rc->x1, widget->rc->y1, bmp->w, bmp->h);
|
|
destroy_bitmap(bmp);
|
|
return TRUE;
|
|
}
|
|
|
|
case JM_BUTTONPRESSED:
|
|
/* change scroll */
|
|
if (msg->any.shifts & KB_SHIFT_FLAG) {
|
|
curve_editor->status = STATUS_SCROLLING;
|
|
jmouse_set_cursor(JI_CURSOR_MOVE);
|
|
}
|
|
/* scaling */
|
|
/* else if (msg->shifts & KB_CTRL_FLAG) { */
|
|
/* curve_editor->status = STATUS_SCALING; */
|
|
/* jmouse_set_cursor(JI_CURSOR_MOVE); */
|
|
/* } */
|
|
/* show manual-entry dialog */
|
|
else if (msg->mouse.right) {
|
|
curve_editor->edit_point =
|
|
curve_editor_get_more_close_point(widget,
|
|
SCR2EDIT_X(msg->mouse.x),
|
|
SCR2EDIT_Y(msg->mouse.y),
|
|
NULL, NULL);
|
|
if (curve_editor->edit_point) {
|
|
jwidget_dirty(widget);
|
|
jwidget_flush_redraw(widget);
|
|
|
|
if (edit_node_manual(curve_editor->edit_point))
|
|
jwidget_emit_signal(widget, SIGNAL_CURVE_EDITOR_CHANGE);
|
|
|
|
curve_editor->edit_point = NULL;
|
|
jwidget_dirty(widget);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/* edit node */
|
|
else {
|
|
curve_editor->edit_point =
|
|
curve_editor_get_more_close_point(widget,
|
|
SCR2EDIT_X(msg->mouse.x),
|
|
SCR2EDIT_Y(msg->mouse.y),
|
|
&curve_editor->edit_x,
|
|
&curve_editor->edit_y);
|
|
|
|
curve_editor->status = STATUS_MOVING_POINT;
|
|
jmouse_set_cursor(JI_CURSOR_HAND);
|
|
}
|
|
|
|
jwidget_capture_mouse(widget);
|
|
/* continue in motion message... */
|
|
|
|
case JM_MOTION:
|
|
if (jwidget_has_capture(widget)) {
|
|
switch (curve_editor->status) {
|
|
|
|
case STATUS_SCROLLING: {
|
|
JWidget view = jwidget_get_view(widget);
|
|
JRect vp = jview_get_viewport_position(view);
|
|
int scroll_x, scroll_y;
|
|
|
|
jview_get_scroll(view, &scroll_x, &scroll_y);
|
|
jview_set_scroll(view,
|
|
scroll_x+jmouse_x(1)-jmouse_x(0),
|
|
scroll_y+jmouse_y(1)-jmouse_y(0));
|
|
|
|
jmouse_control_infinite_scroll(vp);
|
|
jrect_free(vp);
|
|
break;
|
|
}
|
|
|
|
/* case STATUS_SCALING: { */
|
|
/* JID view_id = jwidget_get_view(widget); */
|
|
/* JRect vp = jview_get_viewport_pos(view_id); */
|
|
/* int scroll_x, scroll_y; */
|
|
|
|
/* jview_get_scroll(view_id, &scroll_x, &scroll_y); */
|
|
/* jview_update(view_id); */
|
|
/* jview_set_scroll(view_id, */
|
|
/* scroll_x-(vp.x+vp.w/2), */
|
|
/* scroll_y-(vp.y+vp.h/2)); */
|
|
|
|
/* jmouse_control_infinite_scroll(vp.x, vp.y, vp.w, vp.h); */
|
|
/* break; */
|
|
/* } */
|
|
|
|
case STATUS_MOVING_POINT:
|
|
if (curve_editor->edit_point) {
|
|
/* int old_x = *curve_editor->edit_x; */
|
|
/* int old_y = *curve_editor->edit_y; */
|
|
/* int offset_x, offset_y; */
|
|
|
|
*curve_editor->edit_x = SCR2EDIT_X(msg->mouse.x);
|
|
*curve_editor->edit_y = SCR2EDIT_Y(msg->mouse.y);
|
|
|
|
*curve_editor->edit_x = MID(curve_editor->x1,
|
|
*curve_editor->edit_x,
|
|
curve_editor->x2);
|
|
|
|
*curve_editor->edit_y = MID(curve_editor->y1,
|
|
*curve_editor->edit_y,
|
|
curve_editor->y2);
|
|
|
|
/* if (curve_editor->edit_x == &curve_editor->edit_key->x && */
|
|
/* curve_editor->edit_y == &curve_editor->edit_key->y) { */
|
|
/* offset_x = (*curve_editor->edit_x) - old_x; */
|
|
/* offset_y = (*curve_editor->edit_y) - old_y; */
|
|
|
|
/* curve_editor->edit_key->px += offset_x; */
|
|
/* curve_editor->edit_key->py += offset_y; */
|
|
/* curve_editor->edit_key->nx += offset_x; */
|
|
/* curve_editor->edit_key->ny += offset_y; */
|
|
/* } */
|
|
|
|
/* TODO this should be optional */
|
|
jwidget_emit_signal(widget, SIGNAL_CURVE_EDITOR_CHANGE);
|
|
|
|
jwidget_dirty(widget);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#if 0 /* TODO */
|
|
/* if the mouse move above a curve_editor, the focus change to
|
|
this widget immediately */
|
|
else if (!jwidget_has_focus(widget)) {
|
|
jmanager_set_focus(widget);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case JM_BUTTONRELEASED:
|
|
if (jwidget_has_capture(widget)) {
|
|
jwidget_release_mouse(widget);
|
|
|
|
switch (curve_editor->status) {
|
|
|
|
case STATUS_SCROLLING:
|
|
jmouse_set_cursor(JI_CURSOR_NORMAL);
|
|
break;
|
|
|
|
/* case STATUS_SCALING: */
|
|
/* jmouse_set_cursor(JI_CURSOR_NORMAL); */
|
|
/* break; */
|
|
|
|
case STATUS_MOVING_POINT:
|
|
jmouse_set_cursor(JI_CURSOR_NORMAL);
|
|
jwidget_emit_signal(widget, SIGNAL_CURVE_EDITOR_CHANGE);
|
|
|
|
curve_editor->edit_point = NULL;
|
|
jwidget_dirty(widget);
|
|
break;
|
|
}
|
|
|
|
curve_editor->status = STATUS_STANDBY;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static CurvePoint *curve_editor_get_more_close_point(JWidget widget,
|
|
int x, int y,
|
|
int **edit_x,
|
|
int **edit_y)
|
|
{
|
|
#define CALCDIST(xx, yy) \
|
|
dx = point->xx-x; \
|
|
dy = point->yy-y; \
|
|
dist = sqrt (dx*dx + dy*dy); \
|
|
\
|
|
if (!point_found || dist <= dist_min) { \
|
|
point_found = point; \
|
|
dist_min = dist; \
|
|
\
|
|
if (edit_x) *edit_x = &point->xx; \
|
|
if (edit_y) *edit_y = &point->yy; \
|
|
}
|
|
|
|
CurveEditor *curve_editor = curve_editor_data (widget);
|
|
CurvePoint *point;
|
|
CurvePoint *point_found = NULL;
|
|
JLink link;
|
|
int dx, dy;
|
|
double dist, dist_min = 0;
|
|
|
|
JI_LIST_FOR_EACH(curve_editor->curve->points, link) {
|
|
point = link->data;
|
|
CALCDIST (x, y);
|
|
}
|
|
|
|
/* if (curve_editor->curve->union_type == PROP_SPLINE && edit_x && edit_y) { */
|
|
/* for (it=curve_editor->curve->keys; it; it=it->next) { */
|
|
/* key = it->data; */
|
|
|
|
/* if (it->prev) { */
|
|
/* CALCDIST(px, py); */
|
|
/* } */
|
|
|
|
/* if (it->next) { */
|
|
/* CALCDIST(nx, ny); */
|
|
/* } */
|
|
/* } */
|
|
/* } */
|
|
|
|
return point_found;
|
|
}
|
|
|
|
static int edit_node_manual(CurvePoint *point)
|
|
{
|
|
JWidget window, entry_x, entry_y, button_ok;
|
|
CurvePoint point_copy = *point;
|
|
char buf[512];
|
|
int res;
|
|
|
|
window = load_widget("pntprop.jid", "point_properties");
|
|
if (!window) {
|
|
jalert(_("Error<<Loading %s file||&OK"), "pntprop.jid");
|
|
return FALSE;
|
|
}
|
|
|
|
entry_x = jwidget_find_name(window, "x");
|
|
entry_y = jwidget_find_name(window, "y");
|
|
button_ok = jwidget_find_name(window, "button_ok");
|
|
|
|
sprintf(buf, "%d", point->x);
|
|
jwidget_set_text(entry_x, buf);
|
|
|
|
sprintf(buf, "%d", point->y);
|
|
jwidget_set_text(entry_y, buf);
|
|
|
|
jwindow_open_fg(window);
|
|
|
|
if (jwindow_get_killer(window) == button_ok) {
|
|
point->x = strtod(jwidget_get_text(entry_x), NULL);
|
|
point->y = strtod(jwidget_get_text(entry_y), NULL);
|
|
res = TRUE;
|
|
}
|
|
else {
|
|
point->x = point_copy.x;
|
|
point->y = point_copy.y;
|
|
res = FALSE;
|
|
}
|
|
|
|
jwidget_free(window);
|
|
return res;
|
|
}
|