/* Part of XPCE --- The SWI-Prolog GUI toolkit Author: Jan Wielemaker and Anjo Anjewierden E-mail: jan@swi.psy.uva.nl WWW: http://www.swi.psy.uva.nl/projects/xpce/ Copyright (c) 1985-2002, University of Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Public functions: d_window(sw, x, y, w, h) Initialise for drawing on specified window and area. Sets clip area and clears the area. d_image(i, x, y, w, h) Initialise for drawing on specified image and area. Sets clip area, but does not d_clip(x, y, w, h) Narrow clip area to specified area. d_done() Exit clip or device setup. d_display(d) Make some display the current one d_flush() Flush output to current display *** AREA MANAGEMENT *** r_clear(x, y, w, h) Clear specified area (fill with background color). r_complement(x, y, w, h) Invert the specified area. r_and(x, y, w, h, pattern) And an area with a pattern r_fill(x, y, w, h, pattern) Fill specified rectangle with a pattern. r_fill_polygon(pts, n) Fill a polygon r_fill_triangle(x1, y1, x2, y2, x3, y3) *** GRAPHICS STATE *** r_thickness(pen) Set line width. r_dash(dash) Set dash pattern. r_fillpattern(image, Name which)Set fill pattern r_andpattern(image) Set andpattern for greying r_default_colour(colour) Set default foreground colour r_colour(colour) Set foreground colour *** DRAWING *** r_box(x, y, w, h, round, fill) Draw filled box with rounded corners. r_ellipse(x, y, w, h, fill) Draw filled ellipse r_line(x1, y1, x2, y2) Draw line. r_polygon(pts, n, close) Draw a polygon r_path(points, ox, oy, radius, close, fill) Draw a line through points r_image(bm, sx, sy, x. y, w, h, transparent) Copy an image r_set_pixel(x, y) Set a pixel to the foreground color r_clear_pixel(x, y) Set a pixel to the background color r_complement_pixel(x, y) Invert a pixel *** READING *** r_get_pixel(x, y) Return pixel value at (x,y) *** TEXT MANAGEMENT *** s_size(s, f, *w, *h) Compute size of a string in a font s_string(s, f, x, y, w, h, had, vad) Draw text 's' in font f at (x, y, w, h) adjusted horizontally and vertically - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include #include #include #include "include.h" static void clip_area(int *, int *, int *, int *); static void r_andpattern(Image i); #ifdef USE_XFT static XftDraw *xftDraw(); #endif /******************************** * DEVICE FUNCTIONS * ********************************/ #define MAX_CLIP_NESTING (50) static int quick; /* display quick_and_dirty */ static struct environment { iarea area; /* clip rectangle */ int level; /* nesting level */ } environments[MAX_CLIP_NESTING]; static struct environment *env = environments; typedef struct d_context *DContext; static struct d_context { DContext parent; /* saved parent context */ DrawContext gcs; /* The X GC's */ Display *display; /* Current drawing display */ int screen; /* X screen */ Visual *visual; /* X visual */ Colormap colormap; /* X colourmap */ Drawable drawable; /* X Object we are drawing on */ #ifdef USE_XFT XftDraw *xft_draw; /* XFT drawable */ #endif Name kind; /* Drawable kind */ int depth; /* depth of drawable */ DisplayObj pceDisplay; /* PCE display object */ Image cache; /* Actually writing here */ Window window; /* Window we are caching for */ Any default_background; /* Default background colour */ Any default_colour; /* Colour for @default */ int cache_x; /* X-offset of cache */ int cache_y; /* Y-offset of cache */ int cache_w; /* Width of cache */ int cache_h; /* Height of cache */ int offset_x; /* Paint offset in X direction */ int offset_y; /* Paint offset in Y direction */ int origin_x; /* Origin-X relative to drawable */ int origin_y; int fill_offset_x; /* X-offset for filling */ int fill_offset_y; /* Y-offset for filling */ int fixed_colours; /* The colours are fixed */ /* Save over d_image()/d_done() */ Any colour; /* preserve current colours */ Any background; } context; #define X(x) ((x) + context.offset_x) #define Y(y) ((y) + context.offset_y) #define Translate(x, y) { (x) = X(x); (y) = Y(y); } #define InvTranslate(x, y) { x -= context.offset_x; y -= context.offset_y; } #define Clip(x, y, w, h) clip_area(&x, &y, &w, &h) #include void resetDraw(void) { struct environment *e; int i; for(e=environments, i=0; ilevel = i; env = environments; context.fixed_colours = 0; } static Any registerColour(Any *bin, Any c) { Any old = *bin; *bin = c; if ( isObject(old) && !isProtectedObj(old) ) { delRefObj(old); if ( isVirginObj(old) ) { freeObject(old); old = NIL; } } if ( isObject(c) && !isProtectedObj(c) ) addRefObj(c); return old; } static void d_push_context(void) { DContext ctx = alloc(sizeof(struct d_context)); if ( env->level > 0 ) { registerColour(&context.colour, context.gcs->colour); registerColour(&context.background, context.gcs->background); } *ctx = context; /* structure copy! */ context.parent = ctx; } static void d_pop_context() { #ifdef USE_XFT if ( context.xft_draw ) { if ( !context.parent || context.parent->xft_draw != context.xft_draw ) { XftDrawDestroy(context.xft_draw); context.xft_draw = NULL; } } #endif if ( context.parent != NULL ) { DContext ctx = context.parent; context = *ctx; /* structure copy! */ registerColour(&context.colour, NIL); registerColour(&context.background, NIL); unalloc(sizeof(struct d_context), ctx); } } void d_offset(int x, int y) { DEBUG(NAME_redraw, Cprintf("d_offset(%d, %d)\n", x, y)); context.offset_x = x; context.offset_y = y; } void r_offset(int x, int y) { context.offset_x += x; context.offset_y += y; } static void d_set_filloffset() { int tsx, tsy; tsx = context.fill_offset_x + context.offset_x; tsy = context.fill_offset_y + context.offset_y; XSetTSOrigin(context.display, context.gcs->fillGC, tsx, tsy); XSetTSOrigin(context.display, context.gcs->clearGC, tsx, tsy); XSetTSOrigin(context.display, context.gcs->workGC, tsx, tsy); DEBUG(NAME_fillOffset, Cprintf("set_filloffset() to %d, %d\n", tsx, tsy)); } void r_filloffset(Point offset, int x0, int y0, fill_state *state) { state->x = context.fill_offset_x; state->y = context.fill_offset_y; if ( notNil(offset) ) { context.fill_offset_x = valInt(offset->x) + x0; context.fill_offset_y = valInt(offset->y) + y0; d_set_filloffset(); } } void r_fillrestore(fill_state *state) { if ( state->x != context.fill_offset_x || state->y != context.fill_offset_y ) { context.fill_offset_x = state->x; context.fill_offset_y = state->y; d_set_filloffset(); } } DisplayObj d_display(DisplayObj d) { DisplayObj old = context.pceDisplay; if ( isDefault(d) ) d = CurrentDisplay(NIL); if ( context.pceDisplay != d ) { DisplayWsXref r; openDisplay(d); r = d->ws_ref; context.pceDisplay = d; context.display = r->display_xref; context.screen = r->screen; context.visual = r->visual; context.colormap = r->colour_map; context.depth = r->depth; context.gcs = r->pixmap_context; } quick = (d->quick_and_dirty == ON); return old; } void d_ensure_display() { if ( context.pceDisplay == NULL ) d_display(CurrentDisplay(NIL)); } void d_flush(void) { XFlush(context.display); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initialise a picture to redraw all graphicals in the area (x y w h) in the pictures coordinate system. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ status d_window(PceWindow sw, int x, int y, int w, int h, int clear, int limit) { DisplayObj d = getDisplayGraphical((Graphical)sw); if ( !d ) fail; DEBUG(NAME_redraw, Cprintf("d_window(%s, %d, %d, %d, %d) (on %s)\n", pp(sw), x, y, w, h, pp(d))); if ( env->level != 0 ) resetDraw(); /* security measure */ d_push_context(); d_display(d); context.origin_x = context.offset_x; context.origin_y = context.offset_y; context.drawable = (Drawable) XtWindow(widgetWindow(sw)); #ifdef USE_XFT context.xft_draw = NULL; #endif context.kind = NAME_window; env++; if ( limit ) { int dx, dy, dw, dh; compute_window(sw, &dx, &dy, &dw, &dh); env->area.x = dx; env->area.y = dy; env->area.w = dw; env->area.h = dh; } else { env->area.x = 0; env->area.y = 0; env->area.w = valInt(sw->area->w); env->area.h = valInt(sw->area->h); } DEBUG(NAME_redraw, Cprintf("area = (%d, %d, %d, %d) %s\n", env->area.x, env->area.y, env->area.w, env->area.h, clear ? "clear" : "no clear")); if ( limit && notNil(d->cache) && clear && sw->buffered_update != OFF ) { NormaliseArea(x, y, w, h); context.cache_x = x; context.cache_y = y; context.cache_w = w; context.cache_h = h; Translate(context.cache_x, context.cache_y); Clip(context.cache_x, context.cache_y, context.cache_w, context.cache_h); if ( context.cache_w <= valInt(d->cache->size->w) && context.cache_h <= valInt(d->cache->size->h) ) { context.offset_x -= context.cache_x; context.offset_y -= context.cache_y; env->area.x = env->area.y = 0; env->area.w = context.cache_w; env->area.h = context.cache_h; context.cache = d->cache; context.window = context.drawable; context.drawable = (Drawable) getXrefObject(context.cache, d); #ifdef USE_XFT context.xft_draw = NULL; #endif assign(context.cache, foreground, sw->colour); assign(context.cache, background, sw->background); context.kind = NAME_pixmap; } else { context.cache_x = context.cache_y = 0; context.cache = NULL; } } else { context.cache_x = context.cache_y = 0; context.cache = NULL; } d_set_filloffset(); d_clip(x, y, w, h); r_background(sw->background); context.default_background = sw->background; r_default_colour(sw->colour); if ( clear ) r_clear(x, y, w, h); succeed; } void d_image(Image i, int x, int y, int w, int h) { DisplayObj d = i->display; Pixmap image; DEBUG(NAME_redraw, Cprintf("d_image(%s, %d, %d, %d, %d)\n", pp(i), x, y, w, h)); d_push_context(); if ( isNil(d) ) { if ( isNil(d = context.pceDisplay) || d == NULL ) d = CurrentDisplay(i); } image = (Pixmap) getXrefObject(i, d); d_display(d); d_offset(0, 0); /* Do we need this for images? */ Translate(x, y); context.cache_x = 0; context.cache_y = 0; context.cache = NULL; context.origin_x = 0; context.origin_y = 0; if ( i->kind == NAME_bitmap ) { DisplayWsXref r = d->ws_ref; context.gcs = r->bitmap_context; context.depth = 1; } if ( isDefault(i->foreground) ) context.default_colour = d->foreground; else context.default_colour = i->foreground; if ( isDefault(i->background) ) context.default_background = d->background; else context.default_background = i->background; context.drawable = (Drawable) image; #ifdef USE_XFT context.xft_draw = NULL; #endif context.kind = i->kind; if ( i->kind == NAME_pixmap ) { r_background(context.default_background); r_default_colour(context.default_colour); } env++; env->area.x = 0; env->area.y = 0; env->area.w = valInt(i->size->w); env->area.h = valInt(i->size->h); XSetTSOrigin(context.display, context.gcs->fillGC, context.fill_offset_x, context.fill_offset_y); d_clip(x, y, w, h); } static void d_xwindow(DisplayObj d, Window win, int x, int y, int w, int h) { d_offset(0, 0); Translate(x, y); d_push_context(); d_display(d); context.cache_x = 0; context.cache_y = 0; context.origin_x = 0; context.origin_y = 0; context.drawable = (Drawable) win; #ifdef USE_XFT context.xft_draw = NULL; #endif context.default_background = d->background; context.kind = NAME_window; r_background(d->background); env++; env->area.x = 0; env->area.y = 0; env->area.w = w; env->area.h = h; d_clip(x, y, w, h); } void d_screen(DisplayObj d) { XWindowAttributes atts; int iw, ih; Window root; DisplayWsXref r = d->ws_ref; XGetWindowAttributes(r->display_xref, XtWindow(r->shell_xref), &atts); root = atts.root; XGetWindowAttributes(r->display_xref, root, &atts); iw = atts.width; ih = atts.height; DEBUG(NAME_draw, Cprintf("d_xwindow(%s, %ld, %dx%d\n", pp(d), root, iw, ih)); d_xwindow(d, root, 0, 0, iw, ih); } void d_frame(FrameObj fr, int x, int y, int w, int h) { Widget widget = widgetFrame(fr); if ( widget ) { d_xwindow(fr->display, XtWindow(widget), x, y, w, h); } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Actually set the clipping region. If we are using Xft, we need to use a Region (as far as I understand a monochrome pixmap). This seems expensive. I have the impression that eventually the clip-mask of a GC is a monochrome pixmap anyway. A simple test reveals no significant performance difference between the two approaches, so we'll use the Region based approach regardless of Xft for easier maintenance. Can we get better efficiency by keeping the region around? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static void do_clip(int x, int y, int w, int h) { #if 1 /* was USE_XFT */ XPoint pts[5]; Region r; pts[0].x = x; pts[0].y = y; pts[1].x = x; pts[1].y = y+h; pts[2].x = x+w; pts[2].y = y+h; pts[3].x = x+w; pts[3].y = y; pts[4].x = x; pts[4].y = y; r = XPolygonRegion(pts, 5, WindingRule); XSetRegion(context.display, context.gcs->workGC, r); XSetRegion(context.display, context.gcs->fillGC, r); XSetRegion(context.display, context.gcs->shadowGC, r); XSetRegion(context.display, context.gcs->reliefGC, r); #ifdef USE_XFT XftDrawSetClip(xftDraw(), r); #endif XDestroyRegion(r); #else /*USE_XFT*/ XRectangle rect; # define CLIP(x) XSetClipRectangles(context.display, x, 0, 0, &rect, \ 1, Unsorted) rect.x = x; rect.y = y; rect.width = w; rect.height = h; CLIP(context.gcs->workGC); CLIP(context.gcs->fillGC); CLIP(context.gcs->shadowGC); CLIP(context.gcs->reliefGC); #undef CLIP #endif /*USE_XFT*/ } void d_clip(int x, int y, int w, int h) { DEBUG(NAME_redraw, Cprintf("d_clip(%d, %d, %d, %d) -> ", x, y, w, h)); NormaliseArea(x, y, w, h); Translate(x, y); DEBUG(NAME_redraw, Cprintf("(%d %d %d %d) -> ", x, y, w, h)); Clip(x, y, w, h); DEBUG(NAME_redraw, Cprintf("(%d %d %d %d)\n", x, y, w, h)); env++; env->area.x = x; env->area.y = y; env->area.w = w; env->area.h = h; DEBUG(NAME_redraw, Cprintf("clip to %d %d %d %d\n", x, y, w, h)); do_clip(x, y, w, h); } void d_done() { if ( context.cache != NULL ) { DEBUG(NAME_redraw, Cprintf("writing cache to (%d %d %d %d)\n", context.cache_x, context.cache_y, context.cache_w, context.cache_h)); XCopyArea(context.display, context.drawable, context.window, context.gcs->copyGC, 0, 0, context.cache_w, context.cache_h, context.cache_x, context.cache_y); context.cache = NULL; } env--; d_clip_done(); if ( env->level > 0 ) { Any c; if ( (c=context.parent->colour) && notNil(c) ) r_colour(c); if ( (c=context.parent->background) && notNil(c) ) r_background(c); } d_pop_context(); DEBUG(NAME_redraw, Cprintf("After d_done(): env->level = %d\n", env->level)); } void d_clip_done(void) { env--; DEBUG(NAME_redraw, Cprintf("d_done()\n")); assert(env >= environments); /* stack underflow */ if ( env >= environments && env->level != 0 ) /* outermost: no use */ do_clip(env->area.x, env->area.y, env->area.w, env->area.h); } void intersection_iarea(IArea a, IArea b) { int x, y, w, h; x = (a->x > b->x ? a->x : b->x); y = (a->y > b->y ? a->y : b->y); w = (a->x + a->w < b->x + b->w ? a->x + a->w : b->x + b->w) - x; h = (a->y + a->h < b->y + b->h ? a->y + a->h : b->y + b->h) - y; if ( w < 0 ) w = 0; if ( h < 0 ) h = 0; a->x = x; a->y = y; a->w = w; a->h = h; } static void clip_area(int *x, int *y, int *w, int *h) { iarea a; a.x = *x; a.y = *y; a.w = *w; a.h = *h; intersection_iarea(&a, &env->area); *x = a.x; *y = a.y; *w = a.w; *h = a.h; } static int in_clip(int x, int y) { IArea a = &env->area; if ( x >= a->x && x < a->x + a->w && y >= a->y && y < a->h + a->h ) return TRUE; return FALSE; } /******************************** * AREA MANAGEMENT * ********************************/ void r_clear(int x, int y, int w, int h) { NormaliseArea(x, y, w, h); Translate(x, y); Clip(x, y, w, h); if ( w > 0 && h > 0 ) { DEBUG(NAME_background, Cprintf("r_clear(%d, %d, %d, %d) in %s context\n", x, y, w, h, pp(context.gcs->kind))); /* if ( context.kind == NAME_window ) XClearArea(context.display, context.drawable, x, y, w, h, False); else */ XFillRectangle(context.display, context.drawable, context.gcs->clearGC, x, y, w, h); } } void r_complement(int x, int y, int w, int h) { NormaliseArea(x, y, w, h); Translate(x, y); Clip(x, y, w, h); if ( w > 0 && h > 0 ) XFillRectangle(context.display, context.drawable, context.gcs->complementGC, x, y, w, h); } void r_and(int x, int y, int w, int h, Image pattern) { NormaliseArea(x, y, w, h); Translate(x, y); Clip(x, y, w, h); if ( w > 0 && h > 0 ) { r_andpattern(pattern); XFillRectangle(context.display, context.drawable, context.gcs->andGC, x, y, w, h); } } /******************************** * GRAPHICS STATE * ********************************/ void r_thickness(int pen) { if ( pen != context.gcs->pen ) { XGCValues values; /* pen == 0 draws faster! */ values.line_width = ((pen == 1) && quick ? 0 : pen); XChangeGC(context.display, context.gcs->workGC, GCLineWidth, &values); context.gcs->pen = pen; } } int r_transformed(int val) { return val; /* see msdraw.c */ } static struct dashpattern { Name dash; int line_style; char *dash_list; int dash_list_length; } dash_patterns[] = { { NAME_none, LineSolid, "", 0}, { NAME_dotted, LineOnOffDash, "\01\02", 2}, { NAME_dashed, LineOnOffDash, "\07\07", 2}, { NAME_dashdot, LineOnOffDash, "\07\03\01\07", 4}, { NAME_dashdotted, LineOnOffDash, "\011\03\01\03\01\03\01\03", 8}, { NAME_longdash, LineOnOffDash, "\015\07", 2}, { 0, 0, "", 0}, }; void r_dash(Name name) { if ( name != context.gcs->dash ) { struct dashpattern *dp = dash_patterns; for( ; dp->dash != 0; dp++ ) { if ( dp->dash == name ) { XGCValues values; values.line_style = dp->line_style; XChangeGC(context.display, context.gcs->workGC, GCLineStyle, &values); if ( dp->dash_list_length > 0 ) XSetDashes(context.display, context.gcs->workGC, 0, dp->dash_list, dp->dash_list_length); context.gcs->dash = name; return; } } errorPce(name, NAME_badTexture); } } void d_pen(Pen pen) { r_thickness(valInt(pen->thickness)); r_dash(pen->texture); if ( notDefault(pen->colour) ) r_colour(pen->colour); } void r_fillpattern(Any fill, Name which) /* image or colour */ { DEBUG(NAME_fillPattern, Cprintf("r_fillpattern(%s, %s) ", pp(fill), pp(which))); if ( isDefault(fill) ) fill = context.gcs->colour; else if ( fill == NAME_current ) return; if ( context.fixed_colours && !instanceOfObject(fill, ClassImage) ) fill = (which == NAME_foreground ? context.gcs->colour : context.gcs->background); if ( fill != context.gcs->fill ) { XGCValues values; unsigned long mask = 0L; DEBUG(NAME_fillPattern, Cprintf("Changing\n")); if ( instanceOfObject(fill, ClassImage) ) { Image i = fill; Pixmap pm; if ( context.kind == i->kind ) { pm = (Pixmap)getXrefObject(fill, context.pceDisplay); values.tile = pm; values.fill_style = FillTiled; mask = (GCTile|GCFillStyle); } else if ( context.kind != NAME_bitmap && i->kind == NAME_bitmap ) { /* bitmap pattern in colour context */ pm = (Pixmap)getXrefObject(fill, context.pceDisplay); values.stipple = pm; values.fill_style = FillOpaqueStippled; values.foreground = context.gcs->foreground_pixel; values.background = context.gcs->background_pixel; DEBUG(NAME_fillPattern, Cprintf("fg = %ld, bg = %ld\n", context.gcs->foreground_pixel, context.gcs->background_pixel)); mask = (GCStipple|GCFillStyle|GCForeground|GCBackground); } else /* colour pattern in bitmap context */ { Image mono; if ( !(mono = getAttributeObject(fill, NAME_monochrome)) ) { if ( (mono = get(fill, NAME_monochrome, EAV)) ) attributeObject(fill, NAME_monochrome, mono); } if ( mono && (pm = (Pixmap) getXrefObject(mono, context.pceDisplay)) ) { values.tile = pm; values.fill_style = FillTiled; mask = (GCTile|GCFillStyle); } } } else /* solid colour */ { mask = GCForeground|GCFillStyle; values.foreground = getPixelColour(fill, context.pceDisplay); values.fill_style = FillSolid; } if ( mask ) XChangeGC(context.display, context.gcs->fillGC, mask, &values); /* maintain a reference to */ delRefObj(context.gcs->fill); /* avoid drop-out */ freeableObj(context.gcs->fill); addRefObj(fill); context.gcs->fill = fill; } else { DEBUG(NAME_fillPattern, Cprintf("Not changed\n")); } } void r_arcmode(Name mode) { if ( mode != context.gcs->arcmode ) { XSetArcMode(context.display, context.gcs->fillGC, mode == NAME_chord ? ArcChord : ArcPieSlice); context.gcs->arcmode = mode; } } static void r_andpattern(Image i) { Pixmap image; if ( i != context.gcs->and_pattern && (image = (Pixmap) getXrefObject(i, context.pceDisplay)) != 0 ) { XGCValues values; unsigned long mask; if ( context.kind != NAME_bitmap && i->kind == NAME_bitmap ) { values.stipple = image; values.fill_style = FillOpaqueStippled; mask = (GCStipple|GCFillStyle); } else { values.tile = image; values.fill_style = FillTiled; mask = (GCTile|GCFillStyle); } XChangeGC(context.display, context.gcs->andGC, mask, &values); context.gcs->and_pattern = i; } } void r_fix_colours(Any fg, Any bg, ColourContext ctx) { ctx->foreground = context.gcs->colour; ctx->background = context.gcs->background; ctx->lock = context.fixed_colours; if ( !context.fixed_colours ) { if ( !fg || isNil(fg) ) fg = DEFAULT; if ( !bg || isNil(bg) ) bg = DEFAULT; r_default_colour(fg); r_background(bg); } context.fixed_colours++; } void r_unfix_colours(ColourContext ctx) { if ( (context.fixed_colours = ctx->lock) == 0 ) { r_default_colour(ctx->foreground); r_background(ctx->background); } } Any r_default_colour(Any c) { Any old = context.default_colour; if ( !context.fixed_colours ) { if ( notDefault(c) ) context.default_colour = c; r_colour(context.default_colour); } return old; } Any r_colour(Any c) { Colour old = context.gcs->colour; if ( context.fixed_colours ) return old; if ( isDefault(c) ) c = context.default_colour; if ( c != context.gcs->colour ) { if ( context.gcs->kind != NAME_bitmap ) { XGCValues values; unsigned long mask; if ( instanceOfObject(c, ClassColour) ) { unsigned long pixel = getPixelColour(c, context.pceDisplay); values.foreground = pixel; values.fill_style = FillSolid; mask = (GCForeground|GCFillStyle); context.gcs->foreground_pixel = pixel; } else { Pixmap pm = (Pixmap) getXrefObject(c, context.pceDisplay); values.tile = pm; values.fill_style = FillTiled; mask = (GCTile|GCFillStyle); } XChangeGC(context.display, context.gcs->workGC, mask, &values); if ( instanceOfObject(context.gcs->fill, ClassImage) && instanceOfObject(c, ClassColour)) XChangeGC(context.display, context.gcs->fillGC, GCForeground, &values); } old = registerColour(&context.gcs->colour, c); } return old; } Any r_background(Any c) { Any ob = context.gcs->background; if ( isDefault(c) || context.fixed_colours ) return ob; if ( c != context.gcs->background ) { if ( context.gcs->kind != NAME_bitmap ) { XGCValues values; unsigned long mask; DEBUG(NAME_background, Cprintf("Setting clearGC of %s context to %s\n", pp(context.gcs->kind), pp(c))); if ( instanceOfObject(c, ClassColour) ) { unsigned long pixel = getPixelColour(c, context.pceDisplay); values.foreground = pixel; values.fill_style = FillSolid; mask = (GCForeground|GCFillStyle); context.gcs->background_pixel = pixel; } else { Image i = (Image) c; Pixmap pm = (Pixmap) getXrefObject(i, context.pceDisplay); if ( i->kind == NAME_bitmap ) { DisplayWsXref r = context.pceDisplay->ws_ref; values.stipple = pm; values.fill_style = FillOpaqueStippled; values.foreground = r->black_pixel; values.background = r->white_pixel; mask = (GCStipple|GCFillStyle|GCForeground|GCBackground); } else { values.tile = pm; values.fill_style = FillTiled; mask = (GCTile|GCFillStyle); } } ob = registerColour(&context.gcs->background, c); context.gcs->elevation = NIL; /* force update */ XChangeGC(context.display, context.gcs->clearGC, mask, &values); } } return ob; } void r_swap_background_and_foreground() { if ( context.gcs->kind != NAME_bitmap ) { Colour tc = context.gcs->background; r_background(context.gcs->colour); r_colour(tc); } } BoolObj r_subwindow_mode(BoolObj val) { BoolObj old = context.gcs->subwindow_mode; if ( context.gcs->subwindow_mode != val ) { int mode = (val == ON ? IncludeInferiors : ClipByChildren); XSetSubwindowMode(context.display, context.gcs->workGC, mode); XSetSubwindowMode(context.display, context.gcs->fillGC, mode); XSetSubwindowMode(context.display, context.gcs->copyGC, mode); XSetSubwindowMode(context.display, context.gcs->bitmapGC, mode); XSetSubwindowMode(context.display, context.gcs->andGC, mode); XSetSubwindowMode(context.display, context.gcs->opGC, mode); context.gcs->subwindow_mode = val; } return old; } void r_invert_mode(BoolObj val) { if ( context.gcs->invert_mode != val ) { XGCValues values; int mask = GCFunction|GCPlaneMask; if ( val == ON ) { values.function = GXinvert; values.plane_mask = AllPlanes; } else { values.function = GXcopy; values.plane_mask = AllPlanes; } XChangeGC(context.display, context.gcs->workGC, mask, &values); XChangeGC(context.display, context.gcs->fillGC, mask, &values); XChangeGC(context.display, context.gcs->copyGC, mask, &values); XChangeGC(context.display, context.gcs->bitmapGC, mask, &values); XChangeGC(context.display, context.gcs->opGC, mask, &values); context.gcs->invert_mode = val; } } /******************************* * COORDINATES * *******************************/ void r_translate(int x, int y, int *ox, int *oy) { Translate(x, y); *ox = x; *oy = y; } /******************************** * DRAWING * ********************************/ void r_box(int x, int y, int w, int h, int r, Any fill) { int mwh, pen = context.gcs->pen; int drawpen; if ( context.gcs->dash == NAME_none && quick && pen > 0 ) drawpen = 1; else drawpen = pen; Translate(x, y); r_thickness(drawpen); NormaliseArea(x, y, w, h); mwh = (w < h ? w : h); if ( mwh == 0 ) return; DEBUG(NAME_redraw, Cprintf("r_box(%d, %d, %d, %d)\n", x, y, w, h)); if ( r > mwh / 2 - pen) r = mwh / 2 - pen; x += drawpen / 2; y += drawpen / 2; w -= drawpen; h -= drawpen; if ( drawpen <= 0 ) drawpen = 1; /* ensure termination */ if ( notNil(fill) ) r_fillpattern(fill, NAME_background); if ( r <= 0 ) { int n; if ( notNil(fill) ) XFillRectangle(context.display, context.drawable, context.gcs->fillGC, x, y, w, h); for( n = pen; n > 0; n -= drawpen ) { XDrawRectangle(context.display, context.drawable, context.gcs->workGC, x, y, w, h); x += drawpen; y += drawpen; w -= 2*drawpen; h -= 2*drawpen; } } else if ( r <= 4 ) { XSegment s[8]; /* little rounded corners */ int n; for( n = pen; n >= 0; n -= drawpen ) { s[0].x1 = x+r; s[0].y1 = y; s[0].x2 = x+w-r; s[0].y2 = y; s[1].x1 = x+w-r; s[1].y1 = y; s[1].x2 = x+w; s[1].y2 = y+r; s[2].x1 = x+w; s[2].y1 = y+r; s[2].x2 = x+w; s[2].y2 = y+h-r; s[3].x1 = x+w; s[3].y1 = y+h-r; s[3].x2 = x+w-r; s[3].y2 = y+h; s[4].x1 = x+w-r; s[4].y1 = y+h; s[4].x2 = x+r; s[4].y2 = y+h; s[5].x1 = x+r; s[5].y1 = y+h; s[5].x2 = x; s[5].y2 = y+h-r; s[6].x1 = x; s[6].y1 = y+h-r; s[6].x2 = x; s[6].y2 = y+r; s[7].x1 = x; s[7].y1 = y+r; s[7].x2 = x+r; s[7].y2 = y; if ( n == pen && notNil(fill) ) { XPoint p[8]; int n; for(n=0; n<8; n++) { p[n].x = s[n].x1; p[n].y = s[n].y1; } XFillPolygon(context.display, context.drawable, context.gcs->fillGC, p, 8, Convex, CoordModeOrigin); } if ( n > 0 ) XDrawSegments(context.display, context.drawable, context.gcs->workGC, s, 8); x += drawpen; y += drawpen; w -= 2*drawpen; h -= 2*drawpen; r -= drawpen; } } else { XSegment s[4]; /* rounded corners */ XArc a[4]; short wh; int n; for( n = pen; n >= 0; n -= drawpen ) { wh = r*2; s[0].x1 = x+r; s[0].y1 = y; s[0].x2 = x+w-r; s[0].y2 = y; s[1].x1 = x+w; s[1].y1 = y+r; s[1].x2 = x+w; s[1].y2 = y+h-r; s[2].x1 = x+r; s[2].y1 = y+h; s[2].x2 = x+w-r; s[2].y2 = y+h; s[3].x1 = x; s[3].y1 = y+r; s[3].x2 = x; s[3].y2 = y+h-r; a[0].x = x; a[0].y = y; a[0].width = a[0].height = wh; a[0].angle1 = 90*64; a[0].angle2 = 90*64; a[1].x = x+w-wh; a[1].y = y; a[1].width = a[1].height = wh; a[1].angle1 = 0*64; a[1].angle2 = 90*64; a[2].x = x+w-wh; a[2].y = y+h-wh; a[2].width = a[2].height = wh; a[2].angle1 = 270*64; a[2].angle2 = 90*64; a[3].x = x; a[3].y = y+h-wh; a[3].width = a[3].height = wh; a[3].angle1 = 180*64; a[3].angle2 = 90*64; r_arcmode(NAME_pieSlice); if ( n == pen && notNil(fill) ) { XFillArcs(context.display, context.drawable, context.gcs->fillGC, a, 4); #define FILL(x, y, w, h) \ XFillRectangle(context.display, context.drawable, \ context.gcs->fillGC, x, y, w, h) FILL(x, y+r, r, h-wh); FILL(x+w-r, y+r, r, h-wh); FILL(x+r, y, w-wh, h); #undef FILL } if ( n > 0 ) { XDrawSegments(context.display, context.drawable, context.gcs->workGC, s, 4); XDrawArcs(context.display, context.drawable, context.gcs->workGC, a, 4); } x += drawpen; y += drawpen; w -= 2*drawpen; h -= 2*drawpen; r -= drawpen; } } } void r_shadow_box(int x, int y, int w, int h, int r, int shadow, Image fill) { if ( !shadow ) { r_box(x, y, w, h, r, fill); return; } if ( shadow > h ) shadow = h; if ( shadow > w ) shadow = w; r_colour(BLACK_COLOUR); r_box(x+shadow, y+shadow, w-shadow, h-shadow, r, BLACK_IMAGE); r_colour(DEFAULT); r_box(x, y, w-shadow, h-shadow, r, isNil(fill) ? WHITE_IMAGE : fill); } #define MAX_SHADOW 10 Any r_elevation_shadow(Elevation e) { if ( isDefault(e->shadow) ) { Any bg = context.gcs->background; if ( instanceOfObject(bg, ClassColour) && context.gcs->depth != 1 ) return getReduceColour(bg, DEFAULT); else return BLACK_COLOUR; } else return e->shadow; } static Any r_elevation_relief(Elevation e) { if ( isDefault(e->relief) ) { Any bg = context.gcs->background; if ( instanceOfObject(bg, ClassColour) && context.gcs->depth != 1 ) return getHiliteColour(bg, DEFAULT); else return WHITE_COLOUR; } else return e->relief; } static void r_elevation(Elevation e) { if ( context.gcs->elevation != e ) { Any relief = r_elevation_relief(e); Any shadow = r_elevation_shadow(e); x11_set_gc_foreground(context.pceDisplay, relief, 1, &context.gcs->reliefGC); x11_set_gc_foreground(context.pceDisplay, shadow, 1, &context.gcs->shadowGC); context.gcs->elevation = e; } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r_elevation_fillpattern(Elevation e, int up) Sets the fill-pattern for the interior of elevated areas and returns TRUE if the interior needs to be filled. Returns FALSE otherwise. The special colours `reduced' and `hilited' are interpreted as relative colours to the background. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static int r_elevation_fillpattern(Elevation e, int up) { Any fill = NIL; if ( up && notDefault(e->colour) ) { fill = e->colour; } else if ( !up && notDefault(e->background) ) { fill = e->background; } if ( isNil(fill) ) fail; if ( fill == NAME_reduced || fill == NAME_hilited ) { Any bg = context.gcs->background; if ( instanceOfObject(bg, ClassColour) && context.gcs->depth != 1 ) { if ( fill == NAME_reduced ) fill = getReduceColour(bg, DEFAULT); else fill = getHiliteColour(bg, DEFAULT); } else fail; } r_fillpattern(fill, NAME_background); succeed; } void r_3d_segments(int n, ISegment s, Elevation e, int light) { XSegment *xs = (XSegment *)alloca(sizeof(XSegment) * n); XSegment *xp = xs; ISegment p = s; int i; r_elevation(e); for(i=0; ix1 = X(p->x1); xp->y1 = Y(p->y1); xp->x2 = X(p->x2); xp->y2 = Y(p->y2); } XDrawSegments(context.display, context.drawable, light ? context.gcs->reliefGC : context.gcs->shadowGC, xs, n); } void r_3d_box(int x, int y, int w, int h, int radius, Elevation e, int up) { int i; int pen = 1; int os; GC TopLeftGC, BottomRightGC; int xt, yt; int shadow = valInt(e->height); int fill; /* fill interior */ NormaliseArea(x, y, w, h); if ( radius > 0 ) { int maxr = min(w,h)/2; if ( radius > maxr ) radius = maxr; } if ( e->kind == NAME_shadow ) { XSegment s[2 * MAX_SHADOW]; int is = 0; /* # segments */ r_elevation(e); shadow = abs(shadow); shadow = min(shadow, min(w, h)); if ( shadow > MAX_SHADOW ) shadow = MAX_SHADOW; r_box(x, y, w-shadow, h-shadow, radius-shadow, e->colour); xt = x, yt = y; Translate(xt, yt); if ( radius > 0 ) { int r = min(radius, min(w, h)); XArc as[MAX_SHADOW * 3]; int ns = 0; w--, h--; for( os=0; os < shadow; os++ ) { int ar = r - shadow + os; int ang /*= 90/(os+1) */; s[is].x1 = xt+w-os; s[is].y1 = yt+r-shadow; /* vert */ s[is].x2 = xt+w-os; s[is].y2 = yt+h-r; is++; s[is].x1 = xt+r-shadow; s[is].y1 = yt+h-os; /* hor */ s[is].x2 = xt+w-r; s[is].y2 = yt+h-os; is++; /* bottom-right at xt+w-r, yt+h-r */ as[ns].x = xt+w-r-ar+1; as[ns].y = yt+h-r-ar+1; as[ns].width = as[ns].height = ar*2; as[ns].angle1 = 270*64; as[ns].angle2 = 90*64; ns++; /* top-right around xt+w-r, yt+r */ ang = 90; as[ns].x = xt+w-2*ar-os; as[ns].y = yt; as[ns].width = as[ns].height = ar*2; as[ns].angle1 = 0*64; as[ns].angle2 = ang*64; ns++; /* bottom-left around xt+r, yt+h-r */ as[ns].x = xt; as[ns].y = yt+h-2*ar-os; as[ns].width = as[ns].height = ar*2; as[ns].angle1 = (270-ang)*64; as[ns].angle2 = ang*64; ns++; } XDrawArcs(context.display, context.drawable, context.gcs->shadowGC, as, ns); } else { w -= shadow; h -= shadow; for( os=0; os < shadow; os++ ) { s[is].x1 = xt+w+os; s[is].y1 = yt+shadow; s[is].x2 = xt+w+os; s[is].y2 = yt+h+os; is++; s[is].x1 = xt+shadow; s[is].y1 = yt+h+os; s[is].x2 = xt+w+os; s[is].y2 = yt+h+os; is++; } } XDrawSegments(context.display, context.drawable, context.gcs->shadowGC, s, is); return; } if ( !up ) shadow = -shadow; fill = r_elevation_fillpattern(e, up); /* 3D box */ if ( shadow ) { r_elevation(e); if ( shadow > 0 ) { TopLeftGC = context.gcs->reliefGC; BottomRightGC = context.gcs->shadowGC; } else { TopLeftGC = context.gcs->shadowGC; BottomRightGC = context.gcs->reliefGC; shadow = -shadow; } if ( shadow > MAX_SHADOW ) shadow = MAX_SHADOW; xt = x, yt = y; Translate(xt, yt); if ( radius > 0 ) /* with rounded corners */ { XSegment sr[MAX_SHADOW * 2]; /* top, left */ XArc ar[MAX_SHADOW * 3]; /* idem */ XSegment ss[MAX_SHADOW * 2]; /* bottom, right */ XArc as[MAX_SHADOW * 3]; /* item */ int is=0, ir=0, ns=0, nr=0; /* # items */ int os; w--, h--; r_arcmode(NAME_pieSlice); if ( fill ) { GC FillGC = context.gcs->fillGC; int r2 = 2 * radius; int r = radius; XRectangle rs[3]; ar[0].angle1 = 90*64; ar[0].angle2 = 90*64; /* top-left */ ar[0].x = xt; ar[0].y = yt; ar[0].width = r2; ar[0].height = r2; ar[1].angle1 = 0*64; ar[1].angle2 = 90*64; /* top-right */ ar[1].x = xt+w-r2; ar[1].y = yt; ar[1].width = r2; ar[1].height = r2; ar[2].angle1 = 180*64; ar[2].angle2 = 90*64; /* bottom-left */ ar[2].x = xt; ar[2].y = yt+h-r2; ar[2].width = r2; ar[2].height = r2; ar[3].angle1 = 270*64; ar[3].angle2 = 90*64; /* bottom-left */ ar[3].x = xt+w-r2; ar[3].y = yt+h-r2; ar[3].width=r2; ar[3].height=r2; /*top*/ rs[0].x = xt+r; rs[0].y = yt; rs[0].width = w-r2; rs[0].height = r; /*bot*/ rs[1].x = xt+r; rs[1].y = yt+h-r; rs[1].width = w-r2; rs[1].height = r; /*body*/rs[2].x = xt; rs[2].y = yt+r; rs[2].width = w; rs[2].height = h-r2; XFillArcs(context.display, context.drawable, FillGC, ar, 4); XFillRectangles(context.display, context.drawable, FillGC, rs, 3); } for(os=0; osheight); Translate(x1, y1); Translate(x2, y2); r_elevation(e); if ( z < 0 ) { z = -z; up = !up; } if ( z > MAX_SHADOW ) z = MAX_SHADOW; if ( y1 == y2 ) { y1 -= z; y2 -= z; } else { x1 -= z; x2 -= z; } for(i=0; ireliefGC : context.gcs->shadowGC, s, i); for(i=0; ishadowGC : context.gcs->reliefGC, s, i); } static inline void step_to(int *x1, int *y1, int tx, int ty) { if ( tx > *x1 ) (*x1)++; else if ( tx < *x1 ) (*x1)--; if ( ty > *y1 ) (*y1)++; else if ( ty < *y1 ) (*y1)--; } void r_3d_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Elevation e, int up, int map) { XSegment s[3]; GC topGC, botGC; int z, n, i, p; int cx, cy; if ( !e || isNil(e) ) { r_triangle(x1, y1, x2, y2, x3, y3, up ? NIL : BLACK_COLOUR); return; } r_elevation(e); z = valInt(e->height); if ( !up ) z = -z; if ( z > 0 ) { topGC = context.gcs->reliefGC; botGC = context.gcs->shadowGC; p = z; } else { topGC = context.gcs->shadowGC; botGC = context.gcs->reliefGC; p = -z; } cx = (x1 + x2 + x3)/3; cy = (y1 + y2 + y3)/3; for( i = 0 ; p > 0; p--, i += 3 ) { int i0=0; int i1=i0+1; int i2=i0+2; s[i0].x1=X(x1); s[i0].y1=Y(y1); s[i0].x2=X(x2); s[i0].y2=Y(y2); s[i1].x1=s[i0].x2; s[i1].y1=s[i0].y2; s[i1].x2=X(x3); s[i1].y2=Y(y3); s[i2].x1=s[i1].x2; s[i2].y1=s[i1].y2; s[i2].x2=s[i0].x1; s[i2].y2=s[i0].y1; for(n=0; n<3;) { int f = n; int light = map & (1<height); int nox, noy, wex, wey, sox, soy, eax, eay; r_elevation(e); r_thickness(1); if ( !up ) z = -z; if ( z > 0 ) { topGC = context.gcs->reliefGC; botGC = context.gcs->shadowGC; } else { topGC = context.gcs->shadowGC; botGC = context.gcs->reliefGC; z = -z; } z = (z*3)/2; /* actually sqrt(2) */ DEBUG(NAME_3dDiamond, Cprintf("r_3d_diamond(%d, %d, %d, %d, %s, %d) -->\n\t", x, y, w, h, pp(e), up)); nox = X(x) + w/2; noy = Y(y); wex = X(x) + w; wey = Y(y) + h/2; sox = nox; soy = Y(y) + h; eax = X(x); eay = wey; DEBUG(NAME_3dDiamond, Cprintf("(%d, %d) (%d, %d) (%d, %d) (%d, %d)\n", nox, noy, wex, wey, sox, soy, eax, eay)); while( z > 0 ) { XSegment s[4]; s[0].x1 = eax; s[0].y1 = eay; s[0].x2 = nox; s[0].y2 = noy; s[1].x1 = nox; s[1].y1 = noy; s[1].x2 = wex; s[1].y2 = wey; s[2].x1 = wex; s[2].y1 = wey; s[2].x2 = sox; s[2].y2 = soy; s[3].x1 = sox; s[3].y1 = soy; s[3].x2 = eax; s[3].y2 = eay; XDrawSegments(context.display, context.drawable, topGC, s, 2); XDrawSegments(context.display, context.drawable, botGC, &s[2], 2); if ( --z > 0 ) { noy++; soy--; wex--; eax++; } else { eax++; /* ??? */ } } if ( r_elevation_fillpattern(e, up) ) { XPoint p[4]; p[0].x = wex; p[0].y = wey; p[1].x = nox; p[1].y = noy; p[2].x = eax; p[2].y = eay; p[3].x = sox; p[3].y = soy; XFillPolygon(context.display, context.drawable, context.gcs->fillGC, p, 4, Convex, CoordModeOrigin); } } void r_arc(int x, int y, int w, int h, int s, int e, Any fill) { int pen = context.gcs->pen; int drawpen; int oldpen = pen; int mwh2; Translate(x, y); NormaliseArea(x, y, w, h); mwh2 = min(w, h)/2; if ( pen > mwh2 ) { pen = mwh2; if ( pen == 0 ) return; } drawpen = ((context.gcs->dash == NAME_none) && quick ? 1 : pen); x += drawpen/2; y += drawpen/2; w -= drawpen; h -= drawpen; if ( notNil(fill) ) { r_fillpattern(fill, NAME_background); XFillArc(context.display, context.drawable, context.gcs->fillGC, x, y, w, h, s, e); } if ( fill != BLACK_IMAGE ) { int done; r_thickness(drawpen); for( done = 0; done < pen; done += drawpen ) { XDrawArc(context.display, context.drawable, context.gcs->workGC, x, y, w, h, s, e); x += drawpen; y += drawpen; w -= 2*drawpen; h -= 2*drawpen; } } if ( oldpen != drawpen ) r_thickness(oldpen); } void r_ellipse(int x, int y, int w, int h, Any fill) { r_arc(x, y, w, h, 0, 360*64, fill); } void r_3d_ellipse(int x, int y, int w, int h, Elevation z, int up) { int shadow; if ( !z || isNil(z) ) r_ellipse(x, y, w, h, NIL); shadow = valInt(z->height); if ( !up ) shadow = -shadow; if ( shadow > MAX_SHADOW ) shadow = MAX_SHADOW; if ( shadow ) { GC TopLeftGC, BottomRightGC; int xt=x, yt=y; XArc a[MAX_SHADOW*2]; int an, os; r_elevation(z); if ( shadow > 0 ) { TopLeftGC = context.gcs->reliefGC; BottomRightGC = context.gcs->shadowGC; } else { TopLeftGC = context.gcs->shadowGC; BottomRightGC = context.gcs->reliefGC; shadow = -shadow; } Translate(xt, yt); for(an=0, os=0; os=1 && h>=1; os++) { a[an].x = xt+os; a[an].y = yt+os; a[an].width = w-2*os; a[an].height = h-2*os; a[an].angle1 = 45*64; a[an].angle2 = 180*64; an++; } XDrawArcs(context.display, context.drawable, TopLeftGC, a, an); for(an=0, os=0; os=1 && h>=1; os++) { a[an].x = xt+os; a[an].y = yt+os; a[an].width = w-2*os; a[an].height = h-2*os; a[an].angle1 = 225*64; a[an].angle2 = 180*64; an++; } XDrawArcs(context.display, context.drawable, BottomRightGC, a, an); } if ( r_elevation_fillpattern(z, up) ) { r_thickness(0); r_arc(x+shadow, y+shadow, w-2*shadow, h-2*shadow, 0, 360*64, NAME_current); } } void r_line(int x1, int y1, int x2, int y2) { Translate(x1, y1); Translate(x2, y2); { int p = context.gcs->pen; /* check whether the line is */ int x = x1; /* in the painted area */ int y = y1; int w = x2-x1; int h = y2-y1; NormaliseArea(x, y, w, h); x -= p; y -= p; p *= 2; w += p; h += p; Clip(x, y, w, h); if ( w == 0 || h == 0 ) return; } XDrawLine(context.display, context.drawable, context.gcs->workGC, x1, y1, x2, y2); } void r_polygon(IPoint pts, int n, int close) { if ( context.gcs->pen > 0 ) { XPoint *points = (XPoint *)alloca(n * sizeof(XPoint)); int i; for(i=0; iworkGC, points, i, CoordModeOrigin); } } void r_path(Chain points, int ox, int oy, int radius, int closed, Image fill) { Cell cell; int npoints = valInt(getSizeChain(points)); if ( npoints < 2 ) return; if ( radius == 0 ) { XPoint *pts = (XPoint *)alloca((npoints+1) * sizeof(XPoint)); IArea a = &env->area; int i=0; int linesonly = (isNil(fill) && context.gcs->pen); int x0=0, y0=0; /* keep compiler happy */ for_cell(cell, points) { Point p = cell->value; int x, y; x = X(valInt(p->x) + ox); y = Y(valInt(p->y) + oy); if ( linesonly && i > 0 ) { if ( (x < a->x && x0 < a->x) || (x > a->x + a->w && x0 > a->x + a->w) || (y < a->y && y0 < a->y) || (y > a->y + a->h && y0 > a->y + a->h) ) { /*Cprintf("Skipping %d,%d --> %d,%d\n", x0, y0, x, y);*/ if ( i > 1 ) { /*Cprintf("Drawing %d lines\n", i-1);*/ XDrawLines(context.display, context.drawable, context.gcs->workGC, pts, i, CoordModeOrigin); } i = 0; /* restart */ } } x0 = x; y0 = y; pts[i].x = x; /* TBD: XPoint is 16-bit! */ pts[i].y = y; i++; } if ( closed || notNil(fill) ) { Point p = (Point) points->head->value; pts[i].x = X(valInt(p->x) + ox); pts[i].y = Y(valInt(p->y) + oy); i++; } if ( notNil(fill) ) { r_fillpattern(fill, NAME_background); XFillPolygon(context.display, context.drawable, context.gcs->fillGC, pts, i, Complex, CoordModeOrigin); } if ( context.gcs->pen ) { /*Cprintf("Finally drawing %d lines\n", i-1);*/ XDrawLines(context.display, context.drawable, context.gcs->workGC, pts, i, CoordModeOrigin); } } else { #if 0 /* TBD */ XSegment *sgs = (XSegment *) alloca((npoints-1) * sizeof(XSegment)); int pt = 0; int seg = 0; int x1, x2, x3; int y1, y2, y3; for_cell(cell, points) { Point p = cell->value; int x = X(valInt(p->x)); int y = Y(valInt(p->y)); if ( pt == 0 ) { x1 = x; y1 = y; } else if ( pt == 1 ) { x2 = x1; y2 = y1; x1 = x; y1 = y; } else { x3 = x2; y3 = y2; x2 = x1; y2 = y1; x1 = x; y1 = y; sgs[seg].x1 = x3; sgs[seg].y1 = y3; /* to be continued */ } pt++; } #endif Cprintf("Not yet implemented (r_path())\n"); } } void r_op_image(Image image, int sx, int sy, int x, int y, int w, int h, Name op) { NormaliseArea(x, y, w, h); Translate(x, y); if ( env->area.x > x ) sx += env->area.x - x; if ( env->area.y > y ) sy += env->area.y - y; Clip(x, y, w, h); if ( w > 0 && h > 0 ) { GC opgc; Pixmap pix = (Pixmap) getXrefObject(image, context.pceDisplay); if ( equalName(op, NAME_copy) ) opgc = context.gcs->copyGC; else { opgc = context.gcs->opGC; if ( equalName(op, NAME_or) ) { DisplayWsXref r = context.pceDisplay->ws_ref; if ( context.kind != NAME_bitmap && r->black_pixel == 0 ) XSetFunction(context.display, opgc, GXand); else XSetFunction(context.display, opgc, GXor); } else if ( equalName(op, NAME_and) ) opgc = context.gcs->andGC; else /*if ( equalName(op, NAME_xor) )*/ XSetFunction(context.display, opgc, GXxor); } XCopyArea(context.display, pix, context.drawable, opgc, sx, sy, w, h, x, y); } } void r_image(Image image, int sx, int sy, int x, int y, int w, int h, BoolObj transparent) { XGCValues values; if ( image->size->w == ZERO || image->size->h == ZERO ) return; DEBUG(NAME_image, Cprintf("image <-kind %s on drawable kind %s\n", pp(image->kind), pp(context.kind))); if ( (image->kind == NAME_bitmap && context.kind != NAME_bitmap) || (image->kind != NAME_bitmap && context.kind == NAME_bitmap) ) { NormaliseArea(x, y, w, h); Translate(x, y); if ( image->kind != NAME_bitmap ) { if ( env->area.x > x ) sx += env->area.x - x; if ( env->area.y > y ) sy += env->area.y - y; } values.ts_x_origin = x-sx; values.ts_y_origin = y-sy; if ( notNil(image->mask) ) { values.clip_mask = (Pixmap)getXrefObject(image->mask, context.pceDisplay); values.clip_x_origin = x; values.clip_y_origin = y; } else values.clip_mask = None; Clip(x, y, w, h); if ( w > 0 && h > 0 ) { Pixmap pix = (Pixmap) getXrefObject(image, context.pceDisplay); if ( image->kind == NAME_bitmap ) /* bitmap on pixmap */ { if ( instanceOfObject(context.gcs->colour, ClassColour) ) { values.foreground = context.gcs->foreground_pixel; values.background = context.gcs->background_pixel; values.stipple = pix; values.fill_style = (transparent == ON ? FillStippled : FillOpaqueStippled); XChangeGC(context.display, context.gcs->bitmapGC, GCClipXOrigin|GCClipYOrigin|GCClipMask| GCForeground|GCBackground|GCFillStyle| GCStipple|GCTileStipXOrigin|GCTileStipYOrigin, &values); XFillRectangle(context.display, context.drawable, context.gcs->bitmapGC, x, y, w, h); } else /* bitmap on pixmap, pixmap colour */ { DisplayWsXref r = context.pceDisplay->ws_ref; Pixmap tmp = XCreatePixmap(context.display, XtWindow(r->shell_xref), w, h, context.depth); GC GCtmp; if ( !tmp ) return; r_fillpattern(context.gcs->colour, NAME_foreground); XFillRectangle(context.display, tmp, context.gcs->fillGC, 0, 0, w, h); values.clip_x_origin = values.ts_x_origin; values.clip_y_origin = values.ts_y_origin; values.clip_mask = pix; GCtmp = XCreateGC(context.display, context.drawable, GCClipXOrigin|GCClipYOrigin|GCClipMask| GCClipXOrigin|GCClipYOrigin|GCClipMask, &values); XCopyArea(context.display, tmp, context.drawable, GCtmp, 0, 0, w, h, x, y); XFreePixmap(context.display, tmp); XFreeGC(context.display, GCtmp); } } else /* pixmap on bitmap */ { unsigned long fpixel, bpixel; unsigned long plane = 1L; unsigned int i; if ( isDefault(image->foreground) ) assign(image, foreground, context.pceDisplay->foreground); if ( isDefault(image->background) ) assign(image, background, context.pceDisplay->background); fpixel = getPixelColour(image->foreground, context.pceDisplay); bpixel = getPixelColour(image->background, context.pceDisplay); if ( fpixel != bpixel ) { for(i=1; i++ <= (sizeof(unsigned long) * 8); plane <<= 1) { if ( (fpixel & plane) != (bpixel & plane) ) break; } } DEBUG(NAME_image, Cprintf("fpixel = %ld, bpixel = %ld, plane = %ld\n", fpixel, bpixel, plane)); if ( (fpixel & plane) == 0 ) { values.foreground = 0; values.background = 1; XChangeGC(context.display, context.gcs->copyGC, GCClipXOrigin|GCClipYOrigin|GCClipMask| GCForeground|GCBackground, &values); } XCopyPlane(context.display, (Pixmap) getXrefObject(image, context.pceDisplay), context.drawable, context.gcs->copyGC, sx, sy, w, h, x, y, plane); if ( (fpixel & plane) == 0 ) { values.foreground = 1; values.background = 0; values.clip_mask = None; XChangeGC(context.display, context.gcs->copyGC, GCClipMask|GCForeground|GCBackground, &values); } } } } else if ( transparent == ON && image->kind == NAME_bitmap ) { r_op_image(image, sx, sy, x, y, w, h, NAME_or); } else { Pixmap pm = (Pixmap)getXrefObject(image, context.pceDisplay); if ( notNil(image->mask) ) { values.clip_mask = (Pixmap)getXrefObject(image->mask, context.pceDisplay); values.clip_x_origin = X(x); values.clip_y_origin = Y(y); XChangeGC(context.display, context.gcs->copyGC, GCClipXOrigin|GCClipYOrigin|GCClipMask, &values); } NormaliseArea(x, y, w, h); Translate(x, y); if ( env->area.x > x ) sx += env->area.x - x; if ( env->area.y > y ) sy += env->area.y - y; Clip(x, y, w, h); if ( w > 0 && h > 0 ) XCopyArea(context.display, pm, context.drawable, context.gcs->copyGC, sx, sy, w, h, x, y); if ( notNil(image->mask) ) { values.clip_mask = None; values.clip_x_origin = 0; values.clip_y_origin = 0; XChangeGC(context.display, context.gcs->copyGC, GCClipXOrigin|GCClipYOrigin|GCClipMask, &values); } } } void r_fill(int x, int y, int w, int h, Any pattern) { Translate(x, y); Clip(x, y, w, h); if ( w > 0 && h > 0 ) { r_fillpattern(pattern, NAME_foreground); XFillRectangle(context.display, context.drawable, context.gcs->fillGC, x, y, w, h); } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Note: this function only fills polygons up to 20 points that are convex. This is all we need sofar (cursors and arrow heads). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ void r_fill_polygon(IPoint pts, int n) { XPoint *points = (XPoint *) alloca(n * sizeof(XPoint)); int i; for(i=0; ifillGC, points, n, Convex, CoordModeOrigin); } void r_caret(int cx, int cy, FontObj font) { int ch, cb, ah, cw2; int cw = valInt(getExFont(font)); ipoint pts[3]; if ( cw < 4 ) cw = 4; else if ( cw > 10 ) cw = 10; ch = valInt(getHeightFont(font)); cw2 = cw/2; cb = cy + ch-1; ah = (ch+2)/3; r_thickness(1); r_dash(NAME_none); r_line(cx, cb-2, cx, cb-ch); pts[0].x = cx - cw2; pts[0].y = cb; pts[1].x = cx + cw2; pts[1].y = cb; pts[2].x = cx; pts[2].y = cb-ah; r_fillpattern(BLACK_IMAGE, NAME_foreground); r_fill_polygon(pts, 3); } void r_fill_triangle(int x1, int y1, int x2, int y2, int x3, int y3) { XPoint p[3]; p[0].x = X(x1); p[0].y = Y(y1); p[1].x = X(x2); p[1].y = Y(y2); p[2].x = X(x3); p[2].y = Y(y3); XFillPolygon(context.display, context.drawable, context.gcs->fillGC, p, 3, Convex, CoordModeOrigin); } void r_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Any fill) { XSegment s[3]; if ( notNil(fill) ) { r_fillpattern(fill, NAME_foreground); r_fill_triangle(x1, y1, x2, y2, x3, y3); } s[0].x1 = X(x1); s[0].y1 = Y(y1); s[0].x2 = X(x2); s[0].y2 = Y(y2); s[1].x1 = s[0].x2; s[1].y1 = s[0].y2; s[1].x2 = X(x3); s[1].y2 = Y(y3); s[2].x1 = s[1].x2; s[2].y1 = s[1].y2; s[2].x2 = s[0].x1; s[2].y2 = s[0].y1; XDrawSegments(context.display, context.drawable, context.gcs->workGC, s, 3); } void r_pixel(int x, int y, Any val) { Translate(x, y); if ( in_clip(x, y) ) { if ( context.kind == NAME_bitmap ) { if ( isOn(val) ) XDrawPoint(context.display, context.drawable, context.gcs->workGC, x, y); else XDrawPoint(context.display, context.drawable, context.gcs->clearGC, x, y); } else { r_colour(val); XDrawPoint(context.display, context.drawable, context.gcs->workGC, x, y); } } } void r_complement_pixel(int x, int y) { Translate(x, y); if ( in_clip(x, y) ) XDrawPoint(context.display, context.drawable, context.gcs->complementGC, x, y); } /******************************** * READ DRAWABLES * ********************************/ static Drawable last_drawable = 0; void d_modify() { if ( last_drawable == context.drawable ) last_drawable = 0; /* discard the cache */ } int r_get_mono_pixel(int x, int y) { return r_get_pixel(x, y) == context.gcs->background_pixel ? FALSE : TRUE; } unsigned long r_get_pixel(int x, int y) { static Display *last_display = NULL; static XImage *image = NULL; static int ix, iy, iw, ih; /* Image coordinates */ static int dw, dh; /* Preferred width/height */ int move = FALSE; Translate(x, y); if ( in_clip(x, y) ) { if ( last_drawable != context.drawable || last_display != context.display ) { last_drawable = context.drawable; last_display = context.display; dw = dh = 8; ix = iy = iw = ih = 0; move = TRUE; } if ( x < ix ) { move = TRUE; dw *= 2; ix = x - dw - 1; } if ( x >= ix+iw ) { move = TRUE; dw *= 2; ix = x; } if ( y < iy ) { move = TRUE; dh *= 2; iy = y - dh - 1; } if ( y >= iy+ih ) { move = TRUE; dh *= 2; iy = y; } if ( move ) { if ( image != NULL ) XDestroyImage(image); iw = dw; ih = dh; Clip(ix, iy, iw, ih); image = XGetImage(last_display, last_drawable, ix, iy, iw, ih, AllPlanes, ZPixmap); } return XGetPixel(image, x-ix, y-iy); } return NoPixel; } /******************************** * TEXT MANIPULATION * ********************************/ #ifdef USE_XFT /******************************* * XFT VERSION * *******************************/ static XftDraw * xftDraw() { if ( !context.xft_draw ) { if ( context.depth == 1 ) { context.xft_draw = XftDrawCreateBitmap(context.display, (Pixmap)context.drawable); } else { context.xft_draw = XftDrawCreate(context.display, context.drawable, context.visual, context.colormap); } } return context.xft_draw; } static void s_font(FontObj f) { if ( f ) { d_ensure_display(); if ( context.gcs->font != f ) { XpceFontInfo info; context.gcs->font = f; info = (XpceFontInfo) getXrefObject(f, context.pceDisplay); context.gcs->xft_font = info->xft_font; } } } int s_has_char(FontObj f, unsigned int c) { s_font(f); return XftCharExists(context.display, context.gcs->xft_font, c); } void f_domain(FontObj f, Name which, int *x, int *y) { *x = 0; *y = 0xffff; } int s_default_char(FontObj font) { return 'X'; /* for now */ } int s_ascent(FontObj font) { s_font(font); return context.gcs->xft_font->ascent; } int s_descent(FontObj font) { s_font(font); return context.gcs->xft_font->descent; } int c_width(wint_t c, FontObj font) { FcChar32 s[1]; XGlyphInfo info; s_font(font); s[0] = c; XftTextExtents32(context.display, context.gcs->xft_font, s, 1, &info); return info.xOff; } static int s_advance(PceString s, int from, int to) { int len = to-from; XGlyphInfo info; if ( len <= 0 ) return 0; if ( isstrA(s) ) { XftTextExtents8(context.display, context.gcs->xft_font, s->s_textA+from, len, &info); } else if ( sizeof(charW) == 2 ) { XftTextExtents16(context.display, context.gcs->xft_font, (FcChar16*)s->s_textW+from, len, &info); } else if ( sizeof(charW) == 4 ) { XftTextExtents32(context.display, context.gcs->xft_font, (FcChar32*)s->s_textW+from, len, &info); } return info.xOff; /* Xoff? */ } static int lbearing(wint_t c) { XGlyphInfo info; FcChar32 s[1]; s[0] = c; XftTextExtents32(context.display, context.gcs->xft_font, s, 1, &info); return info.x; } static void xft_color(XftColor *c) { if ( instanceOfObject(context.gcs->colour, ClassColour) ) { Colour cobj = context.gcs->colour; c->pixel = getPixelColour(cobj, context.pceDisplay); c->color.red = valInt(cobj->red); c->color.green = valInt(cobj->green); c->color.blue = valInt(cobj->blue); } else /* Pixmap colour. Use black */ { c->color.red = 0; c->color.green = 0; c->color.blue = 0; } c->color.alpha = 0xffff; } void s_printA(charA *s, int l, int x, int y, FontObj f) { if ( l > 0 ) { XftColor color; xft_color(&color); Translate(x, y); s_font(f); XftDrawString8(xftDraw(), &color, context.gcs->xft_font, x, y, s, l); } } void s_printW(charW *s, int l, int x, int y, FontObj f) { if ( l > 0 ) { XftColor color; xft_color(&color); Translate(x, y); s_font(f); if ( sizeof(charW) == 2 ) { XftDrawString16(xftDraw(), &color, context.gcs->xft_font, x, y, (FcChar16*)s, l); } else if ( sizeof(charW) == 4 ) { XftDrawString32(xftDraw(), &color, context.gcs->xft_font, x, y, (FcChar32*)s, l); } else { assert(0); } } } #else /*USE_XFT*/ /******************************* * Xwc* Function version * *******************************/ #ifdef USE_XFONTSET static void s_font(FontObj f) { if ( f ) { d_ensure_display(); if ( context.gcs->font != f ) { XpceFontInfo info; context.gcs->font = f; info = (XpceFontInfo) getXrefObject(f, context.pceDisplay); context.gcs->font_set = info->font_set; } } } int s_has_char(FontObj f, unsigned int c) { return TRUE; } void f_domain(FontObj f, Name which, int *x, int *y) { *x = 0; *y = 0xffff; } int s_default_char(FontObj font) { return 'X'; /* for now */ } int s_ascent(FontObj font) { XFontSetExtents *exts; s_font(font); exts = XExtentsOfFontSet(context.gcs->font_set); return -exts->max_logical_extent.y; } int s_descent(FontObj font) { XFontSetExtents *exts; s_font(font); exts = XExtentsOfFontSet(context.gcs->font_set); return exts->max_logical_extent.y + exts->max_logical_extent.height; } int c_width(wint_t c, FontObj font) { wchar_t s[1]; s_font(font); s[0] = c; return XwcTextEscapement(context.gcs->font_set, s, 1); } static int s_advance(PceString s, int from, int to) { int len = to-from; if ( len <= 0 ) return 0; if ( isstrA(s) ) { wchar_t *ws = alloca(len*sizeof(wchar_t)); wchar_t *t = ws; const charA *f = &s->s_textA[from]; const charA *e = &f[len]; while(ffont_set, ws, len); } else { return XwcTextEscapement(context.gcs->font_set, s->s_textW+from, len); } } static int lbearing(wint_t c) { XRectangle ink; wchar_t s[1]; s[0] = c; XwcTextExtents(context.gcs->font_set, s, 1, &ink, NULL); return -ink.x; } void s_printA(charA *s, int l, int x, int y, FontObj f) { if ( l > 0 ) { wchar_t *ws = alloca(l*sizeof(wchar_t)); wchar_t *t = ws; const charA *e = &s[l]; while(sfont_set, context.gcs->workGC, x, y, ws, l); } } void s_printW(charW *s, int l, int x, int y, FontObj f) { if ( l > 0 ) { Translate(x, y); s_font(f); XwcDrawString(context.display, context.drawable, context.gcs->font_set, context.gcs->workGC, x, y, s, l); } } #else /*USE_XFONTSET*/ #define MAX_CHAR 256 static void s_font(FontObj f); static XCharStruct * s_char_struct(XFontStruct *info, int c) { if ( info->per_char == NULL ) return &info->max_bounds; else if ( info->min_byte1 == 0 && info->max_byte1 == 0 ) /* 8-bit */ { unsigned int b2 = (unsigned)c % 256; if ( b2 < info->min_char_or_byte2 || b2 > info->max_char_or_byte2 ) return NULL; return &info->per_char[b2 - info->min_char_or_byte2]; } else /* 16-bit font */ { unsigned int b2 = c % 256; unsigned int b1 = c / 256; if ( b2 < info->min_char_or_byte2 || b2 > info->max_char_or_byte2 || b1 < info->min_byte1 || b1 > info->max_byte1 ) return NULL; else { int cols = info->max_char_or_byte2 - info->min_char_or_byte2 + 1; return &info->per_char[((b1-info->min_byte1)*cols) + b2 - info->min_char_or_byte2]; } } } int s_has_char(FontObj f, unsigned int c) { XFontStruct *info; XCharStruct *def; s_font(f); info = context.gcs->font_info; if ( (def = s_char_struct(info, c)) && def->width != 0 ) succeed; fail; } void f_domain(FontObj f, Name which, int *x, int *y) { XFontStruct *info; s_font(f); info = context.gcs->font_info; if ( which == NAME_x ) { *x = info->min_char_or_byte2; *y = info->max_char_or_byte2; } else { *x = info->min_byte1; *y = info->max_byte1; } } int s_default_char(FontObj font) { s_font(font); return context.gcs->font_info->default_char; } static void s_font(FontObj f) { if ( f ) { d_ensure_display(); if ( context.gcs->font != f ) { XpceFontInfo info; context.gcs->font = f; info = (XpceFontInfo) getXrefObject(f, context.pceDisplay); context.gcs->font_info = info->info; context.gcs->char_widths = info->widths; context.gcs->maxchar = info->maxchar; XSetFont(context.display, context.gcs->workGC, context.gcs->font_info->fid); } } } int s_ascent(FontObj f) { s_font(f); return context.gcs->font_info->ascent; } int s_descent(FontObj f) { s_font(f); return context.gcs->font_info->descent; } static int lbearing(wint_t c) { XFontStruct *info = context.gcs->font_info; XCharStruct *def = s_char_struct(info, c); return def ? -def->lbearing : 0; } int c_width(wint_t c, FontObj font) { s_font(font); if ( context.gcs->char_widths ) return context.gcs->char_widths[c]; return context.gcs->font_info->max_bounds.width; } static int s_advance(PceString s, int from, int to) { if ( !context.gcs->char_widths ) { return context.gcs->font_info->max_bounds.width * (to-from); } else { cwidth *widths = context.gcs->char_widths; int width = 0; int n = to-from; if ( from >= to ) return 0; if ( isstrA(s) ) { charA *q = &s->s_textA[from]; for(; n-- > 0; q++) width += widths[*q]; } else { charW *q = &s->s_textW[from]; wint_t maxchr = context.gcs->maxchar; for(; n-- > 0; q++) { wint_t c = *q; if ( c > maxchr ) c = ' '; width += widths[c]; } } return width; } } void s_printA(charA *s, int l, int x, int y, FontObj f) { if ( l > 0 ) { Translate(x, y); s_font(f); XDrawString(context.display, context.drawable, context.gcs->workGC, x, y, s, l); } } void s_printW(charW *s, int l, int x, int y, FontObj f) { if ( l > 0 ) { XChar2b *ws = alloca(l*sizeof(XChar2b)); int i; Translate(x, y); s_font(f); for(i=0; i>8) & 0xff; ws[i].byte2 = s[i] & 0xff; } XDrawString16(context.display, context.drawable, context.gcs->workGC, x, y, ws, l); } } #endif /*USE_XFONTSET*/ #endif /*USE_XFT*/ int s_height(FontObj f) { return s_ascent(f) + s_descent(f); } int str_width(PceString s, int from, int to, FontObj f) { s_font(f); if ( from < 0 ) from = 0; if ( from >= s->s_size || to <= from ) return 0; if ( to > s->s_size ) to = s->s_size; if ( to > from ) { int w = lbearing(str_fetch(s, from)); w += s_advance(s, from, to); return w; } return 0; } int str_advance(PceString s, int from, int to, FontObj font) { s_font(font); return s_advance(s, from, to); } void s_print(PceString s, int x, int y, FontObj f) { if ( isstrA(s) ) s_printA(s->s_textA, s->s_size, x, y, f); else s_printW(s->s_textW, s->s_size, x, y, f); } void s_print_aligned(PceString s, int x, int y, FontObj f) { if ( s->s_size > 0 ) { s_font(f); x += lbearing(str_fetch(s, 0)); s_print(s, x, y, f); } } static void str_draw_text(PceString s, int offset, int len, int x, int y) { if ( offset >= s->s_size ) return; if ( offset < 0 ) { len += offset; offset = 0; } if ( offset + len > s->s_size ) len = s->s_size - offset; if ( s->s_size > 0 ) { InvTranslate(x, y); /* Hack */ if ( isstrA(s) ) { s_printA(s->s_textA+offset, len, x, y, NULL); } else { s_printW(s->s_textW+offset, len, x, y, NULL); } } } static void str_stext(PceString s, int f, int len, int x, int y, Style style) { if ( len > 0 ) { Any ofg = NULL; int w = 0; /* make compiler happy */ if ( notNil(style) ) { w = s_advance(s, f, f+len); if ( notDefault(style->background) ) { int a = s_ascent(NULL); int b = s_descent(NULL); r_fillpattern(style->background, NAME_foreground); XFillRectangle(context.display, context.drawable, context.gcs->fillGC, x, y-a, w, b+a); } if ( notDefault(style->colour) ) ofg = r_colour(style->colour); } str_draw_text(s, f, len, x, y); if ( notNil(style) && style->attributes & TXT_UNDERLINED ) XDrawLine(context.display, context.drawable, context.gcs->workGC, x, y, x+w, y); if ( ofg ) r_colour(ofg); } } static void str_text(PceString s, int x, int y) { if ( s->s_size > 0 ) { x += lbearing(str_fetch(s, 0)); str_draw_text(s, 0, s->s_size, x, y); } } /******************************** * MULTILINE TEXT * ********************************/ #define MAX_TEXT_LINES 200 /* lines in a text object */ typedef struct { short x; /* origin x offset */ short y; /* origin y offset */ short width; /* pixel width of line */ short height; /* pixel height of line */ string text; /* text of the line */ } strTextLine; static void str_break_into_lines(PceString s, strTextLine *line, int *nlines, int maxlines) { int here = 0; int size = s->s_size; int nls = 0; *nlines = 0; if ( size == 0 ) /* totally empty: report one line */ { str_cphdr(&line->text, s); line->text.s_text = s->s_text; line->text.s_size = 0; *nlines = 1; return; } for( ; here < size && nls < maxlines; line++, nls++ ) { int el; str_cphdr(&line->text, s); line->text.s_text = str_textp(s, here); if ( (el = str_next_index(s, here, '\n')) >= 0 ) { line->text.s_size = el - here; here = el + 1; if ( here == size ) /* last char is newline: add a line */ { line++, nls++; str_cphdr(&line->text, s); line->text.s_text = str_textp(s, here); line->text.s_size = 0; } } else { line->text.s_size = size - here; here = size; } } *nlines = nls; } static void str_compute_lines(strTextLine *lines, int nlines, FontObj font, int x, int y, int w, int h, Name hadjust, Name vadjust) { int cy; int th = s_height(font); strTextLine *line; int n; if ( vadjust == NAME_top ) cy = y; else if ( vadjust == NAME_center ) cy = y + (1 + h - nlines*th)/2; else /*if ( vadjust == NAME_bottom )*/ cy = y + h - nlines*th; for( n = 0, line = lines; n++ < nlines; line++, cy += th ) { line->y = cy; line->height = th; line->width = str_width(&line->text, 0, line->text.s_size, font); if ( hadjust == NAME_left ) line->x = x; else if ( hadjust == NAME_center ) line->x = x + (w - line->width)/2; else /*if ( hadjust == NAME_right )*/ line->x = x + w - line->width; } } void str_size(PceString s, FontObj font, int *width, int *height) { strTextLine lines[MAX_TEXT_LINES]; strTextLine *line; int nlines, n; int w = 0; s_font(font); str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES); for(n = 0, line = lines; n++ < nlines; line++) { if ( line->text.s_size > 0 ) { int lw; lw = lbearing(str_fetch(&line->text, 0)); lw += s_advance(&line->text, 0, line->text.s_size); if ( w < lw ) w = lw; } } *width = w; *height = nlines * s_height(font); } void str_string(PceString s, FontObj font, int x, int y, int w, int h, Name hadjust, Name vadjust, int flags) { strTextLine lines[MAX_TEXT_LINES]; strTextLine *line; int nlines, n; int baseline; if ( s->s_size == 0 ) return; Translate(x, y); s_font(font); baseline = s_ascent(font); str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES); str_compute_lines(lines, nlines, font, x, y, w, h, hadjust, vadjust); if ( flags & TXT_UNDERLINED ) { r_dash(NAME_none); r_thickness(1); } for(n=0, line = lines; n++ < nlines; line++) { str_text(&line->text, line->x, line->y+baseline); if ( flags & TXT_UNDERLINED ) XDrawLine(context.display, context.drawable, context.gcs->workGC, line->x, line->y+baseline+1, line->x+line->width, line->y+baseline+1); } } void str_selected_string(PceString s, FontObj font, int f, int t, Style style, /* selection parameters */ int x, int y, int w, int h, Name hadjust, Name vadjust) { strTextLine lines[MAX_TEXT_LINES]; strTextLine *line; int nlines, n; int baseline; int here = 0; if ( s->s_size == 0 ) return; Translate(x, y); s_font(font); baseline = s_ascent(font); str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES); str_compute_lines(lines, nlines, font, x, y, w, h, hadjust, vadjust); for(n=0, line = lines; n++ < nlines; line++) { int len = line->text.s_size; line->x += lbearing(str_fetch(&line->text, 0)); if ( t <= here || f >= here+len ) /* outside */ str_stext(&line->text, 0, len, line->x, line->y+baseline, NIL); else { int sf, sx, sl; sf = (f <= here ? 0 : f-here); sl = (t >= here+len ? len-sf : t-here-sf); sx = s_advance(&line->text, 0, sf); str_stext(&line->text, 0, sf, line->x, line->y+baseline, NIL); str_stext(&line->text, sf, sl, line->x+sx, line->y+baseline, style); if ( sf+sl < len ) { int a = sf+sl; int ax = sx + s_advance(&line->text, sf, a); str_stext(&line->text, a, len-a, line->x+ax, line->y+baseline, NIL); } } here += len + 1; /* 1 for the newline */ } } /******************************* * POSTSCRIPT SUPPORT * *******************************/ void ps_string(PceString s, FontObj font, int x, int y, int w, Name format, int flags) { strTextLine lines[MAX_TEXT_LINES]; strTextLine *line; int nlines, n; int baseline; if ( s->s_size == 0 ) return; s_font(font); ps_font(font); baseline = s_ascent(font); str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES); str_compute_lines(lines, nlines, font, x, y, w, 0, format, NAME_top); for(n=0, line = lines; n++ < nlines; line++) { if ( line->text.s_size > 0 ) { ps_output("~D ~D 0 ~D ~a text\n", line->x, line->y+baseline, line->width, &line->text); if ( flags & TXT_UNDERLINED ) { ps_output("nodash 1 ~D ~D ~D ~D linepath draw\n", line->x, line->y+baseline+2, line->width, 0); } } } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Draws a string, just like str_string(), but underscores the first character matching the accelerator (case-insensitive). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static void str_draw_text_lines(int acc, FontObj font, int nlines, strTextLine *lines, int ox, int oy) { strTextLine *line; int n; int baseline = s_ascent(font); for(n=0, line = lines; n++ < nlines; line++) { str_text(&line->text, line->x+ox, line->y+baseline+oy); if ( acc ) { int cx = line->x; int cn; cx += lbearing(str_fetch(&line->text, 0)); for(cn=0; cntext.s_size; cn++) { int c = str_fetch(&line->text, cn); int cw = c_width(c, font); if ( (int)tolower(c) == acc ) { /* not r_line to avoid double Translate() */ XDrawLine(context.display, context.drawable, context.gcs->workGC, cx, line->y+baseline+1, cx+cw-2, line->y+baseline+1); acc = 0; break; } cx += cw; } } } } static Any r_text_colour(Any c) { int oldfixed = context.fixed_colours; Any old; context.fixed_colours = 0; old = r_colour(c); context.fixed_colours = oldfixed; return old; } void str_label(PceString s, int acc, FontObj font, int x, int y, int w, int h, Name hadjust, Name vadjust, int flags) { strTextLine lines[MAX_TEXT_LINES]; int nlines; if ( s->s_size == 0 ) return; Translate(x, y); s_font(font); str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES); str_compute_lines(lines, nlines, font, x, y, w, h, hadjust, vadjust); if ( acc ) { r_dash(NAME_none); r_thickness(1); } if ( flags & LABEL_INACTIVE ) { if ( context.depth > 1 ) { Any old = r_text_colour(WHITE_COLOUR); str_draw_text_lines(acc, font, nlines, lines, 1, 1); r_text_colour(ws_3d_grey()); str_draw_text_lines(acc, font, nlines, lines, 0, 0); r_text_colour(old); } else { Any old = r_text_colour(GREY50_IMAGE); str_draw_text_lines(acc, font, nlines, lines, 0, 0); r_text_colour(old); } } else str_draw_text_lines(acc, font, nlines, lines, 0, 0); }