#include #include #include #include #include #include #include #include #include #include "lkb-protocol.h" #include "embedded-view.h" #include #include "font-info.h" // parse xtext visualization int init_xtext_view(int id, embedded_view_t *view, lui_xtext *text, int nmenus, struct lui_xtext_menu **menus); void update_xtext(embedded_view_t *view, int x1, int y1, int x2, int y2); void event_xtext(embedded_view_t *view, event_t ev); void postscript_xtext(embedded_view_t *view); void latex_xtext(embedded_view_t *view); void close_xtext_view(embedded_view_t *view); void mouse_xtext(embedded_view_t *view, int x, int y); void render_xtext(lui_xtext *xtext, int hid, int *DY, int *DX, int display, int wwidth); embedded_view_t prototype_xtext_view = { init: (int(*)(embedded_view_t*,...))init_xtext_view, update: update_xtext, event: event_xtext, postscript: postscript_xtext, latex: latex_xtext, close: close_xtext_view, mouse_moved: mouse_xtext, }; struct xtext_view_data { int id, mouse_id; lui_xtext *xtext; int nmenus; struct lui_xtext_menu **menus; }; #define xdata ((struct xtext_view_data*)view->private) font_info xtext_plain_font = {"helvetica", 12, fsRoman, {0,0,0}, 0}; font_info xtext_active_font = {"helvetica", 12, fsRoman, {0,0,0}, 0}; color_t xtext_highlight_color = {0,0,0}; int xtext_margin = 10, xtext_leading = 0; int init_xtext_view(int id, embedded_view_t *view, lui_xtext *text, int nmenus, struct lui_xtext_menu **menus) { int now_xtext_width, now_xtext_height; view->private = malloc(sizeof(*xdata)); xdata->xtext = text; xdata->id = id; xdata->menus = malloc(sizeof(struct lui_xtext_menu*) * nmenus); memcpy(xdata->menus, menus, sizeof(struct lui_xtext_menu*) * nmenus); xdata->nmenus = nmenus; if(!xdata->xtext) { fprintf(stderr, "Error: tried to browse a NULL xtext structure!\n"); return -1; } xdata->mouse_id = -1; //printf("nedges %d\n", xtext->nedges); render_xtext(xdata->xtext, xdata->mouse_id, &now_xtext_height, &now_xtext_width, 0, view->vis_width - xtext_margin); now_xtext_width += xtext_margin*2; now_xtext_height += xtext_margin*2; view->width = now_xtext_width; view->height = now_xtext_height; return 0; } void close_xtext_view(embedded_view_t *view) { lkb_forget_text(xdata->id); free(xdata); } void update_xtext(embedded_view_t *view, int x1, int y1, int x2, int y2) { int now_xtext_width, now_xtext_height; struct timeval tv1, tv2; float secs; gettimeofday(&tv1, 0); yzPenColor(65535,65535,65535); yzRect(x1, y1, x2, y2); yzPenColor(0,0,0); render_xtext(xdata->xtext, xdata->mouse_id, &now_xtext_height, &now_xtext_width, 1, view->vis_width - xtext_margin); now_xtext_width += xtext_margin*2; now_xtext_height += xtext_margin*2; set_embedded_view_size(view, now_xtext_width, now_xtext_height); 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); } void event_xtext(embedded_view_t *view, event_t ev) { switch(ev.type) { case YZ_MOUSE_DOWN: handle_xtext_click(view, ev.x, ev.y, ev.button); break; } } static int pick_id(lui_xtext *xtext, int x, int y) { int i, id = -1; struct lui_xtext_command *n; for(i=0;icount;i++) { n = xtext->commands+i; if(n->opcode==lxt_set_id)id = (int)(long)n->string; if(n->opcode==lxt_string) { if(x>n->x && xx+n->dx && y>n->y && yy+n->dy) return id; else id = -1; } } return -1; } static struct lui_xtext_menu *lookup_menu(embedded_view_t *view, int id) { int i, j; struct xtext_view_data *xd = xdata; for(i=0;inmenus;i++) { if(!xd->menus[i]->nitems)continue; for(j=0;jmenus[i]->nids;j++) if(id == xd->menus[i]->ids[j])return xd->menus[i]; } return 0; } static void menu_action(embedded_view_t *view, struct lui_xtext_menu *menu, int cid, int item) { char command[1024]; if(item==0)return; sprintf(command, menu->send[item-1], cid); printf("%s \n", command);fflush(stdout); } int do_node_popup(embedded_view_t *view, struct lui_xtext_menu *menu, int cid, int x, int y) { window_t *par; lui_xtext *xtext = xdata->xtext; menu_data_t *md; char biglabel[128]; int id, i, ret = 0; md = yzNewMenuData("Options"); for(i=0;initems;i++) yzAddMenuDataItem(md, menu->show[i], i+1); par = yzGetSelectedWindow(); yzShowMenu(md, &id, x - 5, y); menu_action(view, menu, cid, id); // XXX should dispose memory here return ret; } static int do_other_popup(embedded_view_t *view, int x, int y) { window_t *par; lui_xtext *xtext = xdata->xtext; menu_data_t *md; int id, ret = 0; void latex_xtext(embedded_view_t *view); void postscript_xtext(embedded_view_t *view); md = yzNewMenuData("Text Options"); yzAddMenuDataItem(md, "Output Postscript", 3); //yzAddMenuDataItem(md, "Output LaTeX", 4); yzAddMenuDataItem(md, "Close Window", 5); par = yzGetSelectedWindow(); yzShowMenu(md, &id, x + par->origin_x, y + par->origin_y); switch(id) { case 0: break; // cancelled default: printf("unknown item %d\n", id); break; case 3: postscript_xtext(view); break; case 4: latex_xtext(view); break; case 5: sim_key('q'); break; } // XXX should dispose memory here return ret; } handle_xtext_click(embedded_view_t *view, int x, int y, int button) { int id = pick_id(xdata->xtext, x, y); event_t ev; int update = 0; struct lui_xtext_menu *menu = 0; if(id!=-1)menu = lookup_menu(view, id); if(menu==0) { if(button!=1 || yzQueryKey(YZ_KEYCODE_CONTROL)) update = do_other_popup(view, x, y); } else { if(button!=1 || yzQueryKey(YZ_KEYCODE_CONTROL)) update = do_node_popup(view, menu, id, x, y); else menu_action(view, menu, id, 1); // do default click action } if(update) { yzBufferMode(YZ_BUFFER_BACK); update_xtext(view, 0, 0, view->width, view->height); yzUpdateBuffer(YZ_BUFFER_FRONT); } } void mouse_xtext(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 latex_xtext(embedded_view_t *view) { } void postscript_xtext(embedded_view_t *view) { window_t *xtext_ps, *old; int dy, dx, fd; float scale; char message[1024], fname[768] = "xtext-print.ps"; sprintf(fname, "/tmp/xtext.%d.ps", 0); unlink(fname); fd = open(fname, O_WRONLY | O_CREAT, 0664); xtext_ps = yzPostScriptWindow("Text Print", 555, 800, fd); old = yzSelectWindow(xtext_ps); render_xtext(xdata->xtext, 0, &dy, &dx, 0, 500); scale = 660.0 / dy; if((550.0 / dx) < scale) scale = 550.0 / dx; extern int yzPostScriptSetScale(float); yzPostScriptSetScale(scale); yzPushOrigin((int)(25.0 / scale), (int)(35.0 / scale)); render_xtext(xdata->xtext, 0, &dx, &dy, 1, 500); //yzPopOrigin(); yzPostScriptEndPage(); close(fd); sprintf(message, "Printed xtext to `%s' at scale %.1f%%\n", fname, 100*scale); console_add(message); yzSelectWindow(old); render_xtext(xdata->xtext, 0, &dx, &dy, 0, view->vis_width-xtext_margin); } // layout/rendering code int style_text(int x, int y, int dx, int dy, char *t, int bold, int fs); int font_line_height(font_info *f) { use_font(f); return yzDefaultLineHeight(); } void render_xtext(lui_xtext *xtext, int hid, int *DY, int *DX, int display, int wwidth) { int i, x, y = xtext_margin + xtext_plain_font.size, maxx = xtext_margin; int margin[1024], msp = 0, canwrap = 0, cid = -10, j, bdx, vspace; struct lui_xtext_command *n; struct font_info *fnt = &xtext_plain_font, *nf; // calculate the sizes of all the strings int passive_vspace = font_line_height(&xtext_plain_font); int active_vspace = font_line_height(&xtext_active_font); vspace = (passive_vspace > active_vspace) ? passive_vspace : active_vspace; vspace += xtext_leading; use_font(fnt); for(cid=-10,i=0;icount;i++) { n = xtext->commands+i; if(n->opcode == lxt_set_id)cid = (int)(long)n->string; if(n->opcode != lxt_string)continue; if(cid != -10)nf = &xtext_active_font; else nf = &xtext_plain_font; if(nf != fnt)use_font(fnt = nf); n->dx = yzStringSize(n->string, strlen(n->string)); n->dy = fnt->size * 1.2; // FIXME 1.2 x fontsize is pretty decent for roman fonts but apparently not for japanese and others cid = -10; } // layout using 'wwidth' as linewrap width, // but allowing extra width if necessary (and reporting it in 'maxx') x = margin[0] = xtext_margin; for(cid=-10,i=0;icount;i++) { n = xtext->commands+i; switch(n->opcode) { case lxt_newline: y += vspace; x = margin[msp]; break; case lxt_tab: x = (x+63)&(~63); break; case lxt_push: if(msp<1023)margin[++msp] = x; break; case lxt_pop: if(msp>0)--msp; break; case lxt_wrap: canwrap = 1; break; case lxt_set_id: cid = (int)(long)n->string; break; case lxt_string: if(canwrap) { bdx = x; for(j=0;j+icount;j++) { if(n[j].opcode==lxt_string) bdx += n[j].dx; if(n[j].opcode==lxt_tab) bdx = (bdx+63)&(~63); if(n[j].opcode==lxt_wrap || n[j].opcode==lxt_newline) break; } if(bdx > wwidth && x != margin[msp]) { // newline if it helps y += vspace; x = margin[msp]; } canwrap = 0; } if(cid!=-10)nf = &xtext_active_font; else nf = &xtext_plain_font; if(fnt != nf)use_font(fnt = nf); if(display) { if(cid==hid)yzPenColor1c(xtext_highlight_color); style_text(x, y, n->dx, nf->size*1.5, n->string, (cid==hid)?2:0, nf->size); if(cid==hid)yzPenColor1c(nf->color); } n->x = x; n->y = y - nf->size; x += n->dx; if(x > maxx)maxx = x; cid = -10; break; } } *DY = y - xtext_margin + vspace*0.3; *DX = maxx - xtext_margin; }