/* TODO "unique" the edge yield strings interface for efficiently selecting a string -- typing, followed by infix tree navigation? click a string to bring up a parse-chart-like window */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "lkb-protocol.h" #include "lui-genchart.h" #include "embedded-view.h" #include #include #include "font-info.h" // generator chart visualization font_info word_font = {"helvetica", 12, fsRoman, {0,20000,40000}, NULL}; font_info infix_font = {"helvetica", 18, fsBold, {30000,0,0}, NULL}; font_info periphery_font = {"helvetica", 12, fsRoman, {0,0,0}, NULL}; int init_genchart_view(int id, embedded_view_t *view, lui_genchart *text); void update_genchart(embedded_view_t *view, int x1, int y1, int x2, int y2); void event_genchart(embedded_view_t *view, event_t ev); void close_genchart_view(embedded_view_t *view); void mouse_genchart(embedded_view_t *view, int x, int y); void render_genchart(embedded_view_t *view, int *DY, int *DX, int display, int wwidth); embedded_view_t prototype_genchart_view = { init: (int(*)(struct embedded_view_t*,...))init_genchart_view, update: update_genchart, event: event_genchart, postscript: NULL, latex: NULL, close: close_genchart_view, mouse_moved: mouse_genchart, }; struct genstring { char *string; int nedges; lui_genedge **edges; }; struct genword { char *word; int x1, y1, x2, y2; }; struct genaffix { char *affix, *radix; int hits; int x1, y1, x2, y2; }; struct genchart_view_data { char *infix; // only show edges containing this string int nstrings; struct genstring *strings; int nwords; struct genword *words; int nprefixes; struct genaffix *prefixes; int nsuffixes; struct genaffix *suffixes; int nactive_edges; lui_genchart *genchart; int infix_x1, infix_x2, infix_y1, infix_y2; }; void *stringlist_find(int *N, void **L, int size, char *str, int add) { int i, n=*N; for(i=0;iprivate) font_info genchart_plain_font = {"helvetica", 12, fsRoman, {0,0,0}, 0}; color_t genchart_highlight_color = {32000,32000,32000}; int genchart_margin = 10, genchart_leading = 0; int init_genchart_view(int id, embedded_view_t *view, lui_genchart *chart) { int now_genchart_width, now_genchart_height; view->private = calloc(sizeof(struct genchart_view_data),1); view->capture_all_keys = 1; xdata->genchart = chart; xdata->infix = NULL; assert(xdata->genchart); int i; for(i=0;inedges;i++) { struct genstring *s = stringlist_find(&xdata->nstrings, (void**)&xdata->strings, sizeof(struct genstring), chart->edges[i]->yield, 1); s->nedges++; s->edges = realloc(s->edges, sizeof(struct genedge*)*s->nedges); s->edges[s->nedges-1] = chart->edges[i]; } qsort(xdata->strings, xdata->nstrings, sizeof(struct genstring), deref_strcasecmp); for(i=0;instrings;i++) { char *copy = strdup(xdata->strings[i].string); char *word; for(word = strtok(copy, " ") ; word ; word = strtok(NULL, " ")) stringlist_find(&xdata->nwords, (void**)&xdata->words, sizeof(struct genword), word, 1); free(copy); } qsort(xdata->words, xdata->nwords, sizeof(struct genword), deref_strcasecmp); //printf("nedges %d\n", xdata->nedges); render_genchart(view, &now_genchart_height, &now_genchart_width, 0, view->vis_width - genchart_margin); now_genchart_width += genchart_margin*2; now_genchart_height += genchart_margin*2; view->width = now_genchart_width; view->height = now_genchart_height; return 0; } void close_genchart_view(embedded_view_t *view) { lkb_forget_text(xdata->genchart->id); free(xdata); } void mouse_genchart(embedded_view_t *view, int x, int y) { /*int id = pick_id(xdata->xtext, x, y); if(id != xdata->mouse_id) { xdata->mouse_id = id; yzDrawScrollArea(view->scroll_area); }*/ } void update_genchart(embedded_view_t *view, int x1, int y1, int x2, int y2) { int now_genchart_width, now_genchart_height; yzPenColor(65535,65535,65535); yzRect(x1, y1, x2, y2); yzPenColor(0,0,0); render_genchart(view, &now_genchart_height, &now_genchart_width, 1, view->vis_width - genchart_margin); now_genchart_width += genchart_margin*2; now_genchart_height += genchart_margin*2; set_embedded_view_size(view, now_genchart_width, now_genchart_height); } void add_affix(int *N, struct genaffix **L, char *affix, char *radix, int hits, int reversed) { int i, j, n = *N, maxshare_i = -1, maxshare_l = 0; for(i=0;i0 && radix[j] == ' '){l = j;} } if((!radix[j]||radix[j]==' ') && (!s[j]||s[j]==' ')){l = j;} if(l > maxshare_l) { maxshare_i = i; maxshare_l = l; } } //printf("insert '%s' = '%s'; maxshare_l = %d\n", radix, affix, maxshare_l); if(maxshare_i != -1) { // found an affix that we can share with if(maxshare_l < strlen((*L)[maxshare_i].radix)) { // shortening this entry. // this can't cause it to collide with other entries, // since if it shared a prefix with another entry we wouldn't have inserted one of them separately. char *shorter = (*L)[maxshare_i].radix; shorter[maxshare_l] = 0; char *shorter_affix = (*L)[maxshare_i].affix; if(!reversed)shorter_affix[maxshare_l] = 0; else { //printf("shortening prefix '%s' ... ", shorter_affix); int trim = strlen(shorter_affix) - maxshare_l; memmove(shorter_affix, shorter_affix+trim, strlen(shorter_affix+trim)+1); //printf("into '%s'\n", shorter_affix); } } (*L)[maxshare_i].hits += hits; } else { // nothing with a matching prefix at all. new entry. (*L) = realloc(*L,sizeof(struct genaffix)*(n+1)); *N = n+1; struct genaffix *a = &(*L)[n]; a->radix = strdup(radix); a->affix = strdup(affix); a->hits = hits; } } void reset_affixes(struct embedded_view_t *view) { int i; for(i=0;inprefixes;i++) { free(xdata->prefixes[i].affix); free(xdata->prefixes[i].radix); } free(xdata->prefixes); xdata->prefixes = NULL; xdata->nprefixes = 0; for(i=0;insuffixes;i++) { free(xdata->suffixes[i].affix); free(xdata->suffixes[i].radix); } free(xdata->suffixes); xdata->suffixes = NULL; xdata->nsuffixes = 0; xdata->nactive_edges = 0; } int valid_infix(struct embedded_view_t *view, char *infix) { int i; for(i=0;instrings;i++) if(strcasestr(xdata->strings[i].string, infix))return 1; return 0; } void find_affixes(struct embedded_view_t *view) { int i, j; reset_affixes(view); //printf("looking for infix `%s'\n", xdata->infix); for(i=0;instrings;i++) { char *s = xdata->strings[i].string; char *in = strcasestr(s, xdata->infix); if(!in)continue; if(!strcasecmp(s, xdata->infix))xdata->nactive_edges += xdata->strings[i].nedges; char *after = strdup(in + strlen(xdata->infix)); //printf("%s\n", s); //printf("in = `%s'\n", in); //if(!*after){free(after);after = strdup("");} //printf("after = `%s'\n", after); if(*after) add_affix(&xdata->nsuffixes, &xdata->suffixes, after, after, xdata->strings[i].nedges, 0); char *before = malloc(in-s + 1); char *before_backwards = malloc(in-s + 1); for(j=0;j<(in-s);j++) { before[j] = s[j]; before_backwards[j] = in[-1-j]; } before[j] = 0; before_backwards[j] = 0; //if(!*before){free(before);free(before_backwards);before = strdup("");before_backwards=strdup("");} //printf("before = %s, after = %s\n", before, after); if(*before) add_affix(&xdata->nprefixes, &xdata->prefixes, before, before_backwards, xdata->strings[i].nedges, 1); free(before); free(after); free(before_backwards); } } compose_infix(struct embedded_view_t *view, char *prefix, char *suffix) { char *newinfix = malloc(strlen(prefix) + strlen(suffix) + 1); sprintf(newinfix, "%s%s", prefix, suffix); free(xdata->infix); xdata->infix = newinfix; find_affixes(view); yzDrawScrollArea(view->scroll_area); } void click_infix(struct embedded_view_t *view, int x, int y) { int i; if(x >= xdata->infix_x1 && x<= xdata->infix_x2 && y>=xdata->infix_y1 && y<=xdata->infix_y2) { printf("genchart %d explore [", xdata->genchart->id); for(i=0;instrings;i++) if(!strcasecmp(xdata->strings[i].string, xdata->infix)) { int j; struct genstring *s = &xdata->strings[i]; for(j=0;jnedges;j++) printf(" %d", s->edges[j]->id); } printf("] \n"); fflush(stdout); return; } for(i=0;inprefixes;i++) { struct genaffix *w = &xdata->prefixes[i]; if(w->x1 <= x && w->x2 >= x && w->y1 <= y && w->y2 >= y) { compose_infix(view, w->affix, xdata->infix); return; } } for(i=0;insuffixes;i++) { struct genaffix *w = &xdata->suffixes[i]; if(w->x1 <= x && w->x2 >= x && w->y1 <= y && w->y2 >= y) { compose_infix(view, xdata->infix, w->affix); return; } } } void click_word(struct embedded_view_t *view, int x, int y) { int i; for(i=0;inwords;i++) { struct genword *w = &xdata->words[i]; if(w->x1 <= x && w->x2 >= x && w->y1 <= y && w->y2 >= y) { //printf("click word picked '%s'\n", w->word); xdata->infix = strdup(w->word); find_affixes(view); yzDrawScrollArea(view->scroll_area); return; } } } void kill_infix(embedded_view_t *view) { if(xdata->infix)free(xdata->infix); xdata->infix = NULL; yzDrawScrollArea(view->scroll_area); } void shrink_infix(embedded_view_t *view) { if(!xdata->infix || !*xdata->infix)return; xdata->infix[strlen(xdata->infix)-1] = 0; find_affixes(view); yzDrawScrollArea(view->scroll_area); } void grow_infix(embedded_view_t *view, int key) { char *now = xdata->infix?:""; char *bigger = malloc(strlen(now)+2); sprintf(bigger, "%s%c", now, key); if(!valid_infix(view, bigger))return; if(xdata->infix)free(xdata->infix); xdata->infix = bigger; find_affixes(view); yzDrawScrollArea(view->scroll_area); } void event_genchart(embedded_view_t *view, event_t ev) { switch(ev.type) { case YZ_MOUSE_DOWN: if(xdata->infix)click_infix(view, ev.x - genchart_margin, ev.y - genchart_margin); else click_word(view, ev.x - genchart_margin, ev.y - genchart_margin); return; case YZ_KEY_DOWN: if(ev.key >= 256)return; if(ev.key == 'u' && yzQueryKey(YZ_KEYCODE_CONTROL) == 1)kill_infix(view); if(ev.key == 'q' && yzQueryKey(YZ_KEYCODE_CONTROL) == 1) { window_t *win = yzGetSelectedWindow(); close_view(view); deregister_window_manager(win); yzDeleteWindow(win); return; } else if(ev.key == '' || ev.key == '')shrink_infix(view); else if(ev.key == '\n') { if(xdata->infix)click_infix(view, xdata->infix_x1, xdata->infix_y1); } else grow_infix(view, ev.key); return; } } void render_genchart_words(embedded_view_t *view, int *DY, int *DX, int display, int wwidth) { use_font(&word_font); int line_height = yzDefaultLineHeight(); int max_width = 0; int i; for(i=0;inwords;i++) { int w = yzStringSize(xdata->words[i].word, strlen(xdata->words[i].word)); if(w>max_width)max_width = w; xdata->words[i].x1 = 0; xdata->words[i].y1 = 0; xdata->words[i].x2 = w; xdata->words[i].y2 = line_height; } *DX = max_width; *DY = line_height * xdata->nwords + 10; int baseline = line_height * 0.8; if(display) { int y = 0; for(i=0;inwords;i++) { yzText(0, y + baseline, xdata->words[i].word); xdata->words[i].y1 += y; xdata->words[i].y2 += y; y += line_height; //yzOutlineRect(xdata->words[i].x1, xdata->words[i].y1, xdata->words[i].x2, xdata->words[i].y2); } } } void render_genchart_infix(embedded_view_t *view, int *DY, int *DX, int display, int wwidth) { char countstr[128]; sprintf(countstr, "%d edges with this yield", xdata->nactive_edges); use_font(&infix_font); int ilen = yzStringSize(xdata->infix, strlen(xdata->infix)); int iheight = yzDefaultLineHeight(); int pwidth = 0, swidth = 0, pheight = 0, sheight = 0, aheight = 0; use_font(&periphery_font); int count_width = yzStringSize(countstr, strlen(countstr)); int plh = yzDefaultLineHeight(); int i; pheight = plh * xdata->nprefixes; for(i=0;inprefixes;i++) { int w = yzStringSize(xdata->prefixes[i].affix, strlen(xdata->prefixes[i].affix)); if(w > pwidth)pwidth = w; } sheight = plh * xdata->nsuffixes; for(i=0;insuffixes;i++) { int w = yzStringSize(xdata->suffixes[i].affix, strlen(xdata->suffixes[i].affix)); if(w > swidth)swidth = w; } if(sheight > pheight)aheight = sheight; else aheight = pheight; int baseline = 0.8*plh; int infix_x = pwidth + 10; int suffix_x = infix_x + ilen + 10; *DX = ilen + pwidth +10 + swidth + 10; if(count_width + 20 > *DX)*DX = count_width + 20; if(aheight > iheight)*DY = aheight + plh; else *DY = iheight + plh; if(display) { use_font(&infix_font); yzText(infix_x,iheight + plh, xdata->infix); xdata->infix_x1 = infix_x; xdata->infix_x2 = infix_x + ilen; xdata->infix_y1 = plh; xdata->infix_y2 = plh + iheight*1.5; use_font(&periphery_font); yzText(10, plh, countstr); for(i=0;inprefixes;i++) { int w = yzStringSize(xdata->prefixes[i].affix, strlen(xdata->prefixes[i].affix)); int y = i*plh + plh; yzText(pwidth - w, y + baseline, xdata->prefixes[i].affix); xdata->prefixes[i].x1 = pwidth-w; xdata->prefixes[i].x2 = pwidth; xdata->prefixes[i].y1 = y; xdata->prefixes[i].y2 = y + plh; } for(i=0;insuffixes;i++) { int w = yzStringSize(xdata->suffixes[i].affix, strlen(xdata->suffixes[i].affix)); int y = i*plh + plh; yzText(suffix_x, y + baseline, xdata->suffixes[i].affix); xdata->suffixes[i].x1 = suffix_x; xdata->suffixes[i].x2 = suffix_x + w; xdata->suffixes[i].y1 = y; xdata->suffixes[i].y2 = y + plh; } } } void render_genchart(embedded_view_t *view, int *DY, int *DX, int display, int wwidth) { const int min_genchart_width = 400, min_genchart_height = 250; yzPushOrigin(genchart_margin, genchart_margin); if(!xdata->infix) render_genchart_words(view, DY, DX, display, wwidth); else render_genchart_infix(view, DY, DX, display, wwidth); if(DX && *DX < min_genchart_width)*DX = min_genchart_width; if(DY && *DY < min_genchart_height)*DY = min_genchart_height; yzPopOrigin(); }