From 531c229b0e8f480c0b8e0cd6d5065b3dcd734230 Mon Sep 17 00:00:00 2001 From: h_vogt Date: Wed, 4 Oct 2017 15:35:03 +0200 Subject: [PATCH] Add library xft for allowing text rotation. Setting variabe 'old_x11' will retain previous behaviour. --- src/frontend/plotting/x11.c | 188 +++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 36 deletions(-) diff --git a/src/frontend/plotting/x11.c b/src/frontend/plotting/x11.c index f581028f5..e7d0d9626 100644 --- a/src/frontend/plotting/x11.c +++ b/src/frontend/plotting/x11.c @@ -14,6 +14,7 @@ Author: 1988 Jeffrey M. Hsu # include # include /* PN */ # include /* PN */ +# include # include "ngspice/graph.h" # include "ngspice/ftedbgra.h" @@ -37,6 +38,8 @@ Author: 1988 Jeffrey M. Hsu # include # include +#include + # ifdef DEBUG # include /* for _Xdebug */ # endif @@ -66,6 +69,11 @@ typedef struct x11info { int lastx, lasty; /* used in X_DrawLine */ int lastlinestyle; /* used in X_DrawLine */ Pixel colors[NUMCOLORS]; + /* use with xft */ + char txtcolor[16]; + char bgcolor[16]; + char fname[BSIZE_SP]; + int fsize; } X11devdep; #define DEVDEP(g) (*((X11devdep *) (g)->devdep)) @@ -91,6 +99,8 @@ static Bool noclear = False; static GRAPH *lasthardcopy; /* graph user selected */ static int X11_Open = 0; static int numdispplanes; +static int xfont_size; +static char fontname[513]; /* static functions */ static void initlinestyles(void); @@ -99,6 +109,7 @@ static void X_ScreentoData(GRAPH *graph, int x, int y, double *fx, double *fy); static void linear_arc(int x0, int y0, int radius, double theta, double delta_theta); static void slopelocation(GRAPH *graph, int x0, int y0); static void zoomin(GRAPH *graph); +static int Xget_str_length(char *text, int* wlen, int* wheight, XftFont* gfont, char* name, int fsize); //XtEventHandler static void handlekeypressed(Widget w, XtPointer clientdata, XEvent *ev, Boolean *continue_dispatch); @@ -139,6 +150,9 @@ X11_Init(void) return (1); } + if (cp_getvar("old_x11", CP_BOOL, NULL)) + old_x11 = TRUE; + # ifdef DEBUG _Xdebug = 1; # endif @@ -149,9 +163,11 @@ X11_Init(void) argv[2] = "-geometry"; argv[3] = "=1x1+2+2"; */ - + /* initialze internationalization (i18n) */ + XtSetLanguageProc(NULL, NULL, NULL); /* initialize X toolkit */ toplevel = XtInitialize("ngspice", "Nutmeg", NULL, 0, &argc, argv); + setlocale(LC_NUMERIC, "C"); display = XtDisplay(toplevel); @@ -235,6 +251,12 @@ initcolors(GRAPH *graph) (void) sprintf(buf, "color%d", i); if (!cp_getvar(buf, CP_STRING, colorstring, sizeof(colorstring))) (void) strcpy(colorstring, colornames[i]); + /* text color info for xft */ + if((!old_x11) && (i == 1)) + strncpy(DEVDEP(graph).txtcolor, colorstring, 15); + else if((!old_x11) && (i == 0)) + strncpy(DEVDEP(graph).bgcolor, colorstring, 15); + if (!XAllocNamedColor(display, DefaultColormap(display, DefaultScreen(display)), colorstring, &visualcolor, &exactcolor)) { @@ -348,7 +370,7 @@ handle_wm_messages(Widget w, XtPointer client_data, XEvent *ev, Boolean *cont) int X11_NewViewport(GRAPH *graph) { - char fontname[513]; /* who knows . . . */ +// char fontname[513]; /* who knows . . . */ char *p, *q; Cursor cursor; XSetWindowAttributes w_attrs; @@ -376,8 +398,8 @@ X11_NewViewport(GRAPH *graph) }; static Arg viewargs[] = { { XtNresizable, (XtArgVal) TRUE }, - { XtNwidth, (XtArgVal) 300 }, - { XtNheight, (XtArgVal) 300 }, + { XtNwidth, (XtArgVal) 600 }, + { XtNheight, (XtArgVal) 500 }, { XtNright, (XtArgVal) XtChainRight } }; int trys; @@ -389,6 +411,7 @@ X11_NewViewport(GRAPH *graph) DEVDEP(graph).shell = XtCreateApplicationShell ("shell", topLevelShellWidgetClass, NULL, 0); + XtVaSetValues(DEVDEP(graph).shell, XtNtitleEncoding, XInternAtom(display, "UTF8_STRING", False), NULL); XtVaSetValues(DEVDEP(graph).shell, XtNtitle, graph->plotname, NULL); /* set up form widget */ @@ -428,32 +451,47 @@ X11_NewViewport(GRAPH *graph) /* set up fonts */ if (!cp_getvar("xfont", CP_STRING, fontname, sizeof(fontname))) (void) strcpy(fontname, DEF_FONT); + strncpy(DEVDEP(graph).fname, fontname, BSIZE_SP - 1); - for (p = fontname; *p && *p <= ' '; p++) - ; - - if (p != fontname) { - for (q = fontname; *p; *q++ = *p++) + if(old_x11) { + for (p = fontname; *p && *p <= ' '; p++) ; - *q = '\0'; - } - trys = 1; - while (!(DEVDEP(graph).font = XLoadQueryFont(display, fontname))) { - sprintf(ErrorMessage, "can't open font %s", fontname); - strcpy(fontname, "fixed"); - if (trys > 1) { - internalerror(ErrorMessage); - RECOVERNEWVIEWPORT(); - return (1); + if (p != fontname) { + for (q = fontname; *p; *q++ = *p++) + ; + *q = '\0'; + } + trys = 1; + while (!(DEVDEP(graph).font = XLoadQueryFont(display, fontname))) { + sprintf(ErrorMessage, "can't open font %s", fontname); + strcpy(fontname, "fixed"); + if (trys > 1) { + internalerror(ErrorMessage); + RECOVERNEWVIEWPORT(); + return (1); + } + trys += 1; } - trys += 1; } - graph->fontwidth = DEVDEP(graph).font->max_bounds.rbearing - - DEVDEP(graph).font->min_bounds.lbearing + 1; - graph->fontheight = DEVDEP(graph).font->max_bounds.ascent + - DEVDEP(graph).font->max_bounds.descent + 1; + /* font size */ + if (!cp_getvar("xfont_size", CP_NUM, &xfont_size)) + xfont_size = 16; + + if(old_x11) { + graph->fontwidth = DEVDEP(graph).font->max_bounds.rbearing - + DEVDEP(graph).font->min_bounds.lbearing + 1; + graph->fontheight = DEVDEP(graph).font->max_bounds.ascent + + DEVDEP(graph).font->max_bounds.descent + 1; + } + else { + int wl, wh; + Xget_str_length("ABCD", &wl, &wh, NULL, fontname, xfont_size); + graph->fontwidth = (int)(wl / 4); + graph->fontheight = wh; + DEVDEP(graph).fsize = xfont_size; + } XtRealizeWidget(DEVDEP(graph).shell); @@ -462,13 +500,19 @@ X11_NewViewport(GRAPH *graph) w_attrs.bit_gravity = ForgetGravity; XChangeWindowAttributes(display, DEVDEP(graph).window, CWBitGravity, &w_attrs); + /* have to note font and set mask GCFont in XCreateGC, p.w.h. */ - gcvalues.font = DEVDEP(graph).font->fid; gcvalues.line_width = MW_LINEWIDTH; gcvalues.cap_style = CapNotLast; gcvalues.function = GXcopy; - DEVDEP(graph).gc = XCreateGC(display, DEVDEP(graph).window, + if (old_x11) { + gcvalues.font = DEVDEP(graph).font->fid; + DEVDEP(graph).gc = XCreateGC(display, DEVDEP(graph).window, GCFont | GCLineWidth | GCCapStyle | GCFunction, &gcvalues); + } + else + DEVDEP(graph).gc = XCreateGC(display, DEVDEP(graph).window, + GCLineWidth | GCCapStyle | GCFunction, &gcvalues); /* should absolute.positions really be shell.pos? */ graph->absolute.xpos = DEVDEP(graph).view->core.x; @@ -483,6 +527,7 @@ X11_NewViewport(GRAPH *graph) cursor = XCreateFontCursor(display, XC_left_ptr); XDefineCursor(display, DEVDEP(graph).window, cursor); + /* WM_DELETE_WINDOW protocol */ atom_wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False); atom_wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); @@ -554,16 +599,59 @@ X11_Text(char *text, int x, int y, int angle) { /* We specify text position by lower left corner, so have to adjust for X11's font nonsense. */ + NG_IGNORE(angle); - if (DEVDEP(currentgraph).isopen) - XDrawString(display, DEVDEP(currentgraph).window, - DEVDEP(currentgraph).gc, x, - currentgraph->absolute.height - - (y + DEVDEP(currentgraph).font->max_bounds.descent), - text, (int) strlen(text)); + int wlen = 0, wheight = 0; - /* note: unlike before, we do not save any text here */ + if(old_x11) { + if (DEVDEP(currentgraph).isopen) + XDrawString(display, DEVDEP(currentgraph).window, + DEVDEP(currentgraph).gc, x, + currentgraph->absolute.height + - (y + DEVDEP(currentgraph).font->max_bounds.descent), + text, (int) strlen(text)); + + /* note: unlike before, we do not save any text here */ + return 0; + } + + /* new font selection with rotation */ + XftPattern *new_pat = XftPatternCreate(); // the pattern we will use for rotating + XftPatternAddString(new_pat, XFT_FAMILY, DEVDEP(currentgraph).fname); + XftPatternAddDouble (new_pat, XFT_PIXEL_SIZE, (double)DEVDEP(currentgraph).fsize); + + if(angle != 0){ + XftMatrix m; + XftMatrixInit(&m); + XftMatrixRotate(&m,cos(M_PI*angle/180.),sin(M_PI*angle/180.)); + XftPatternAddMatrix (new_pat, XFT_MATRIX,&m); + } + + XftResult rot_result; + XftPattern *rot_pat = XftFontMatch(display, 0, new_pat, &rot_result); /* do not destroy!*/ + XftFont *new_font = XftFontOpenPattern(display, rot_pat); + XftPatternDestroy(new_pat); + + /* calculate and add offset, if ylabel with angle 90° */ + if (angle == 90) { + Xget_str_length(text, &wlen, &wheight, new_font, NULL, 0); + } + + Colormap cmap = DefaultColormap(display, 0); + XftColor color; + XftColorAllocName(display, DefaultVisual(display, 0), cmap, DEVDEP(currentgraph).txtcolor, &color); + XftDraw* draw = XftDrawCreate( + display, DEVDEP(currentgraph).window, DefaultVisual(display, 0), cmap + ); + /* Draw text */ + XftDrawStringUtf8( + draw, &color, new_font, + x + (int)(1.5 * wlen), currentgraph->absolute.height - y + (int)(0.5 * wheight), (FcChar8*)text, strlen(text) + ); + XftFontClose( display, new_font); + XftDrawDestroy(draw); + XftColorFree(display, DefaultVisual(display, 0), cmap, &color); return 0; } @@ -828,8 +916,9 @@ zoomin(GRAPH *graph) "setplot %s; %s xlimit %.20e %.20e ylimit %.20e %.20e; setplot $curplot\n", buf2, graph->commandline, fx0, fx1, fy0, fy1); } else { - (void) sprintf(buf, "%s xlimit %e %e ylimit %e %e\n", - graph->commandline, fx0, fx1, fy0, fy1); + /* set the foreground and background colors to the "calling" window's colors */ + (void) sprintf(buf, "set color0=%s; set color1=%s; %s xlimit %e %e ylimit %e %e\n", + DEVDEP(graph).bgcolor, DEVDEP(graph).txtcolor, graph->commandline, fx0, fx1, fy0, fy1); } /* don't use the following if using GNU Readline or BSD EditLine */ @@ -906,7 +995,8 @@ void RemoveWindow(GRAPH *graph) /* MW. Not sure but DestroyGraph might free() too much - try Xt...() first */ XtUnmapWidget(DEVDEP(graph).shell); XtDestroyWidget(DEVDEP(graph).shell); - XFreeFont(display, DEVDEP(graph).font); + if (old_x11) + XFreeFont(display, DEVDEP(graph).font); XFreeGC(display, DEVDEP(graph).gc); } @@ -1105,6 +1195,32 @@ linear_arc(int x0, int y0, int radius, double theta, double delta_theta) } } + /* After font selection for XftTextExtentsUtf8 + * to measure character string length. + * Same as rotation below, but 0° angle */ +static int +Xget_str_length(char *text, int* wlen, int* wheight, XftFont* gfont, char* foname, int fsize) { + XGlyphInfo extents; + XftFont* hfont = NULL; + /* if font name and font size are given */ + if (gfont == NULL) { + XftPattern *ext_pat = XftPatternCreate(); // the pattern we will use for rotating + XftPatternAddString(ext_pat, XFT_FAMILY, foname); + XftPatternAddDouble(ext_pat, XFT_PIXEL_SIZE, (double)fsize); + XftResult ext_result; + hfont = gfont = XftFontOpenPattern(display, XftFontMatch(display, 0, ext_pat, &ext_result)); + XftTextExtentsUtf8(display, gfont, (XftChar8 *)text, strlen(text), &extents); + XftPatternDestroy(ext_pat); + } + XftTextExtentsUtf8( display, gfont, (XftChar8 *)text, strlen(text), &extents ); + if(hfont) + XftFontClose( display,hfont); + + /* size of the string */ + *wlen = extents.width; + *wheight = extents.height; + return 0; +} #else