diff --git a/inst.c b/inst.c index c09c816..1572b76 100644 --- a/inst.c +++ b/inst.c @@ -162,6 +162,34 @@ static void inst_select_inst(struct inst *inst) } +/* + * @@@ This logic is overly complicated and should be simplified. The general + * idea was to avoid making unnecessary changes to the user's selections, but + * that risk doesn't exist. Furthermore, the way activate_item is used, its + * preconditions aren't met. It works anyway but it could be simpler as a + * consequence. + * + * activate_item tries to activate the path through the frame references, + * leading to a specific instance. It returns whether this is failed or whether + * it may have been successful. + * + * The initial condition is that we want to activate an item on a frame + * instance that's not active. Since the frame has been instantiated, there + * must be a way to activate it. We just have to find out how. + * + * The first test eliminates the root frame. If we're at the root frame and + * still haven't figured out what to do, something is wrong and we give up. + * + * The next test skips references that are already right. Since we know that + * there must be at least one reference that leads elsewhere, and we haven't + * found it yet, the recursion will tell us whether it can find it at all. + * + * Finally, if we've found a mismatch, we correct it. We then try to fix any + * further mismatches. Since we've made progress, we return 1, even if the + * other fixes should fail (or reach the root frame). + * + */ + static int activate_item(struct inst *inst) { if (!inst->outer) @@ -174,7 +202,7 @@ static int activate_item(struct inst *inst) } -int inst_select(struct coord pos) +static int __inst_select(struct coord pos, int tries) { enum inst_prio prio; const struct inst *prev; @@ -188,6 +216,10 @@ int inst_select(struct coord pos) int select_next; int dist, i; + if (!tries) { + fprintf(stderr, "__inst_select: tries exhausted\n"); + return 0; + } prev = selected_inst; deselect_outside(); edit_nothing(); @@ -259,14 +291,17 @@ int inst_select(struct coord pos) return 0; if (any_same_frame) { - if (activate_item(any_same_frame)) - return inst_select(pos); + activate_item(any_same_frame); + search_inst(any_same_frame); + instantiate(); + change_world(); + return __inst_select(pos, tries-1); } if (any_first) { frame = any_first->outer ? any_first->outer->u.frame.ref : NULL; if (frame != active_frame) { select_frame(frame); - return inst_select(pos); + return __inst_select(pos, tries-1); } } @@ -278,6 +313,17 @@ selected: } +int inst_select(struct coord pos) +{ + /* + * We shouldn't need more than 2 tries to select any item, so 5 is more + * than enough. This can still fail, but then it would for any number + * of tries. + */ + return __inst_select(pos, 5); +} + + struct inst *inst_find_point(struct coord pos) { struct inst *inst, *found; @@ -696,6 +742,7 @@ int inst_vec(struct vec *vec, struct coord base) inst = add_inst(&vec_ops, ip_vec, base); inst->vec = vec; inst->u.vec.end = vec->pos; + find_inst(inst); update_bbox(&inst->bbox, vec->pos); propagate_bbox(inst); return 1; @@ -745,6 +792,7 @@ int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width) inst->obj = obj; inst->u.rect.end = b; inst->u.rect.width = width; + find_inst(inst); update_bbox(&inst->bbox, b); grow_bbox_by_width(&inst->bbox, width); propagate_bbox(inst); @@ -785,6 +833,7 @@ int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width) inst->obj = obj; inst->u.rect.end = b; inst->u.rect.width = width; + find_inst(inst); update_bbox(&inst->bbox, b); grow_bbox_by_width(&inst->bbox, width); propagate_bbox(inst); @@ -873,6 +922,7 @@ int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b) inst->u.pad.name = stralloc(name); inst->u.pad.other = b; inst->u.pad.layers = pad_type_to_layers(obj->u.pad.type); + find_inst(inst); update_bbox(&inst->bbox, b); propagate_bbox(inst); return 1; @@ -946,6 +996,7 @@ int inst_arc(struct obj *obj, struct coord center, struct coord start, inst->bbox.max.x = center.x+r; inst->bbox.min.y = center.y-r; inst->bbox.max.y = center.y+r; + find_inst(inst); grow_bbox_by_width(&inst->bbox, width); propagate_bbox(inst); return 1; @@ -1122,6 +1173,7 @@ void inst_begin_frame(struct obj *obj, struct frame *frame, inst->u.frame.ref = frame; inst->u.frame.active = is_active_frame; inst->active = active; + find_inst(inst); curr_frame = inst; } diff --git a/obj.c b/obj.c index 88514f1..89ccd72 100644 --- a/obj.c +++ b/obj.c @@ -38,6 +38,67 @@ struct frame *active_frame = NULL; void *instantiation_error = NULL; +/* ----- Searching --------------------------------------------------------- */ + + +/* + * @@@ Known bug: we should compare all parameters of an instance, not just the + * object's base or the vectors end. + */ + +static int found = 0; +static int search_suspended = 0; +static const struct vec *find_vec = NULL; +static const struct obj *find_obj = NULL; +static struct coord find_pos; + + +static void suspend_search(void) +{ + search_suspended++; +} + +static void resume_search(void) +{ + assert(search_suspended > 0); + search_suspended--; +} + + +static struct coord get_pos(const struct inst *inst) +{ + return inst->obj ? inst->base : inst->u.vec.end; +} + + +void find_inst(const struct inst *inst) +{ + struct coord pos; + + if (search_suspended) + return; + if (find_vec != inst->vec) + return; + if (find_obj != inst->obj) + return; + pos = get_pos(inst); + if (pos.x != find_pos.x || pos.y != find_pos.y) + return; + found++; +} + + +void search_inst(const struct inst *inst) +{ + find_vec = inst->vec; + find_obj = inst->obj; + find_pos = get_pos(inst); +} + + +/* ----- Instantiation ----------------------------------------------------- */ + + static int generate_frame(struct frame *frame, struct coord base, const struct frame *parent, struct obj *frame_ref, int active); @@ -192,6 +253,7 @@ static int run_loops(struct frame *frame, struct loop *loop, { struct num from, to; int n; + int found_before, ok; if (!loop) return generate_items(frame, base, active); @@ -233,9 +295,17 @@ static int run_loops(struct frame *frame, struct loop *loop, instantiation_error = loop; goto fail; } - if (!run_loops(frame, loop->next, base, - active && loop->active == n)) + found_before = found; + if (loop->found == loop->active) + suspend_search(); + ok = run_loops(frame, loop->next, base, + active && loop->active == n); + if (loop->found == loop->active) + resume_search(); + if (!ok) goto fail; + if (found_before != found) + loop->found = n; n++; } loop->initialized = 0; @@ -255,13 +325,24 @@ fail: static int iterate_tables(struct frame *frame, struct table *table, struct coord base, int active) { + int found_before, ok; + if (!table) return run_loops(frame, frame->loops, base, active); for (table->curr_row = table->rows; table->curr_row; - table->curr_row = table->curr_row->next) - if (!iterate_tables(frame, table->next, base, - active && table->active_row == table->curr_row)) + table->curr_row = table->curr_row->next) { + found_before = found; + if (table->found_row == table->active_row) + suspend_search(); + ok = iterate_tables(frame, table->next, base, + active && table->active_row == table->curr_row); + if (table->found_row == table->active_row) + resume_search(); + if (!ok) return 0; + if (found_before != found) + table->found_row = table->curr_row; + } return 1; } @@ -296,6 +377,46 @@ static void reset_all_loops(void) } +static void reset_found(void) +{ + struct frame *frame; + struct table *table; + struct loop *loop; + + for (frame = frames; frame; frame = frame->next) { + for (table = frame->tables; table; table = table->next) + table->found_row = NULL; + for (loop = frame->loops; loop; loop = loop->next) + loop->found = -1; + frame->found_ref = NULL; + } +} + + +/* + * Note: we don't use frame->found_ref yet. Instead, we adjust the frame + * references with activate_item in inst.c + */ + +static void activate_found(void) +{ + struct frame *frame; + struct table *table; + struct loop *loop; + + for (frame = frames; frame; frame = frame->next) { + for (table = frame->tables; table; table = table->next) + if (table->found_row) + table->active_row = table->found_row; + for (loop = frame->loops; loop; loop = loop->next) + if (loop->found != -1) + loop->active = loop->found; + if (frame->found_ref) + frame->active_ref = frame->found_ref; + } +} + + int instantiate(void) { struct coord zero = { 0, 0 }; @@ -305,7 +426,14 @@ int instantiate(void) inst_start(); instantiation_error = NULL; reset_all_loops(); + reset_found(); + found = 0; + search_suspended = 0; ok = generate_frame(root_frame, zero, NULL, NULL, 1); + if (ok && (find_vec || find_obj) && found) + activate_found(); + find_vec = NULL; + find_obj = NULL; if (ok) ok = refine_layers(); if (ok) diff --git a/obj.h b/obj.h index 8774295..83a215a 100644 --- a/obj.h +++ b/obj.h @@ -1,8 +1,8 @@ /* * obj.h - Object definition model * - * Written 2009 by Werner Almesberger - * Copyright 2009 by Werner Almesberger + * Written 2009, 2010 by Werner Almesberger + * Copyright 2009, 2010 by Werner Almesberger * * 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 @@ -23,6 +23,34 @@ #include "layer.h" +/* + * Objects contain various fields that help to select instances under various + * conditions. They are "current", "active", and "found": + * + * - current: the path taken while instantiating. E.g., we may make one frame + * reference the "current" reference of this frame and then recurse into it. + * "Current" is reset to a null value after instantiation is complete, to + * allow other functions (such as expression evaluation) to distinguish + * between instantiation and editing. + * + * - active: the path selected by the user, through the GUI. This allows the + * user to reach any instance, similar to how instantiation visits all + * instances. The difference to "current" is that "active" is persistent + * across instantiation while "current" iterates through all possible values + * during instantiation. + * + * - found: then clicking on an unselected instance, fped will try to activate + * this instance. In order to do so, it needs to determine which choices need + * to be activated to reach the instance. "Found" records this information. + * At the end of the search, all "found" choices become "active". + * + * If, during the search, an instance can be reached with the "found" choice + * being equal to the choice active at that time, "found" will not be set to + * any other value. This prevents searches from affecting choices that play + * no role in the selection of the instance. + */ + + struct var { const char *name; struct var *next; @@ -67,6 +95,9 @@ struct table { /* GUI use */ struct row *active_row; + + /* For searching */ + struct row *found_row; /* NULL if not found yet */ }; struct loop { @@ -83,6 +114,9 @@ struct loop { double n; /* start value when it was active */ int iterations; /* iterations when it was active */ + /* For searching */ + int found; /* -1 if not found yet */ + /* for evaluation */ int initialized; }; @@ -127,6 +161,9 @@ struct frame { /* generating and editing */ struct obj *active_ref; + /* For searching */ + struct obj *found_ref; /* NULL if not found yet */ + /* for dumping */ int dumped; @@ -196,6 +233,24 @@ extern struct frame *active_frame; extern void *instantiation_error; +struct inst; + +/* + * Search callback from inst, invoked after the instance has been populated. + */ + +void find_inst(const struct inst *inst); + +/* + * If invoking search_inst before calling "instantiate", loop and tables are + * adjusted such that an instance matching the one passed to search_inst will + * become active. Note that this doesn't necessarily succeed, in which case no + * change is made. Also, if multiple matches are encountered, the result is + * arbitrary. + */ + +void search_inst(const struct inst *inst); + int instantiate(void); void obj_cleanup(void);