#include #include #include #include #include #include #include #include #include #include "lkb-avm.h" #include "lkb-protocol.h" #include "embedded-view.h" #include #include "gc-list.h" // feature structure visualization int init_avm_view(embedded_view_t *view, int id, lkb_avm *avm, lui_list *failures); void update_avm(embedded_view_t *view, int x1, int y1, int x2, int y2); void event_avm(embedded_view_t *view, event_t ev); void postscript_avm(embedded_view_t *view); void latex_avm(embedded_view_t *view); void close_avm_view(embedded_view_t *view); void mouse_avm(embedded_view_t *view, int x, int y); embedded_view_t prototype_avm_view = { init: (int(*)(struct embedded_view_t*,...))init_avm_view, update: update_avm, event: event_avm, postscript: postscript_avm, latex: latex_avm, close: close_avm_view, mouse_moved: mouse_avm, }; struct avm_view_data { void *selected_avm; char *moused_tag; char *shown_path; int id; int dirty; lkb_avm *avm; gc_list gcl; int fail_idx; lui_list *failures; }; lkb_avm_tag *find_tag_usage(lkb_avm *avm, char *name, lkb_avm_tag **start, int *tt, void **refer, int *listoff); extern void *hilite_avm; extern char *hilite_tag; extern char *empty_list_type; extern gc_list *target_gcl; extern int GC_ONLY_TAGS; int current_offset_x, current_offset_y; void render_avm_item(lkb_avm *t, int what, int *DY, int *DX, int display, lkb_avm_tag *tag); #define tvdata ((struct avm_view_data*)view->private) int avm_margin = 20; font_info avm_bar_font = {"helvetica", 12, fsRoman, {0,0,0}, 0}; extern font_info tag_display_font; int init_avm_view(embedded_view_t *view, int id, lkb_avm *avm, lui_list *failures) { int now_avm_width=0, now_avm_height=0; int i; view->private = calloc(1, sizeof(*tvdata)); tvdata->avm = avm; if(!tvdata->avm) { fprintf(stderr, "Error: tried to browse a NULL avm structure!\n"); free(tvdata); return -1; } tvdata->id = id; tvdata->selected_avm = 0; tvdata->shown_path = 0; tvdata->moused_tag = 0; tvdata->gcl.ngc = 0; tvdata->gcl.gc = 0; tvdata->gcl.scale = 1.0; tvdata->fail_idx = -1; tvdata->failures = failures; if(failures) { //printf("browsing an avm with %d failures\n", failures->length); for(i=0;ilength;i++) if(record_failure(avm, failures->data[i])) { fprintf(stderr, "Error: unable to make sense of failure record\n"); free(tvdata); return -1; } } current_offset_x = current_offset_y = avm_margin; hilite_avm = tvdata->selected_avm; reset_gc_list(&tvdata->gcl); target_gcl = &tvdata->gcl; render_avm_item(tvdata->avm, avm_structure, &now_avm_height, &now_avm_width, 1, 0); tvdata->dirty = 0; now_avm_width += avm_margin*2; now_avm_height += avm_margin*2; view->width = now_avm_width; view->height = now_avm_height; return 0; } void prescroll_avm_view(embedded_view_t *view) { lkb_failure *fail; if(!tvdata->failures || !tvdata->failures->length)return; fail = tvdata->failures->data[0]; scroll_embedded_view_to(view, fail->x, fail->y); } extern char *first_feature, *rest_feature; lkb_failure *mark_cycle = 0; lkb_avm *traverse_avm_list_path(lkb_avm *root, lkb_avm_list *list, lui_list *pathl, int pathi, int *tt, int off, int give_tag); lkb_avm *recurse_path(lkb_avm *root, lkb_avm *avm, lui_list *pathl, int pathi, int *tt, int type, int give_tag); lkb_avm *traverse_avm_path(lkb_avm *root, lkb_avm *avm, lui_list *pathl, int pathi, int *tt, int give_tag); lkb_avm *traverse_avm_tag_path(lkb_avm *root, lkb_avm_tag *tag, lui_list *pathl, int pathi, int *tt, int give_tag) { lkb_avm_tag *to, *start = 0; int reftype, listoff; void *refer; lkb_avm *ret; char *path = (pathilength)?pathl->data[pathi]:0; if(give_tag && (!path || !path[0])) { *tt = avm_tag; return (lkb_avm*)tag; } fprintf(stderr, "a_tag_path: [%s]->%s\n", tag->name, path); //printf("should define %s\n", t->name); to = find_tag_usage(root, tag->name, &start, &reftype, &refer, &listoff); if(!to) { fprintf(stderr, "Couldn't define correference `%d' in path\n", listoff); return 0; } //printf(" tag resolver: ref %p reftype %d reflistoff %d\n", refer, reftype, listoff); if(reftype==avm_list) ret = traverse_avm_list_path(root, refer, pathl, pathi, tt, listoff, give_tag); else ret = recurse_path(root, refer, pathl, pathi, tt, reftype, give_tag); //ret = traverse_avm_path(root, refer, path, tt, give_tag); //printf(" tag returns %p\n", ret); return ret; } lkb_avm *traverse_avm_list_path(lkb_avm *root, lkb_avm_list *list, lui_list *pathl, int pathi, int *tt, int off, int give_tag) { void *ret; int i; char *path = (pathilength)?pathl->data[pathi]:0; //printf("a_list_path(%d) %p: %s\n", off, list, path); //for(i=0;ilength;i++)printf("(%d) %p ", list->what[i], list->items[i]); if(!path || !path[0]) { extern char *list_type; char *type = list_type; int i; /*if(off == list->length) // not needed anymore { // if there's no item to split off into its own "cons" cell, // we should return the end 'rest' of the list *tt = list->rest_type; return list->rest_weird; }*/ // split the list here and stick the current node in as a structure w/ first+rest lkb_avm *avm = calloc(sizeof(lkb_avm), 1); lkb_avm_list *rest = calloc(sizeof(lkb_avm_list), 1); if(offlength) { for(i=0;i<=off;i++) if(list->type[i])type = list->type[i]; avm->type = strdup(type); avm->nattr = 2; avm->attr_what = malloc(sizeof(int)*2); avm->attr_what[0] = list->what[off]; avm->attr_what[1] = avm_list; avm->attr_name = malloc(sizeof(char*)*2); avm->attr_name[0] = strdup(first_feature); avm->attr_name[1] = strdup(rest_feature); avm->attr_value = malloc(sizeof(void*)*2); avm->attr_value[0] = list->items[off]; avm->attr_value[1] = rest; avm->attr_x = calloc(sizeof(int),2); avm->attr_y = calloc(sizeof(int),2); avm->attr_dx = calloc(sizeof(int),2); avm->attr_dy = calloc(sizeof(int),2); avm->attr_ldx = calloc(sizeof(int),2); avm->attr_hide = calloc(sizeof(int),2); avm->attr_cycle = calloc(sizeof(lkb_failure*), 2); avm->attr_tag = malloc(sizeof(lkb_avm_tag*)*2); avm->attr_tag[0] = list->tags[off]; avm->attr_tag[1] = list->rtags[off]; } else { avm->type = strdup(empty_list_type); } avm->expand = 1; avm->show_hiders = 1; avm->failure = 0; avm->path = strdup("XXX fixme"); rest->length = list->length - off - 1; list->length = off; if(rest->length>0 || (avm->nattr==2 && !avm->attr_tag[1])) { rest->what = malloc(sizeof(int)*rest->length); memcpy(rest->what, list->what+off+1, sizeof(int)*rest->length); rest->type = malloc(sizeof(char*)*rest->length); memcpy(rest->type, list->type+off+1, sizeof(char*)*rest->length); rest->items = malloc(sizeof(void*)*rest->length); memcpy(rest->items, list->items+off+1, sizeof(void*)*rest->length); rest->tags = malloc(sizeof(lkb_avm_tag*)*rest->length); memcpy(rest->tags, list->tags+off+1, sizeof(lkb_avm_tag*)*rest->length); rest->rtags = malloc(sizeof(lkb_avm_tag*)*rest->length); memcpy(rest->rtags, list->rtags+off+1, sizeof(lkb_avm_tag*)*rest->length); rest->rest_weird = list->rest_weird; rest->rest_type = list->rest_type; rest->open_ended = list->open_ended; } else { free(rest); if(avm->nattr==2) { avm->attr_value[1] = 0; avm->attr_what[1] = avm_tag; } } list->rest_weird = avm; list->rest_type = avm_structure; list->open_ended = 0; //fprintf(stderr, "Paths cannot currently point to list.\n"); *tt = avm_structure; return avm; } if(list->length<=off) { fprintf(stderr, "Error: tried to reach %d inside a list of length %d\n", off+1, list->length); return 0; } //dot = strchr(path, PATH_DIV); //if(dot)*dot++ = 0; if(strcasecmp(path, first_feature) && strcasecmp(path, rest_feature)) { fprintf(stderr, "Path asked for feature `%s' of list `%s'\n", path, list->type[0]?:"(unknown-list-type)"); //if(dot)dot[-1] = PATH_DIV; return 0; } if(!strcasecmp(path, first_feature)) { //if(dot)dot[-1] = PATH_DIV; if(list->what[off] == avm_tag) { ret = traverse_avm_tag_path(root, list->tags[off], pathl, pathi+1, tt, give_tag); if(ret && mark_cycle) list->tags[off]->failure = mark_cycle; } else ret = recurse_path(root, list->items[off], pathl, pathi+1, tt, list->what[off], give_tag); //printf("lfirst_ret(%d) %p type was %d ptr %p\n", off, ret, list->what[off], list->items[off]); return ret; } if(!strcasecmp(path, rest_feature)) { //if(dot)dot[-1] = PATH_DIV; if(off+1 < list->length || (!list->rest_weird && !list->rtags[off])) { if(off+1==list->length && !list->rest_weird && !list->rtags[off]) { if(pathi+1==pathl->length) { // `REST' is our final destination; invent a rest_weird for it extern char *empty_list_type; struct lkb_avm *avm = calloc(sizeof(struct lkb_avm), 1); avm->type = strdup(empty_list_type); avm->path = strdup("XXX fixme"); list->rest_weird = avm; list->rest_type = avm_structure; return avm; } else return 0; // there's no 'REST' to traverse... } ret = traverse_avm_list_path(root, list, pathl, pathi+1, tt, off+1, give_tag); } else if(list->rest_weird)ret = recurse_path(root, list->rest_weird, pathl, pathi+1, tt, list->rest_type, give_tag); else { ret = traverse_avm_tag_path(root, list->rtags[off], pathl, pathi+1, tt, give_tag); if(ret && mark_cycle) list->rtags[off]->failure = mark_cycle; } //printf("lrest_ret %p\n", ret); return ret; } fprintf(stderr, "Huh? Shouldn't get here.\n"); exit(-1); } lkb_avm *recurse_path(lkb_avm *root, lkb_avm *avm, lui_list *pathl, int pathi, int *tt, int type, int give_tag) { char *path = (pathilength)?pathl->data[pathi]:0; //printf("r_path: %s\n", path); if((!path || !path[0]) && type != avm_list) { *tt = type; //printf("r_path bail %p\n", avm); return avm; } switch(type) { case avm_terminal: if(path) { fprintf(stderr, "Requested path `%s' into terminal\n", path); return 0; } else { fprintf(stderr, "Requested a terminal, oops!\n"); return 0; } case avm_tag: root = traverse_avm_tag_path(root, (lkb_avm_tag*)avm, pathl, pathi, tt, give_tag); if(root && mark_cycle) ((lkb_avm_tag*)avm)->failure = mark_cycle; return root; case avm_structure: return traverse_avm_path(root, avm, pathl, pathi, tt, give_tag); case avm_list: return traverse_avm_list_path(root, (lkb_avm_list*)avm, pathl, pathi, tt, 0, give_tag); default: fprintf(stderr, "What is avm_type %d?\n", type); return 0; } } lkb_avm *traverse_avm_path(lkb_avm *root, lkb_avm *avm, lui_list *pathl, int pathi, int *tt, int give_tag) { lkb_avm *res = 0; int i; char *path = (pathilength)?pathl->data[pathi]:0; //printf("t_avm_path: %s\n", path); *tt = avm_structure; if(!path || !path[0])return avm; if(!pathl->length)return avm; // root path //if(!strcmp(path, PATH_DIV_STR))return avm; // root path *tt = -1; //dot = strchr(path, PATH_DIV); //if(dot)*dot = 0; for(i=0;inattr;i++) { if(!strcasecmp(path, avm->attr_name[i])) { if(mark_cycle) { avm->attr_cycle[i]=mark_cycle; avm->expand = 1; } //if(dot)*dot++ = PATH_DIV; if(avm->attr_what[i]==avm_tag) { res = traverse_avm_tag_path(root, avm->attr_tag[i], pathl, pathi+1, tt, give_tag); if(res && mark_cycle) ((lkb_avm_tag*)avm->attr_tag[i])->failure = mark_cycle; } else res = recurse_path(root, avm->attr_value[i], pathl, pathi+1, tt, avm->attr_what[i], give_tag); break; } } //if(!res && dot)dot[-1] = PATH_DIV; //if(!strcasecmp(path, first_feature) || !strcasecmp(path, rest_feature)) if(i == avm->nattr) { //fprintf(stderr, "Path asked for feature `%s' of non-list `%s'\n", fprintf(stderr, "Path asked for nonextant feature `%s' in type `%s'\n", path, avm->type); res = 0; } //printf(" ret %p\n", res); return res; } int expand_around(lkb_avm *root, void *val, int type) { int i; lkb_avm_list *list = (lkb_avm_list*)root; if(val==root)return 1; switch(type) { case avm_structure: for(i=0;inattr;i++) if(expand_around(root->attr_value[i], val, root->attr_what[i])) { root->expand = 1; root->attr_hide[i] = 0; return 1; } break; case avm_list: for(i=0;ilength;i++) if(expand_around(list->items[i], val, list->what[i])) return 1; if(list->rest_weird) if(expand_around(list->rest_weird, val, list->rest_type)) return 1; break; } return 0; } int record_failure(lkb_avm *avm, lkb_failure *fail) { lkb_avm *loc, *crc; int loct, cyct, i; //loc = traverse_avm_path(avm, avm, fail->path, 0, &loct, (fail->what==ufail_cycle)?1:0); loc = traverse_avm_path(avm, avm, fail->path, 0, &loct, 0); if(!loc) { fprintf(stderr, "Path `"); if(!fail->path->length)fprintf(stderr, PATH_DIV_STR); else for(i=0;ipath->length;i++) fprintf(stderr, "%s%s", (i>0)?PATH_DIV_STR:"", (char*)fail->path->data[i]); fprintf(stderr, "' for failure didn't exist\n"); return -1; } //printf("loc = %p ; loct = %d\n", loc, loct); // make it visible expand_around(avm, loc, avm_structure); if(fail->what==ufail_cycle) { mark_cycle = fail; crc = recurse_path(avm, loc, fail->cycle, 0, &cyct, loct, 0); mark_cycle = 0; //printf("path followed by cycle `%s' in node %p\n", fail->cycle, loc); //printf(" cycle start %p [%d] end %p [%d]\n", loc, loct, crc, cyct); } else { //printf("path -> %p type %d is AVM[%s]\n", loc, loct, loc->type); loc->failure = fail; } return 0; } /*void print_failure(lkb_failure *fail) { switch(fail->what) { case ufail_no_glb: printf("No GLB for types `%s' and `%s' at path `%s'\n", fail->type1, fail->type2, fail->path); break; case ufail_cycle: printf("Cycle introduced from path `%s'\n", fail->path); break; case ufail_constraint: printf("Type-heirarchy constraint from type `%s' conflicted with types `%s' and `%s' at `%s'\n", fail->constraint, fail->type1, fail->type2, fail->path); break; } }*/ void close_avm_view(embedded_view_t *view) { reset_gc_list(&tvdata->gcl); lkb_forget_avm(tvdata->id); free(tvdata); } update_gcl(embedded_view_t *view) { int now_avm_width = 0, now_avm_height = 0; current_offset_x = current_offset_y = avm_margin; hilite_avm = tvdata->selected_avm; hilite_tag = tvdata->moused_tag; reset_gc_list(&tvdata->gcl); target_gcl = &tvdata->gcl; render_avm_item(tvdata->avm, avm_structure, &now_avm_height, &now_avm_width, 1, 0); now_avm_width += avm_margin*2; now_avm_height += avm_margin*2; set_embedded_view_size(view, now_avm_width, now_avm_height); tvdata->dirty = 0; } void update_avm(embedded_view_t *view, int x1, int y1, int x2, int y2) { struct timeval tv1, tv2; float secs; gettimeofday(&tv1, 0); yzBufferMode(YZ_BUFFER_BACK); if(!GC_ONLY_TAGS) { yzPenColor(65535,65535,65535); //printf("update in %d %d -> %d %d (vis %d %d)\n", x1, y1, x2, y2, //view->vis_width, view->vis_height); yzRect(x1, y1, x2, y2); yzPenColor(0,0,0); } hilite_avm = tvdata->selected_avm; hilite_tag = tvdata->moused_tag; draw_gc_list(&tvdata->gcl, x1, y1, x2, y2, 1); gettimeofday(&tv2, 0); secs = tv2.tv_sec - tv1.tv_sec; secs += ((float)tv2.tv_usec - tv1.tv_usec) / 1000000; //fprintf(stderr, "display took %.5f seconds\n", secs); if(x2-x1+1 == view->vis_width && y2-y1+1 == view->vis_height) show_path(view, tvdata->shown_path, 1, 1); } void event_avm(embedded_view_t *view, event_t ev) { switch(ev.type) { case YZ_MOUSE_DOWN: if(handle_avm_click(view, ev.x, ev.y, ev.button)) return; break; case YZ_KEY_DOWN: //fprintf(stderr, "key %c\n", ev.key); switch(ev.key) { case '-': case '_': tvdata->gcl.scale *= 0.50; if(tvdata->gcl.scale < 1.0 / 128) tvdata->gcl.scale = 1.0 / 128; yzUpdateScrollArea(view->scroll_area, 0, 0, view->width, view->height); break; case '+': case '=': tvdata->gcl.scale *= 2; if(tvdata->gcl.scale > 2)tvdata->gcl.scale = 2; yzUpdateScrollArea(view->scroll_area, 0, 0, view->width, view->height); break; case YZ_KEYCODE_RIGHT_ARROW: if(!tvdata->failures)break; tvdata->fail_idx = (tvdata->fail_idx+1)%tvdata->failures->length; hilite_failure(view, tvdata->failures->data[tvdata->fail_idx]); break; case YZ_KEYCODE_LEFT_ARROW: if(!tvdata->failures)break; tvdata->fail_idx = (tvdata->fail_idx+tvdata->failures->length-1)%tvdata->failures->length; hilite_failure(view, tvdata->failures->data[tvdata->fail_idx]); break; } break; } } hilite_failure(embedded_view_t *view, lkb_failure *fail) { yzBufferMode(YZ_BUFFER_BACK); /*yzPenColor(65535, 65535, 65535); yzRect(0, 0, view->width+20, view->height+20); if(tvdata->dirty) update_gcl(view); update_avm(view, 0, 0, view->width+20, view->height+20);*/ yzPushClipRect(0, 0, view->width, view->height); yzPushOrigin(0,0); do_attention_selection(view, fail->x, fail->y); yzPopOrigin(); yzPopClipRect(); yzUpdateBuffer(YZ_BUFFER_FRONT); } int nullstrcmp(char *a, char *b) { if(a==b)return 0; if(!a || !b)return a-b; return strcmp(a, b); } show_path(embedded_view_t *view, char *path, int force, int from_view) { int ox, oy, cx1, cy1, cx2, cy2; char *tmp = tvdata->shown_path; if(force || nullstrcmp(tmp, path)) { if(path)tvdata->shown_path = strdup(path); else tvdata->shown_path = 0; if(tmp)free(tmp); if(from_view) { yzPopClipRect4p(&cx1, &cy1, &cx2, &cy2); yzPopOrigin2p(&ox, &oy); } int height = top_bar_height(&avm_bar_font); yzBufferMode(YZ_BUFFER_BACK); yzPushClipRect(0, 0, view->use_width, height); yzPenColor(50000, 50000, 50000); yzRect(0, 0, view->use_width, height); yzPenColor(0,0,0); yzOutlineRect(0, 0, view->use_width, height); if(tvdata->shown_path) { use_font(&avm_bar_font); yzText(5, height*0.8, tvdata->shown_path); } yzUpdateBuffer(YZ_BUFFER_FRONT); yzPopClipRect(); if(from_view) { yzPushOrigin(ox, oy); yzPushClipRect(cx1, cy1, cx2, cy2); } } //yzQueryKey(YZ_KEYCODE_CONTROL); } lkb_avm *pick_avm_item(lkb_avm *root, int what, int x, int y, int *tt, lkb_avm_tag *tag, int full_struct, int *which_child); lkb_avm *pick_avm(lkb_avm *root, int x, int y, int *tt, int full_struct, int *which_child); set_hiders(lkb_avm *t, int v) { int i; for(i=0;inattr;i++) { if(t->attr_hide[i]>0) t->attr_hide[i] = v; } } do_avm_popup(embedded_view_t *view, lkb_avm *t) { window_t *par; menu_data_t *md; int id; md = yzNewMenuData("AVM Options"); if(t->expand)yzAddMenuDataItem(md, "Collapse", 1); if(!t->expand)yzAddMenuDataItem(md, "Expand", 2); if(t->show_hiders)yzAddMenuDataItem(md, "Banish Hidden", 8); if(!t->show_hiders)yzAddMenuDataItem(md, "Reveal Hidden", 9); yzAddMenuDataItem(md, "Select", 3); yzAddMenuDataItem(md, "Type Hierarchy", 4); yzAddMenuDataItem(md, "Type Definition", 5); yzAddMenuDataItem(md, "Expanded Type", 6); yzAddMenuDataItem(md, "Show Source", 7); yzAddMenuDataItem(md, "Show Provenance", 10); par = yzGetSelectedWindow(); yzShowMenu(md, &id, t->x1 + par->origin_x, t->y1 + par->origin_y); switch(id) { case 0: break; // cancelled default: printf("unknown item %d\n", id); break; case 1: t->expand = 0; tvdata->dirty = 1; break; case 2: t->expand = 1; tvdata->dirty = 1; break; case 3: tvdata->selected_avm = t; tvdata->dirty = 1; break; case 4: lkb_type_command(t->type, "hierarchy"); break; case 5: lkb_type_command(t->type, "skeleton"); break; case 6: lkb_type_command(t->type, "expansion"); break; case 7: lkb_type_command(t->type, "source"); break; case 10: lkb_type_command(t->type, "provenance"); break; case 8: t->show_hiders = 0; set_hiders(t, 2); tvdata->dirty = 1; break; case 9: t->show_hiders = 1; set_hiders(t, 1); tvdata->dirty = 1; break; } // XXX should dispose memory here } lkb_avm_tag *find_tag_usage_list(lkb_avm_list *list, char *name, lkb_avm_tag **start, int *tt, void **refer, int *listoff) { lkb_avm_tag *ret; int i; for(i=0;ilength;i++) { if(list->tags[i] && !strcmp(name, list->tags[i]->name)) { if(*start == list->tags[i]) *start = 0; else if(!*start) { *tt = list->what[i]; *refer = list->items[i]; *listoff = 0; return list->tags[i]; } } if(list->rtags[i] && !strcmp(name, list->rtags[i]->name)) { if(*start == list->rtags[i]) *start = 0; else if(!*start) { if(i+1 == list->length && list->rest_weird) { *tt = list->rest_type; *refer = list->rest_weird; *listoff = 0; } else { *tt = avm_list; *refer = list; *listoff = i+1; } //fprintf(stderr, "matched `%s' rtag for item %d in list %p len %d\n", // name, i, list, list->length); return list->rtags[i]; } } if(list->items[i]) { if(list->what[i]==avm_structure) ret = find_tag_usage(list->items[i], name, start, tt, refer, listoff); else if(list->what[i]==avm_list) ret = find_tag_usage_list(list->items[i], name, start, tt, refer, listoff); else ret = 0; if(ret)return ret; } } return 0; } int dirty; lkb_avm_tag *find_tag_usage(lkb_avm *avm, char *name, lkb_avm_tag **start, int *tt, void **refer, int *listoff) { lkb_avm_tag *ret; int i; for(i=0;inattr;i++) { // XXX I'm not sure skipping hidden nodes is really the right answer here... if(avm->attr_hide[i]==2)continue; if(avm->attr_tag[i] && !strcmp(name, avm->attr_tag[i]->name)) { if(*start == avm->attr_tag[i]) *start = 0; else if(!*start) { if(!avm->expand)dirty = 1; avm->expand = 1; *tt = avm->attr_what[i]; *refer = avm->attr_value[i]; *listoff = 0; return avm->attr_tag[i]; } } if(avm->attr_value[i]) { if(avm->attr_what[i]==avm_structure) ret = find_tag_usage(avm->attr_value[i], name, start, tt, refer, listoff); else if(avm->attr_what[i]==avm_list) ret = find_tag_usage_list(avm->attr_value[i], name, start, tt, refer, listoff); else ret = 0; if(ret) { if(!avm->expand)dirty = 1; avm->expand = 1; return ret; } } } return 0; } int att_x, att_y; lkb_avm_tag *att_tag; select_tag_definition(embedded_view_t *view, lkb_avm_tag *t) { lkb_avm_tag *to, *start = 0; int reftype, listoff; void *refer; //printf("should define %s\n", t->name); dirty = 0; to = find_tag_usage(tvdata->avm, t->name, &start, &reftype, &refer, &listoff); //printf("should select %p\n", to); if(dirty)tvdata->dirty = 1; tvdata->selected_avm = to; att_tag = to; } select_tag_next_occurance(embedded_view_t *view, lkb_avm_tag *t) { lkb_avm_tag *to, *start = t; int reftype, listoff; void *refer; //printf("should chase %s\n", t->name); dirty = 0; to = find_tag_usage(tvdata->avm, t->name, &start, &reftype, &refer, &listoff); if(!to) { start = 0; to = find_tag_usage(tvdata->avm, t->name, &start, &reftype, &refer, &listoff); //printf("search wrapped\n"); } //printf("should select %p\n", to); if(dirty)tvdata->dirty = 1; tvdata->selected_avm = to; att_tag = to; } select_tag_prev_occurance(embedded_view_t *view, lkb_avm_tag *t) { lkb_avm_tag *to, *prev, *start = 0; int reftype, listoff; void *refer; //printf("should chase %s\n", t->name); dirty = 0; do { prev = start; to = find_tag_usage(tvdata->avm, t->name, &start, &reftype, &refer, &listoff); start = to; } while(to != t && to != 0); if(to==0) { fprintf(stderr, "prev-occurance search found no matches for `%s'\n", t->name); return; } // now prev is the tag we found just before t if(prev==0) { // have to find *last* occurance //fprintf(stderr, "prev-occurance wrapped\n"); start = t; do { prev = start; to = find_tag_usage(tvdata->avm, t->name, &start, &reftype, &refer, &listoff); start = to; } while(to != 0); } //printf("should select %p\n", to); if(dirty)tvdata->dirty = 1; tvdata->selected_avm = prev; att_tag = prev; } do_attention_selection(embedded_view_t *view, int x, int y) { int i; scroll_embedded_view_to(view, x, y); yzBufferMode(YZ_BUFFER_BACK); update_avm(view, 0, 0, view->width, view->height); yzBufferMode(YZ_BUFFER_FRONT); for(i=80;i>10;i-=6) { yzUpdateBuffer(YZ_BUFFER_FRONT); yzOutlineRect(x-i, y-i, x+i, y+i); usleep(10000); yzGetUnfilteredEvent(0); // make sure Expose events get processed } yzBufferMode(YZ_BUFFER_BACK); } do_avm_tag_popup(embedded_view_t *view, lkb_avm_tag *t) { window_t *par; menu_data_t *md; int id; md = yzNewMenuData("Tag Options"); yzAddMenuDataItem(md, "Find Next", 1); yzAddMenuDataItem(md, "Find Previous", 2); if(!t->is_def)yzAddMenuDataItem(md, "Find Definition", 3); par = yzGetSelectedWindow(); yzShowMenu(md, &id, t->x1 + par->origin_x, t->y1 + par->origin_y); switch(id) { case 0: break; // cancelled default: fprintf(stderr, "unknown item %d\n", id); break; case 1: select_tag_next_occurance(view, t); break; case 2: select_tag_prev_occurance(view, t); break; case 3: select_tag_definition(view, t); break; } // XXX should dispose memory here } sim_key(int key) { event_t ev; ev.type = YZ_KEY_DOWN; ev.key = key; embedded_view_event(ev); return 0; } misc_avm_popup(embedded_view_t *view, int x, int y) { window_t *par; menu_data_t *md; int id; md = yzNewMenuData("AVM Options"); yzAddMenuDataItem(md, "Output Postscript", 1); yzAddMenuDataItem(md, "Output LaTeX", 2); yzAddMenuDataItem(md, "Close Window", 3); par = yzGetSelectedWindow(); yzShowMenu(md, &id, x-8, y-8); switch(id) { case 0: break; // cancelled default: fprintf(stderr, "unknown item %d\n", id); break; case 1: postscript_avm(view); break; case 2: latex_avm(view); break; case 3: sim_key('q'); return -1; } // XXX should dispose memory here return 0; } click_avm_tag(embedded_view_t *view, lkb_avm_tag *t, int b) { //if(yzQueryKey(YZ_KEYCODE_CONTROL) || b!=1) return do_avm_tag_popup(view, t); //printf("Clicked %s tag: %s\n", t->is_def?"master":"slave", t->name); //tvdata->selected_avm = t; } // a mouse-down has occured on the title/type of an avm // decide what to do. #define DRAG_DIST 10 handle_avm_structure_click(embedded_view_t *view, lkb_avm *avm, int x, int y, int b) { event_t ev; int dx = 0, dy = 0; do { ev = yzGetUnfilteredEvent(1000); if(ev.type==YZ_MOUSE_DRAG)dx += ev.x2 - ev.x, dy += ev.y2 - ev.y; } while((dx*dx + dy*dy < DRAG_DIST*DRAG_DIST) && ev.type != YZ_MOUSE_UP); if(dx*dx + dy*dy < DRAG_DIST*DRAG_DIST) { // just a simple click avm->expand = !avm->expand; tvdata->dirty = 1; } else { // a full-fledged drag! if(!tvdata->failures) // only produce a drag if we have no failures handle_avm_drag(view, avm, x, y, b); } } handle_avm_click(embedded_view_t *view, int x, int y, int b) { lkb_avm *ptr; int type, which_child; int cx1, cy1, cx2, cy2, ox, oy; att_x = att_y = 0; att_tag = 0; ptr = pick_avm(tvdata->avm, x, y, &type, 0, &which_child); //printf("clicked at %d, %d, b %d, picked up item %p of type %d\n", x, y, b, ptr, type); if(type==avm_structure && which_child>=0 && ptr->attr_what[which_child]==avm_tag) {ptr = (lkb_avm*)ptr->attr_tag[which_child]; type=avm_tag; which_child=-1;} if(ptr && which_child>=0) { fprintf(stderr, "clicked child %d of parent %p type %d\n", which_child, ptr, type); } else if(ptr && type == avm_structure) { if(yzQueryKey(YZ_KEYCODE_CONTROL) || b!=1) do_avm_popup(view, ptr); else handle_avm_structure_click(view, ptr, x, y, b); } else if(ptr && type == avm_tag) { click_avm_tag(view, (lkb_avm_tag*)ptr, b); } else if(!ptr && (b!=1 || yzQueryKey(YZ_KEYCODE_CONTROL)))if(misc_avm_popup(view, x, y))return -1; //printf("height = %d\n", view->height); if(tvdata->dirty) update_gcl(view); /* // old method of redrawing after reformat yzBufferMode(YZ_BUFFER_BACK); yzPenColor(65535, 65535, 65535); yzRect(0, 0, view->width+20, view->height+20); update_avm(view, 0, 0, view->width+20, view->height+20); */ // redisplay the whole view after a click yzPopOrigin2p(&ox, &oy); yzPopClipRect4p(&cx1, &cy1, &cx2, &cy2); yzDrawScrollArea(view->scroll_area); yzPushClipRect(cx1, cy1, cx2, cy2); yzPushOrigin(ox, oy); if(att_tag) { att_x = att_tag->x1, att_y = att_tag->y1; do_attention_selection(view, att_x, att_y); } yzUpdateBuffer(YZ_BUFFER_FRONT); return 0; } int near_tag(int x, int y, lkb_avm_tag *tag) { use_font(&tag_display_font); if(x>=tag->x1-2 && y>=tag->y1-8*tag_display_font.size/12 && x<=tag->x1+yzStringSize(tag->name, strlen(tag->name))+2 && y <= tag->y1+4*tag_display_font.size/12) return 1; else return 0; } lkb_avm *pick_avm_item(lkb_avm *root, int what, int x, int y, int *tt, lkb_avm_tag *tag, int full_struct, int *which_child) { lkb_avm_list *l; lkb_avm *t; int i; if((!full_struct || !root) && tag && near_tag(x, y, tag)) { *tt = avm_tag; *which_child = -1; return (lkb_avm*)tag; } switch(what) { case avm_list: l = (lkb_avm_list*)root; for(i=0;ilength;i++) { if(l->what[i]==avm_terminal) continue; else { *which_child = -1; t = pick_avm_item(l->items[i], l->what[i], x, y, tt, l->tags[i], full_struct, which_child); if(t)return t; } if(!full_struct && l->rtags[i] && near_tag(x, y, l->rtags[i])) { *tt = avm_tag; return (lkb_avm*)l->rtags[i]; } } if(l->rest_weird) { if(l->rest_type == avm_terminal) break; else { *which_child = -1; t = pick_avm_item(l->rest_weird, l->rest_type, x, y, tt, 0, full_struct, which_child); if(t)return t; } } break; case avm_structure: *tt = what; *which_child = -1; return pick_avm(root, x, y, tt, full_struct, which_child); case avm_tag: //fprintf(stderr, "avm_tag root = %p, tag = %p\n", root, tag); break; case avm_terminal: fprintf(stderr, "pick reached a terminal - bad!\n"); return 0; } return 0; } lkb_avm *pick_avm(lkb_avm *root, int x, int y, int *tt, int full_struct, int *which_child) { int i; lkb_avm *t; if(!full_struct && x >= root->x1-2 && x <= root->x1+root->type_dx && y >= root->y1-14 && y <= root->y1 + root->type_dy - 14) { *tt = avm_structure; *which_child = -1; return root; } if(root->expand) // don't allow checking subitems of a closed structure { for(i=0;inattr;i++) { if(root->attr_hide[i]==2)continue; if(root->attr_what[i]==avm_terminal) { /*printf("terminal %s at %d %d %d %d ; x %d y %d\n", root->attr_value[i], root->attr_x[i], root->attr_y[i], root->attr_dx[i], root->attr_dy[i], x, y);*/ // if we're in the tag and we're allowed to return tags... if(!full_struct && root->attr_tag[i] && near_tag(x, y, root->attr_tag[i])) { *tt = avm_tag; *which_child = -1; return (lkb_avm*)root->attr_tag[i]; } // or if we're inside the terminal... if(x>root->attr_x[i] && y>root->attr_y[i] && xattr_x[i]+root->attr_dx[i] && yattr_y[i]+root->attr_dy[i]) { *which_child = i; *tt = avm_structure; return root; } } else { // this is a big substructure, recurse on it *which_child = -1; t = pick_avm_item(root->attr_value[i], root->attr_what[i], x, y, tt, root->attr_tag[i], full_struct && (root->attr_what[i]!=avm_tag), which_child); if(t) { if(root->attr_what[i] == avm_tag) { *which_child = i; *tt = avm_structure; return root; } else return t; } } } } if(full_struct) { if(x >= root->x1-4 && x <= root->x1+root->dx-4 && y >= root->y1-12 && y <= root->y1+root->dy-14) { *tt = avm_structure; *which_child = -1; return root; } } return 0; } void show_failure(embedded_view_t *view, lkb_failure *fail) { //print_failure(fail); switch(fail->what) { case ufail_no_glb: show_path(view, "Unification Failure: No GLB Exists", 0, 0); break; case ufail_cycle: show_path(view, "Unification Failure: Cycle Introduced", 0, 0); break; case ufail_constraint: show_path(view, "Unification Failure: GLB Type Constraint Violated", 0, 0); break; } } void mouse_avm(embedded_view_t *view, int x, int y) { lkb_avm *avm; lkb_avm_tag *tag; int type, which_child; char *omt = tvdata->moused_tag; char path[1024]; //printf("mouse moved to %d %d\n", x, y); tvdata->moused_tag = 0; tag = (lkb_avm_tag*)pick_avm(tvdata->avm, x, y, &type, 0, &which_child); if(type==avm_structure && which_child>=0 && ((lkb_avm*)tag)->attr_what[which_child]==avm_tag) {tag = ((lkb_avm*)tag)->attr_tag[which_child]; type=avm_tag; which_child=-1;} if(tag && type==avm_tag) tvdata->moused_tag = tag->name; avm = pick_avm(tvdata->avm, x, y, &type, 1, &which_child); if(!avm)show_path(view, 0, 0, 0); else { if(type == avm_structure) { //printf("mouse over %p path %s\n", avm, avm->path); if(which_child>=0) { //printf("subitem %d of structure %p %s\n", which_child, avm, avm->path); snprintf(path, 1023, "%s%s%s", avm->path?avm->path:"", avm->path?" ":"", avm->attr_name[which_child]); path[1023] = 0; show_path(view, path, 0, 0); } else { if(avm->failure)show_failure(view, avm->failure); else show_path(view, avm->path, 0, 0); } } else if(type != avm_tag) show_path(view, "Not an AVM", 0, 0); } if(omt != tvdata->moused_tag) { // redraw all tags in the gc-list GC_ONLY_TAGS = 1; yzDrawScrollArea(view->scroll_area); GC_ONLY_TAGS = 0; } } char drop_target_path[1024]; int drop_target_id; lkb_avm *dropping_avm; window_t *dropping_window; int drop_x1, drop_y1, drop_x2, drop_y2; display_avm_child_background(embedded_view_t *view, gc_list *gcl, color_t col, lkb_avm *avm, int child, lkb_avm *replacer, int replacer_child) { int avm_x, avm_y, avm_dx, avm_dy, cx1, cy1, cx2, cy2; int rep_x, rep_y, rep_dx, rep_dy; color_t drop_color={65535,55000,55000}, rep_color={55000,55000,65535}; if(child>=0) { avm_x = avm->attr_x[child]-2; avm_y = avm->attr_y[child]+2; avm_dx = avm->attr_dx[child]+2; avm_dy = avm->attr_dy[child]-2; } else { avm_x = avm->x1-4; avm_y = avm->y1-12; avm_dx = avm->dx; avm_dy = avm->dy-2; } if(replacer) { if(replacer_child>=0) { rep_x = replacer->attr_x[replacer_child]-2; rep_y = replacer->attr_y[replacer_child]+2; rep_dx = replacer->attr_dx[replacer_child]+2; rep_dy = replacer->attr_dy[replacer_child]-2; } else { rep_x = replacer->x1-4; rep_y = replacer->y1-12; rep_dx = replacer->dx; rep_dy = replacer->dy-2; } } yzBufferMode(YZ_BUFFER_BACK); yzRecomputeScrollAreaClipping(view->scroll_area); yzPushClipRect(avm_x, avm_y, avm_x+avm_dx-1, avm_y+avm_dy-1); yzPopClipRect4p(&cx1, &cy1, &cx2, &cy2); yzPushClipRect(cx1, cy1, cx2, cy2); /*printf("passed %d %d %d %d, got %d %d %d %d\n", avm_x, avm_y, avm_x+avm_dx-1, avm_y+avm_dy-1, cx1, cy1, cx2, cy2);*/ if(dropping_avm && dropping_window==yzGetSelectedWindow() && avm_x>=drop_x1 && avm_x<=drop_x2 && avm_y>=drop_y1 && avm_y<=drop_y2) { // avm is inside drop yzPenColor1c(drop_color); yzRect(drop_x1, drop_y1, drop_x2, drop_y2); // draw avm second, if nonwhite background if(col.r != 65535 || col.g != 65535 || col.b != 65535) { yzPenColor1c(col); yzRect(avm_x, avm_y, avm_x+avm_dx-1, avm_y+avm_dy-1); } if(replacer) { yzPenColor1c(rep_color); yzRect(rep_x, rep_y, rep_x+rep_dx-1, rep_y+rep_dy-1); } } else { // avm is outside drop yzPenColor1c(col); yzRect(avm_x, avm_y, avm_x+avm_dx-1, avm_y+avm_dy-1); if(replacer) { yzPenColor1c(rep_color); yzRect(rep_x, rep_y, rep_x+rep_dx-1, rep_y+rep_dy-1); } // draw drop second if(dropping_avm && dropping_window==yzGetSelectedWindow()) { yzPenColor1c(drop_color); yzRect(drop_x1, drop_y1, drop_x2, drop_y2); } } draw_gc_list(gcl, cx1, cy1, cx2, cy2, 0);//avm_x, avm_y, avm_x+avm_dx-1, avm_y+avm_dy-1); yzUpdateBuffer(YZ_BUFFER_FRONT); yzPopClipRect(); yzPopClipRect(); yzPopOrigin(); } handle_avm_drag(embedded_view_t *view, lkb_avm *avm, int x, int y, int b) { event_t ev; window_t *w, *prevw = 0; int relx, rely, acc = 0; color_t white = {65535, 65535, 65535, 65535}, hicolor = {65535, 55000, 55000}; dropping_window = yzGetSelectedWindow(); dropping_avm = avm; drop_x1 = avm->x1-4; drop_x2 = avm->x1+avm->dx-4; drop_y1 = avm->y1-12; drop_y2 = avm->y1+avm->dy-14; yzPopOrigin(); yzPopClipRect(); display_avm_child_background(view, &tvdata->gcl, hicolor, avm, -1, 0, 0); do { ev = yzGetUnfilteredEvent(10); if(ev.type == YZ_MOUSE_DRAG) { w = (window_t*)yzLocateWindow(ev.x2, ev.y2, &relx, &rely); if(w != prevw) { if(prevw)notify_drag(prevw, "avm", -1, -1); prevw = w; } if(w)acc = notify_drag(w, "avm", relx, rely); } } while(ev.type != YZ_MOUSE_UP); if(prevw)notify_drag(prevw, "avm", -1, -1); // end the drag dropping_avm = 0; display_avm_child_background(view, &tvdata->gcl, white, avm, -1, 0, 0); yzRecomputeScrollAreaClipping(view->scroll_area); #define ROOT_AVM_PATH PATH_DIV_STR if(prevw && acc) lkb_send_unify( tvdata->id, (avm->path==0)?ROOT_AVM_PATH:avm->path, drop_target_id, drop_target_path); } int avm_view_drag(struct embedded_view_t *view, char *type, int x, int y) { static lkb_avm *hilite = 0; static int hilite_child = -1; lkb_avm *avm; int atype, which_child, hx, hy, replaced = 0; color_t white = {65535, 65535, 65535, 65535}, hicolor = {55000, 55000, 65535}; if(tvdata->failures) return 0; // do not accept drags if we have failures //printf("drag is at %d %d\n", x, y); if(strcmp(type, "avm")) { //printf("drag is unknown type `%s'\n", type); return 0; } if(x==-1 && y==-1) { avm = 0; which_child = -1; } else avm = pick_avm(tvdata->avm, x, y, &atype, 1, &which_child); if(!avm || atype != avm_structure) { //printf(" drag is not over an AVM\n"); avm = 0; which_child = -1; } else if(avm == dropping_avm && which_child==-1)avm = 0; // can't drop onto itself! else { if(which_child>=0) snprintf(drop_target_path, 1023, "%s%s%s", avm->path?avm->path:"", avm->path?PATH_DIV_STR:"", avm->attr_name[which_child]); else if(avm->path)strncpy(drop_target_path, avm->path, 1023); else strcpy(drop_target_path, ROOT_AVM_PATH); drop_target_path[1023] = 0; drop_target_id = tvdata->id; //printf("drag %d %d path `%s'\n", x, y, drop_target_path); } if(hilite != avm || hilite_child != which_child) { // erase old hilite, if it is not contained in new hilite if(hilite) { hx = (hilite_child>=0)?hilite->attr_x[hilite_child]:hilite->x1; hy = (hilite_child>=0)?hilite->attr_y[hilite_child]:hilite->y1; if(!avm || which_child>=0 || hx < avm->x1 || hx >= avm->x1+avm->dx || hy < avm->y1 || hy >= avm->y1+avm->dy) { // old is not contained in new // check if new is contained in old if(avm) { hx = (which_child>=0)?avm->attr_x[which_child]:avm->x1; hy = (which_child>=0)?avm->attr_y[which_child]:avm->y1; if(hilite_child==-1 && hx>=hilite->x1 && hxx1+hilite->dx && hy>=hilite->y1 && hyy1+hilite->dy) { replaced = 1; display_avm_child_background(view, &tvdata->gcl, white, hilite, hilite_child, avm, which_child); //printf("culled SHOW_NEW\n"); } } if(!replaced)display_avm_child_background(view, &tvdata->gcl, white, hilite, hilite_child, 0, 0); } //else printf("culled CLEAR_OLD\n"); } hilite = avm; hilite_child = which_child; if(hilite && !replaced)display_avm_child_background(view, &tvdata->gcl, hicolor, hilite, hilite_child, 0, 0); } return hilite?1:0; } void do_postscript_avm(embedded_view_t *view, int width, int height) { window_t *avm_ps, *old; int dy, dx, fd; float scale; struct timeval tv1, tv2; float secs; char foo[1024], fname[768] = "avm-print.ps"; sprintf(fname, "/tmp/avm.%d.ps", tvdata->id); gettimeofday(&tv1, 0); unlink(fname); fd = open(fname, O_WRONLY | O_CREAT, 0664); avm_ps = (window_t*)yzPostScriptWindow("Feature Structure", 800, 555, fd); old = yzSelectWindow(avm_ps); if(width > height) { scale = 660.0 / width; if((540.0 / height) < scale) scale = 540.0 / height; yzPostScriptSetLandscape(); } else { scale = 540.0 / width; if((660.0 / height) < scale) scale = 660.0 / height; yzSetOrigin(0,350); } extern int yzPostScriptSetScale(float); yzPostScriptSetScale(scale); current_offset_x = current_offset_y = 20; //render_avm(avm, &dy, &dx, 2); draw_gc_list(&tvdata->gcl, 0, 0, width, height, 1); yzPostScriptEndPage(); close(fd); gettimeofday(&tv2, 0); secs = tv2.tv_sec - tv1.tv_sec; secs += ((float)tv2.tv_usec - tv1.tv_usec) / 1000000; //fprintf(stderr, "printing took %.2f seconds\n", secs); sprintf(foo, "Printed AVM to '%s' at scale %.1f%%\n", fname, 100*scale); yzSelectWindow(old); console_add(foo); } void postscript_avm(embedded_view_t *view) { do_postscript_avm(view, view->width, view->height); } void latex_avm(embedded_view_t *view) { char fname[768], message[1024]; sprintf(fname, "/tmp/avm.%d.tex", tvdata->id); do_latex_avm(tvdata->avm, view->width, view->height, fname); sprintf(message, "Output AVM to `%s'\n", fname); console_add(message); }