diff --git a/Makefile b/Makefile index 6173637..59e3d03 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ OBJS = fped.o expr.o coord.o obj.o inst.o util.o error.o \ gui_tools.o XPMS = point.xpm vec.xpm frame.xpm \ - line.xpm rect.xpm pad.xpm circ.xpm arc.xpm meas.xpm + line.xpm rect.xpm pad.xpm circ.xpm meas.xpm CFLAGS_GTK = `pkg-config --cflags gtk+-2.0` LIBS_GTK = `pkg-config --libs gtk+-2.0` @@ -76,7 +76,7 @@ endif .fig.xpm: fig2dev -L xpm -Z 0.32 -S 4 $< | \ convert -crop 24x24+1+1 - - | \ - sed s/xpm__/xpm_`basename $@ .xpm`/ >$@ + sed "s/*.*\[]/*xpm_`basename $@ .xpm`[]/" >$@ all: fped diff --git a/README b/README index 801bcd4..9b379d0 100644 --- a/README +++ b/README @@ -277,9 +277,9 @@ loop n = 1, 3 and -loop n = 1, 3 +loop n = 1, 3.5 -both assigns the values 1, 2, and 3 to the variable "n". +both assign the values 1, 2, and 3 to the variable "n". When a loop is executed, the objects contained in the body of the enclosing frame are generated for each value of the variable. If diff --git a/coord.c b/coord.c index 12fbf3d..ca9c2fd 100644 --- a/coord.c +++ b/coord.c @@ -27,7 +27,7 @@ double mm_to_mil(double mm, int exponent) double mil_to_mm(double mil, int exponent) { - return mil*pow(MIL_IN_MM, exponent); + return mil*pow(MIL_IN_MM, exponent); } /* ----- vector operations ------------------------------------------------- */ @@ -160,5 +160,5 @@ unit_type dist_circle(struct coord p, struct coord c, unit_type r) unit_type d; d = hypot(p.x-c.x, p.y-c.y); - return fabs(d-r); + return fabs(d-r); } diff --git a/fpd.y b/fpd.y index 9e930bd..84e5b2a 100644 --- a/fpd.y +++ b/fpd.y @@ -342,8 +342,6 @@ vec: $$ = alloc_type(struct vec); $$->name = NULL; $$->base = $2; - if ($2) - $2->n_refs++; $$->x = $4; $$->y = $6; $$->n_refs = 0; @@ -367,6 +365,7 @@ base: yyerrorf(". without predecessor"); YYABORT; } + $$->n_refs++; } | ID { @@ -375,6 +374,7 @@ base: yyerrorf("unknown vector \"%s\"", $1); YYABORT; } + $$->n_refs++; } ; @@ -405,7 +405,7 @@ obj: $$ = new_obj(ot_arc); $$->base = $2; $$->u.arc.start = $3; - $$->u.arc.end = $3; + $$->u.arc.end = get_vec($3); $$->u.arc.width = $4; } | TOK_ARC base base base opt_expr diff --git a/gui.c b/gui.c index 00145cc..07011f4 100644 --- a/gui.c +++ b/gui.c @@ -88,7 +88,7 @@ static int find_var_in_frame(const struct frame *frame, const char *name) for (loop = frame->loops; loop; loop = loop->next) if (!strcmp(loop->var.name, name)) return 1; - return 0; + return 0; } @@ -106,14 +106,14 @@ static void unselect_var(void *data) { struct var *var = data; - label_in_box_bg(var->widget, COLOR_VAR_PASSIVE); + label_in_box_bg(var->widget, COLOR_VAR_PASSIVE); } static void edit_var(struct var *var) { inst_select_outside(var, unselect_var); - label_in_box_bg(var->widget, COLOR_VAR_EDITING); + label_in_box_bg(var->widget, COLOR_VAR_EDITING); status_set_type_entry("name ="); status_set_name(var->name); edit_unique(&var->name, validate_var_name, var); @@ -127,7 +127,7 @@ static void unselect_value(void *data) { struct value *value = data; - label_in_box_bg(value->widget, + label_in_box_bg(value->widget, value->row && value->row->table->active_row == value->row ? COLOR_CHOICE_SELECTED : COLOR_EXPR_PASSIVE); } @@ -136,7 +136,7 @@ static void unselect_value(void *data) static void edit_value(struct value *value) { inst_select_outside(value, unselect_value); - label_in_box_bg(value->widget, COLOR_EXPR_EDITING); + label_in_box_bg(value->widget, COLOR_EXPR_EDITING); edit_expr(&value->expr); } @@ -172,7 +172,7 @@ static GtkWidget *add_activator(GtkWidget *hbox, int active, static gboolean assignment_var_select_event(GtkWidget *widget, - GdkEventButton *event, gpointer data) + GdkEventButton *event, gpointer data) { edit_var(data); return TRUE; @@ -180,7 +180,7 @@ static gboolean assignment_var_select_event(GtkWidget *widget, static gboolean assignment_value_select_event(GtkWidget *widget, - GdkEventButton *event, gpointer data) + GdkEventButton *event, gpointer data) { edit_value(data); return TRUE; @@ -188,7 +188,7 @@ static gboolean assignment_value_select_event(GtkWidget *widget, static void build_assignment(GtkWidget *vbox, struct frame *frame, - struct table *table) + struct table *table) { GtkWidget *hbox, *field; char *expr; @@ -241,7 +241,7 @@ static void select_row(struct row *row) static gboolean table_var_select_event(GtkWidget *widget, - GdkEventButton *event, gpointer data) + GdkEventButton *event, gpointer data) { edit_var(data); return TRUE; @@ -249,7 +249,7 @@ static gboolean table_var_select_event(GtkWidget *widget, static gboolean table_value_select_event(GtkWidget *widget, - GdkEventButton *event, gpointer data) + GdkEventButton *event, gpointer data) { struct value *value = data; @@ -264,7 +264,7 @@ static gboolean table_value_select_event(GtkWidget *widget, static void build_table(GtkWidget *vbox, struct frame *frame, - struct table *table) + struct table *table) { GtkWidget *tab, *field; GtkWidget *evbox; @@ -290,7 +290,7 @@ static void build_table(GtkWidget *vbox, struct frame *frame, gtk_container_add(GTK_CONTAINER(evbox), tab); col = get_color(COLOR_VAR_TABLE_SEP); gtk_widget_modify_bg(GTK_WIDGET(evbox), - GTK_STATE_NORMAL, &col); + GTK_STATE_NORMAL, &col); gtk_table_set_row_spacings(GTK_TABLE(tab), 1); gtk_table_set_col_spacings(GTK_TABLE(tab), 1); @@ -335,7 +335,7 @@ static void build_table(GtkWidget *vbox, struct frame *frame, static gboolean loop_var_select_event(GtkWidget *widget, - GdkEventButton *event, gpointer data) + GdkEventButton *event, gpointer data) { struct loop *loop = data; @@ -345,7 +345,7 @@ static gboolean loop_var_select_event(GtkWidget *widget, static gboolean loop_from_select_event(GtkWidget *widget, - GdkEventButton *event, gpointer data) + GdkEventButton *event, gpointer data) { struct loop *loop = data; @@ -355,7 +355,7 @@ static gboolean loop_from_select_event(GtkWidget *widget, static gboolean loop_to_select_event(GtkWidget *widget, - GdkEventButton *event, gpointer data) + GdkEventButton *event, gpointer data) { struct loop *loop = data; @@ -365,7 +365,7 @@ static gboolean loop_to_select_event(GtkWidget *widget, static gboolean loop_select_event(GtkWidget *widget, GdkEventButton *event, - gpointer data) + gpointer data) { struct loop *loop = data; @@ -376,7 +376,7 @@ static gboolean loop_select_event(GtkWidget *widget, GdkEventButton *event, static void build_loop(GtkWidget *vbox, struct frame *frame, - struct loop *loop) + struct loop *loop) { GtkWidget *hbox, *field, *label; char *expr; @@ -484,7 +484,7 @@ static void unselect_frame(void *data) * change here doesn't matter if selecting a different frame.) * So we revert from "editing" to "selected". */ - label_in_box_bg(frame->label, COLOR_FRAME_SELECTED); + label_in_box_bg(frame->label, COLOR_FRAME_SELECTED); } @@ -508,7 +508,7 @@ static void select_frame(struct frame *frame) static gboolean frame_select_event(GtkWidget *widget, GdkEventButton *event, - gpointer data) + gpointer data) { if (active_frame != data) select_frame(data); @@ -543,7 +543,7 @@ static GtkWidget *build_frame_label(struct frame *frame) static gboolean frame_ref_select_event(GtkWidget *widget, GdkEventButton *event, - gpointer data) + gpointer data) { struct obj *obj = data; @@ -591,13 +591,13 @@ static void build_frames(GtkWidget *vbox) for (frame = root_frame; frame; frame = frame->prev) { label = build_frame_label(frame); gtk_table_attach_defaults(GTK_TABLE(tab), label, - 0, 1, n*2, n*2+1); + 0, 1, n*2, n*2+1); refs = build_frame_refs(frame); gtk_table_attach_defaults(GTK_TABLE(tab), refs, - 1, 2, n*2, n*2+1); + 1, 2, n*2, n*2+1); vars = build_vars(frame); gtk_table_attach_defaults(GTK_TABLE(tab), vars, - 1, 2, n*2+1, n*2+2); + 1, 2, n*2+1, n*2+2); n++; } gtk_widget_show_all(tab); @@ -648,6 +648,7 @@ static void make_center_area(GtkWidget *vbox) void change_world(void) { + tool_reset(); inst_deselect(); status_begin_reporting(); instantiate(); diff --git a/gui_canvas.c b/gui_canvas.c index f7d2f9a..3736ffd 100644 --- a/gui_canvas.c +++ b/gui_canvas.c @@ -19,6 +19,7 @@ #include "gui_inst.h" #include "gui_style.h" #include "gui_status.h" +#include "gui_tools.h" #include "gui.h" #include "gui_canvas.h" @@ -27,6 +28,9 @@ static struct draw_ctx ctx; static struct coord curr_pos; static struct coord user_origin = { 0, 0 }; +static int dragging = 0; +static struct coord drag_start; + /* ----- status display ---------------------------------------------------- */ @@ -105,6 +109,12 @@ void redraw(void) static void drag_left(struct coord pos) { + if (!dragging) + return; + if (hypot(pos.x-drag_start.x, pos.y-drag_start.y)/ctx.scale < + DRAG_MIN_R) + return; + tool_drag(&ctx, pos); } @@ -114,7 +124,7 @@ static void drag_middle(struct coord pos) static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, - gpointer data) + gpointer data) { struct coord pos = canvas_to_coord(&ctx, event->x, event->y); @@ -122,6 +132,8 @@ static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, curr_pos.y = event->y; if (event->state & GDK_BUTTON1_MASK) drag_left(pos); + else + tool_hover(&ctx, pos); if (event->state & GDK_BUTTON2_MASK) drag_middle(pos); update_pos(pos); @@ -133,13 +145,23 @@ static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event, - gpointer data) + gpointer data) { struct coord pos = canvas_to_coord(&ctx, event->x, event->y); const struct inst *prev; switch (event->button) { case 1: + if (dragging) { + fprintf(stderr, "HUH ?!?\n"); + tool_cancel_drag(&ctx); + dragging = 0; + } + if (tool_consider_drag(&ctx, pos)) { + dragging = 1; + drag_start = pos; + break; + } prev = selected_inst; inst_deselect(); inst_select(&ctx, pos); @@ -156,8 +178,20 @@ static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event, static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event, - gpointer data) + gpointer data) { + struct coord pos = canvas_to_coord(&ctx, event->x, event->y); + + if (dragging) { + dragging = 0; + if (hypot(pos.x-drag_start.x, pos.y-drag_start.y)/ctx.scale < + DRAG_MIN_R) + tool_cancel_drag(&ctx); + else { + if (tool_end_drag(&ctx, pos)) + change_world(); + } + } return TRUE; } @@ -197,7 +231,7 @@ static void zoom_out(struct coord pos) static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, - gpointer data) + gpointer data) { struct coord pos = canvas_to_coord(&ctx, event->x, event->y); @@ -219,7 +253,7 @@ static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event, - gpointer data) + gpointer data) { struct coord pos = canvas_to_coord(&ctx, curr_pos.x, curr_pos.y); @@ -258,7 +292,7 @@ static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event, static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, - gpointer data) + gpointer data) { static int first = 1; if (first) { @@ -274,7 +308,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, static gboolean enter_notify_event(GtkWidget *widget, GdkEventCrossing *event, - gpointer data) + gpointer data) { gtk_widget_grab_focus(widget); return TRUE; @@ -282,8 +316,12 @@ static gboolean enter_notify_event(GtkWidget *widget, GdkEventCrossing *event, static gboolean leave_notify_event(GtkWidget *widget, GdkEventCrossing *event, - gpointer data) + gpointer data) { + if (dragging) + tool_cancel_drag(&ctx); + tool_dehover(&ctx); + dragging = 0; return TRUE; } diff --git a/gui_inst.c b/gui_inst.c index 41c7e02..7b00517 100644 --- a/gui_inst.c +++ b/gui_inst.c @@ -49,9 +49,9 @@ struct coord canvas_to_coord(const struct draw_ctx *ctx, int x, int y) x -= ctx->widget->allocation.width/2; y -= ctx->widget->allocation.height/2; - y = -y; - pos.x = x*ctx->scale+ctx->center.x; - pos.y = y*ctx->scale+ctx->center.y; + y = -y; + pos.x = x*ctx->scale+ctx->center.x; + pos.y = y*ctx->scale+ctx->center.y; return pos; } @@ -59,27 +59,11 @@ struct coord canvas_to_coord(const struct draw_ctx *ctx, int x, int y) /* ----- drawing primitives ------------------------------------------------ */ -static void draw_arc(struct draw_ctx *ctx, GdkGC *gc, int fill, - int x, int y, int r, double a1, double a2) -{ - if (a1 == a2) - a2 = a1+360; - gdk_draw_arc(DA, gc, fill, x-r, y-r, 2*r, 2*r, a1*64, (a2-a1)*64); -} - - -static void draw_circle(struct draw_ctx *ctx, GdkGC *gc, int fill, - int x, int y, int r) -{ - draw_arc(ctx, gc, fill, x, y, r, 0, 360); -} - - static void draw_eye(struct draw_ctx *ctx, GdkGC *gc, struct coord center, int r1, int r2) { - draw_circle(ctx, gc, TRUE, center.x, center.y, r1); - draw_circle(ctx, gc, FALSE, center.x, center.y, r2); + draw_circle(DA, gc, TRUE, center.x, center.y, r1); + draw_circle(DA, gc, FALSE, center.x, center.y, r2); } @@ -159,6 +143,16 @@ unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos, } +void gui_hover_vec(struct inst *self, struct draw_ctx *ctx) +{ + struct coord center = translate(ctx, self->u.rect.end); + GdkGC *gc; + + gc = gc_vec[mode_hover]; + draw_circle(DA, gc, FALSE, center.x, center.y, VEC_EYE_R); +} + + void gui_draw_vec(struct inst *self, struct draw_ctx *ctx) { struct coord from = translate(ctx, self->base); @@ -168,7 +162,7 @@ void gui_draw_vec(struct inst *self, struct draw_ctx *ctx) gc = gc_vec[get_mode(self)]; draw_arrow(ctx, gc, TRUE, from, to, VEC_ARROW_LEN, VEC_ARROW_ANGLE); gdk_draw_line(DA, gc, from.x, from.y, to.x, to.y); - draw_circle(ctx, gc, FALSE, to.x, to.y, VEC_EYE_R); + draw_circle(DA, gc, FALSE, to.x, to.y, VEC_EYE_R); } @@ -295,8 +289,8 @@ unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale) p = rotate_r(c, self->u.arc.r, self->u.arc.a2); d = hypot(pos.x-p.x, pos.y-p.y); - if (d < d_min) - d_min = d; + if (d < d_min) + d_min = d; if (d_min/scale <= r) return d; @@ -328,7 +322,7 @@ void gui_draw_arc(struct inst *self, struct draw_ctx *ctx) gc = gc_obj[get_mode(self)]; set_width(gc, self->u.arc.width/ctx->scale); - draw_arc(ctx, gc, FALSE, center.x, center.y, + draw_arc(DA, gc, FALSE, center.x, center.y, self->u.arc.r/ctx->scale, self->u.arc.a1, self->u.arc.a2); } @@ -399,6 +393,25 @@ void gui_draw_meas(struct inst *self, struct draw_ctx *ctx) /* ----- frame ------------------------------------------------------------- */ +unit_type gui_dist_frame(struct inst *self, struct coord pos, unit_type scale) +{ + unit_type d; + + d = dist_point(pos, self->base)/scale; + return d > FRAME_EYE_R2 ? -1 : d; +} + + +void gui_hover_frame(struct inst *self, struct draw_ctx *ctx) +{ + struct coord center = translate(ctx, self->base); + GdkGC *gc; + + gc = gc_frame[mode_hover]; + draw_circle(DA, gc, FALSE, center.x, center.y, FRAME_EYE_R2); +} + + void gui_draw_frame(struct inst *self, struct draw_ctx *ctx) { struct coord center = translate(ctx, self->base); diff --git a/gui_inst.h b/gui_inst.h index 893fb58..0590dc4 100644 --- a/gui_inst.h +++ b/gui_inst.h @@ -38,6 +38,7 @@ unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale); unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale); unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale); unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale); +unit_type gui_dist_frame(struct inst *self, struct coord pos, unit_type scale); void gui_draw_vec(struct inst *self, struct draw_ctx *ctx); void gui_draw_line(struct inst *self, struct draw_ctx *ctx); @@ -47,4 +48,7 @@ void gui_draw_arc(struct inst *self, struct draw_ctx *ctx); void gui_draw_meas(struct inst *self, struct draw_ctx *ctx); void gui_draw_frame(struct inst *self, struct draw_ctx *ctx); +void gui_hover_vec(struct inst *self, struct draw_ctx *ctx); +void gui_hover_frame(struct inst *self, struct draw_ctx *ctx); + #endif /* !GUI_INST_H */ diff --git a/gui_status.c b/gui_status.c index f49fa4d..c99825a 100644 --- a/gui_status.c +++ b/gui_status.c @@ -367,7 +367,7 @@ void edit_y(struct expr **expr) static gboolean changed(GtkWidget *widget, GdkEventMotion *event, - gpointer data) + gpointer data) { struct edit_ops *ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops"); @@ -381,7 +381,7 @@ static gboolean changed(GtkWidget *widget, GdkEventMotion *event, static gboolean activate(GtkWidget *widget, GdkEventMotion *event, - gpointer data) + gpointer data) { struct edit_ops *ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops"); @@ -462,9 +462,9 @@ static GtkWidget *add_entry(GtkWidget *tab, int col, int row) col, col+1, row, row+1); g_signal_connect(G_OBJECT(entry), "changed", - G_CALLBACK(changed), entry); + G_CALLBACK(changed), entry); g_signal_connect(G_OBJECT(entry), "activate", - G_CALLBACK(activate), entry); + G_CALLBACK(activate), entry); return entry; } diff --git a/gui_style.c b/gui_style.c index a97163a..9e9bedc 100644 --- a/gui_style.c +++ b/gui_style.c @@ -23,6 +23,7 @@ GdkGC *gc_bg; +GdkGC *gc_drag; GdkGC *gc_vec[mode_n]; GdkGC *gc_obj[mode_n]; GdkGC *gc_pad[mode_n]; @@ -59,6 +60,7 @@ static void style(GdkGC *gcs[mode_n], void gui_setup_style(GdkDrawable *drawable) { gc_bg = gc("#000000", 0); + gc_drag = gc("#ffffff", 2); /* inactive in+path active act+path selected */ style(gc_vec, "#202000", "#404020", "#909040", "#c0c080", "#ffff80"); style(gc_obj, "#006060", INVALID, "#00ffff", INVALID, "#ffff80"); @@ -66,4 +68,6 @@ void gui_setup_style(GdkDrawable *drawable) style(gc_ptext, "#404040", INVALID, "#ffffff", INVALID, "#ffffff"); style(gc_meas, "#280040", INVALID, "#ff00ff", INVALID, "#ffff80"); style(gc_frame, "#004000", "#205020", "#00ff00", INVALID, INVALID); + + gc_frame[mode_hover] = gc_vec[mode_hover] = gc("#c00000", 1); } diff --git a/gui_style.h b/gui_style.h index edbba49..8cd73e0 100644 --- a/gui_style.h +++ b/gui_style.h @@ -46,6 +46,8 @@ #define SELECT_R 6 /* pixels within which we select */ +#define DRAG_MIN_R 5 + #define MIN_FONT_SCALE 0.20 /* don't scale fonts below this */ @@ -73,11 +75,15 @@ #define COLOR_VAR_TABLE_SEP "black" +#define TOOL_UNSELECTED "#dcdad5" +#define TOOL_SELECTED "red" + /* ----- canvas drawing styles --------------------------------------------- */ extern GdkGC *gc_bg; +extern GdkGC *gc_drag; extern GdkGC *gc_vec[mode_n]; extern GdkGC *gc_obj[mode_n]; extern GdkGC *gc_pad[mode_n]; diff --git a/gui_tools.c b/gui_tools.c index 8986ca4..e5e76bc 100644 --- a/gui_tools.c +++ b/gui_tools.c @@ -13,11 +13,15 @@ #include +#include "util.h" +#include "inst.h" +#include "obj.h" #include "gui_util.h" +#include "gui_style.h" +#include "gui_inst.h" #include "gui_tools.h" -#include "icons/arc.xpm" #include "icons/circ.xpm" #include "icons/frame.xpm" #include "icons/line.xpm" @@ -28,13 +32,292 @@ #include "icons/vec.xpm" -static GtkToolItem *tool_button(GtkWidget *bar, GdkDrawable *drawable, - char **xpm, GtkToolItem *last) +#define DA GDK_DRAWABLE(ctx->widget->window) + + +struct tool_ops { + struct pix_buf *(*drag)(struct draw_ctx *ctx, struct inst *from, + struct coord to); + int (*end)(struct draw_ctx *ctx, struct inst *from, struct inst *to); +}; + + +static GtkWidget *ev_point; +static GtkWidget *active_tool; +static struct tool_ops *active_ops = NULL; +static struct inst *hover_inst = NULL; + +static struct inst *drag; +static struct pix_buf *pix_buf; + +static struct tool_ops vec_ops; +static struct tool_ops frame_ops; +static struct tool_ops pad_ops; +static struct tool_ops circ_ops; +static struct tool_ops meas_ops; + + +static struct obj *new_obj(enum obj_type type, struct inst *base) +{ + struct obj *obj, **walk; + + obj = alloc_type(struct obj); + obj->type = type; + obj->base = inst_get_ref(base); + obj->next = NULL; + obj->lineno = 0; + for (walk = &active_frame->objs; *walk; walk = &(*walk)->next); + *walk = obj; + return obj; +} + + +/* ----- line -------------------------------------------------------------- */ + + +static struct pix_buf *drag_new_line(struct draw_ctx *ctx, + struct inst *from, struct coord to) +{ + struct coord pos; + struct pix_buf *buf; + + pos = translate(ctx, inst_get_point(from)); + to = translate(ctx, to); + buf = save_pix_buf(DA, pos.x, pos.y, to.x, to.y, 1); + gdk_draw_line(DA, gc_drag, pos.x, pos.y, to.x, to.y); + return buf; +} + + +static int end_new_line(struct draw_ctx *ctx, + struct inst *from, struct inst *to) +{ + struct obj *obj; + + if (from == to) + return 0; + obj = new_obj(ot_line, from); + obj->u.line.other = inst_get_ref(to); + obj->u.line.width = NULL; + return 1; +} + + +static struct tool_ops line_ops = { + .drag = drag_new_line, + .end = end_new_line, +}; + + +/* ----- rect -------------------------------------------------------------- */ + + +static void swap_coord(unit_type *a, unit_type *b) +{ + unit_type tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + + +static struct pix_buf *drag_new_rect(struct draw_ctx *ctx, + struct inst *from, struct coord to) +{ + struct coord pos; + struct pix_buf *buf; + + pos = translate(ctx, inst_get_point(from)); + to = translate(ctx, to); + if (pos.x > to.x) + swap_coord(&pos.x, &to.x); + if (pos.y > to.y) + swap_coord(&pos.y, &to.y); + buf = save_pix_buf(DA, pos.x, pos.y, to.x, to.y, 1); + gdk_draw_rectangle(DA, gc_drag, FALSE, + pos.x, pos.y, to.x-pos.x, to.y-pos.y); + return buf; +} + + +static int end_new_rect(struct draw_ctx *ctx, + struct inst *from, struct inst *to) +{ + struct obj *obj; + + if (from == to) + return 0; + obj = new_obj(ot_rect, from); + obj->u.rect.other = inst_get_ref(to); + obj->u.rect.width = NULL; + return 1; +} + + +static struct tool_ops rect_ops = { + .drag = drag_new_rect, + .end = end_new_rect, +}; + + +/* ----- circ -------------------------------------------------------------- */ + + +static struct pix_buf *drag_new_circ(struct draw_ctx *ctx, + struct inst *from, struct coord to) +{ + struct coord pos; + struct pix_buf *buf; + double r; + + pos = translate(ctx, inst_get_point(from)); + to = translate(ctx, to); + r = hypot(to.x-pos.x, to.y-pos.y); + buf = save_pix_buf(DA, pos.x-r, pos.y-r, pos.x+r, pos.y+r, 1); + draw_circle(DA, gc_drag, FALSE, pos.x, pos.y, r); + return buf; +} + + +static int end_new_circ(struct draw_ctx *ctx, + struct inst *from, struct inst *to) +{ + struct obj *obj; + + if (from == to) + return 0; + obj = new_obj(ot_arc, from); + obj->u.arc.start = inst_get_ref(to); + obj->u.arc.end = inst_get_ref(to); + obj->u.arc.width = NULL; + return 1; +} + + +static struct tool_ops circ_ops = { + .drag = drag_new_circ, + .end = end_new_circ, +}; + + +/* ----- mouse actions ----------------------------------------------------- */ + + +void tool_dehover(struct draw_ctx *ctx) +{ + if (hover_inst) + inst_hover(hover_inst, ctx, 0); + hover_inst = NULL; +} + + +void tool_hover(struct draw_ctx *ctx, struct coord pos) +{ + struct inst *inst; + + if (!active_ops) + return; + inst = inst_find_point(ctx, pos); + if (inst != hover_inst) + tool_dehover(ctx); + if (inst) { + inst_hover(inst, ctx, 1); + hover_inst = inst; + } +} + + +int tool_consider_drag(struct draw_ctx *ctx, struct coord pos) +{ + if (!active_ops) + return 0; + drag = inst_find_point(ctx, pos); + if (!drag) + return 0; + pix_buf = NULL; + return 1; +} + + +void tool_drag(struct draw_ctx *ctx, struct coord to) +{ + if (pix_buf) + restore_pix_buf(pix_buf); + tool_hover(ctx, to); + pix_buf = active_ops->drag(ctx, drag, to); +} + + +void tool_cancel_drag(struct draw_ctx *ctx) +{ + tool_dehover(ctx); + tool_reset(); + if (pix_buf) + restore_pix_buf(pix_buf); + drag = NULL; + active_ops = NULL; +} + + +int tool_end_drag(struct draw_ctx *ctx, struct coord to) +{ + struct inst *from = drag; + struct inst *end; + struct tool_ops *ops = active_ops; + + tool_cancel_drag(ctx); + end = inst_find_point(ctx, to); + if (end) + return ops->end(ctx, from, end); + return 0; +} + + +/* ----- tool bar creation ------------------------------------------------- */ + + +static void tool_select(GtkWidget *evbox, struct tool_ops *ops) +{ + GdkColor col; + + if (active_tool) { + col = get_color(TOOL_UNSELECTED); + gtk_widget_modify_bg(active_tool, GTK_STATE_NORMAL, &col); + active_tool = NULL; + } + col = get_color(TOOL_SELECTED); + gtk_widget_modify_bg(evbox, GTK_STATE_NORMAL, &col); + active_tool = evbox; + active_ops = ops; +} + + +void tool_reset(void) +{ + tool_select(ev_point, NULL); +} + + +static gboolean tool_button_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + tool_select(widget, data); + return TRUE; +} + + +static GtkWidget *tool_button(GtkWidget *bar, GdkDrawable *drawable, + char **xpm, GtkWidget *last_evbox, struct tool_ops *ops) { GdkPixmap *pixmap; - GtkWidget *image; + GtkWidget *image, *evbox; GtkToolItem *item; + GtkToolItem *last = NULL; + if (last_evbox) + last = GTK_TOOL_ITEM(gtk_widget_get_ancestor(last_evbox, + GTK_TYPE_TOOL_ITEM)); pixmap = gdk_pixmap_create_from_xpm_d(drawable, NULL, NULL, xpm); image = gtk_image_new_from_pixmap(pixmap, NULL); @@ -50,38 +333,44 @@ static GtkToolItem *tool_button(GtkWidget *bar, GdkDrawable *drawable, item = gtk_radio_tool_button_new(NULL); gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), image); #else - item = gtk_tool_item_new(); - gtk_container_add(GTK_CONTAINER(item), image); + evbox = gtk_event_box_new(); + gtk_misc_set_padding(GTK_MISC(image), 1, 1); + gtk_container_add(GTK_CONTAINER(evbox), image); + g_signal_connect(G_OBJECT(evbox), "button_press_event", + G_CALLBACK(tool_button_press_event), ops); - gtk_container_set_border_width(GTK_CONTAINER(item), 1); + item = gtk_tool_item_new(); + gtk_container_add(GTK_CONTAINER(item), evbox); + + gtk_container_set_border_width(GTK_CONTAINER(item), 0); #endif gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1); - return item; + return evbox; } GtkWidget *gui_setup_tools(GdkDrawable *drawable) { GtkWidget *bar; - GtkToolItem *last; + GtkWidget *last; bar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS); gtk_toolbar_set_orientation(GTK_TOOLBAR(bar), GTK_ORIENTATION_VERTICAL); -//gtk_container_set_border_width(GTK_CONTAINER(bar), 5); - last = tool_button(bar, drawable, xpm_point, NULL); - last = tool_button(bar, drawable, xpm_vec, last); - last = tool_button(bar, drawable, xpm_frame, last); - last = tool_button(bar, drawable, xpm_pad, last); - last = tool_button(bar, drawable, xpm_line, last); - last = tool_button(bar, drawable, xpm_rect, last); - last = tool_button(bar, drawable, xpm_circ, last); - last = tool_button(bar, drawable, xpm_arc, last); - last = tool_button(bar, drawable, xpm_meas, last); + ev_point = tool_button(bar, drawable, xpm_point, NULL, NULL); + last = tool_button(bar, drawable, xpm_vec, ev_point, &vec_ops); + last = tool_button(bar, drawable, xpm_frame, last, &frame_ops); + last = tool_button(bar, drawable, xpm_pad, last, &pad_ops); + last = tool_button(bar, drawable, xpm_line, last, &line_ops); + last = tool_button(bar, drawable, xpm_rect, last, &rect_ops); + last = tool_button(bar, drawable, xpm_circ, last, &circ_ops); + last = tool_button(bar, drawable, xpm_meas, last, &meas_ops); + + tool_reset(); return bar; } diff --git a/gui_tools.h b/gui_tools.h index 5099801..300c393 100644 --- a/gui_tools.h +++ b/gui_tools.h @@ -16,6 +16,17 @@ #include +#include "inst.h" + + +void tool_dehover(struct draw_ctx *ctx); +void tool_hover(struct draw_ctx *ctx, struct coord pos); +int tool_consider_drag(struct draw_ctx *ctx, struct coord pos); +void tool_drag(struct draw_ctx *ctx, struct coord to); +void tool_cancel_drag(struct draw_ctx *ctx); +int tool_end_drag(struct draw_ctx *ctx, struct coord to); + +void tool_reset(void); GtkWidget *gui_setup_tools(GdkDrawable *drawable); diff --git a/gui_util.c b/gui_util.c index c645668..2fe0fb8 100644 --- a/gui_util.c +++ b/gui_util.c @@ -15,6 +15,7 @@ #include #include +#include "util.h" #include "gui_style.h" #include "gui.h" #include "gui_util.h" @@ -47,6 +48,74 @@ void set_width(GdkGC *gc, int width) } +/* ----- backing store ----------------------------------------------------- */ + + +struct pix_buf *save_pix_buf(GdkDrawable *da, int xa, int ya, int xb, int yb, + int border) +{ + struct pix_buf *buf; + int tmp; + int w, h; + + if (xa > xb) { + tmp = xa; + xa = xb; + xb = tmp; + } + if (ya > yb) { + tmp = ya; + ya = yb; + yb = tmp; + } + buf = alloc_type(struct pix_buf); + buf->da = da; + buf->x = xa-border; + buf->y = ya-border; + w = xb-xa+1+2*border; + h = yb-ya+1+2*border; + if (buf->x < 0) { + w += buf->x; + buf->x = 0; + } + if (buf->y < 0) { + w += buf->y; + buf->y = 0; + } + buf->buf = gdk_pixbuf_get_from_drawable(NULL, da, NULL, + buf->x, buf->y, 0, 0, w, h); + return buf; +} + + +void restore_pix_buf(struct pix_buf *buf) +{ + gdk_draw_pixbuf(buf->da, NULL, buf->buf, 0, 0, buf->x, buf->y, -1, -1, + GDK_RGB_DITHER_NORMAL, 0, 0); + g_object_unref(G_OBJECT(buf->buf)); + free(buf); +} + + +/* ----- arcs and circles -------------------------------------------------- */ + + +void draw_arc(GdkDrawable *da, GdkGC *gc, int fill, + int x, int y, int r, double a1, double a2) +{ + if (a1 == a2) + a2 = a1+360; + gdk_draw_arc(da, gc, fill, x-r, y-r, 2*r, 2*r, a1*64, (a2-a1)*64); +} + + +void draw_circle(GdkDrawable *da, GdkGC *gc, int fill, + int x, int y, int r) +{ + draw_arc(da, gc, fill, x, y, r, 0, 360); +} + + /* ----- labels in a box --------------------------------------------------- */ diff --git a/gui_util.h b/gui_util.h index 0fc742d..6683e59 100644 --- a/gui_util.h +++ b/gui_util.h @@ -17,10 +17,26 @@ #include +struct pix_buf { + GdkDrawable *da; + int x, y; + GdkPixbuf *buf; +}; + + GdkColor get_color(const char *spec); void set_width(GdkGC *gc, int width); +struct pix_buf *save_pix_buf(GdkDrawable *da, int xa, int ya, int xb, int yb, + int border); +void restore_pix_buf(struct pix_buf *buf); + +void draw_arc(GdkDrawable *da, GdkGC *gc, int fill, + int x, int y, int r, double a1, double a2); +void draw_circle(GdkDrawable *da, GdkGC *gc, int fill, + int x, int y, int r); + GtkWidget *label_in_box_new(const char *s); GtkWidget *box_of_label(GtkWidget *label); void label_in_box_bg(GtkWidget *box, const char *color); diff --git a/inst.c b/inst.c index 993762f..3ece47f 100644 --- a/inst.c +++ b/inst.c @@ -20,6 +20,7 @@ #include "expr.h" #include "obj.h" #include "gui_status.h" +#include "gui_status.h" #include "gui_inst.h" #include "inst.h" @@ -28,8 +29,9 @@ struct inst_ops { void (*debug)(struct inst *self); void (*save)(FILE *file, struct inst *self); void (*draw)(struct inst *self, struct draw_ctx *ctx); + void (*hover)(struct inst *self, struct draw_ctx *ctx); unit_type (*distance)(struct inst *self, struct coord pos, - unit_type scale); + unit_type scale); void (*select)(struct inst *self); }; @@ -169,6 +171,60 @@ selected: } +struct inst *inst_find_point(const struct draw_ctx *ctx, struct coord pos) +{ + struct inst *inst, *found; + int best_dist = 0; /* keep gcc happy */ + int dist; + + found = NULL; + for (inst = insts[ip_frame]; inst; inst = inst->next) { + if (!inst->active || !inst->ops->distance) + continue; + dist = inst->ops->distance(inst, pos, ctx->scale); + if (dist >= 0 && (!found || best_dist > dist)) { + found = inst; + best_dist = dist; + } + } + if (found) + return found; + + for (inst = insts[ip_vec]; inst; inst = inst->next) { + if (!inst->active || !inst->ops->distance) + continue; + dist = inst->ops->distance(inst, pos, ctx->scale); + if (dist >= 0 && (!found || best_dist > dist)) { + found = inst; + best_dist = dist; + } + } + return found; +} + + +struct coord inst_get_point(const struct inst *inst) +{ + if (inst->ops == &vec_ops) + return inst->u.rect.end; + if (inst->ops == &frame_ops) + return inst->base; + abort(); +} + + +struct vec *inst_get_ref(const struct inst *inst) +{ + if (inst->ops == &vec_ops) { + inst->vec->n_refs++; + return inst->vec; + } + if (inst->ops == &frame_ops) + return NULL; + abort(); +} + + void inst_deselect(void) { if (selected_inst) @@ -298,6 +354,7 @@ static void vec_op_select(struct inst *self) static struct inst_ops vec_ops = { .debug = vec_op_debug, .draw = gui_draw_vec, + .hover = gui_hover_vec, .distance = gui_dist_vec, .select = vec_op_select, }; @@ -586,6 +643,7 @@ static void frame_op_debug(struct inst *self) static struct inst_ops frame_ops = { .debug = frame_op_debug, .draw = gui_draw_frame, + .hover = gui_hover_frame, }; @@ -685,6 +743,17 @@ void inst_draw(struct draw_ctx *ctx) } +void inst_hover(struct inst *inst, struct draw_ctx *ctx, int on) +{ + if (!inst->ops->hover) + return; + if (on) + inst->ops->hover(inst, ctx); + else + inst->ops->draw(inst, ctx); +} + + void inst_debug(void) { enum inst_prio prio; diff --git a/inst.h b/inst.h index 6d7bf7c..c8cdda8 100644 --- a/inst.h +++ b/inst.h @@ -26,6 +26,7 @@ enum mode { mode_active, /* on active frame */ mode_active_in_path, /* active and is in path to selected */ mode_selected, /* item is selected */ + mode_hover, /* hovering over item's contact area */ mode_n /* number of modes */ }; @@ -78,6 +79,10 @@ void inst_select_outside(void *item, void (*deselect)(void *item)); int inst_select(const struct draw_ctx *ctx, struct coord pos); void inst_deselect(void); +struct inst *inst_find_point(const struct draw_ctx *ctx, struct coord pos); +struct coord inst_get_point(const struct inst *inst); +struct vec *inst_get_ref(const struct inst *inst); + int inst_vec(struct vec *vec, struct coord base); int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width); int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width); @@ -100,6 +105,7 @@ void inst_commit(void); void inst_revert(void); void inst_draw(struct draw_ctx *ctx); +void inst_hover(struct inst *inst, struct draw_ctx *ctx, int on); void inst_debug(void); #endif /* !INST_H */ diff --git a/obj.h b/obj.h index 8ab2b7c..0e30b58 100644 --- a/obj.h +++ b/obj.h @@ -172,6 +172,14 @@ extern struct frame *root_frame; extern struct frame *active_frame; +static inline struct vec *get_vec(struct vec *vec) +{ + if (vec) + vec->n_refs++; + return vec; +} + + int instantiate(void); #endif /* !OBJ_H */