/* This file is part of the FElt finite element analysis package. Copyright (C) 1993-2000 Jason I. Gobat and Darren C. Atkinson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /************************************************************************ * File: Figure.c * * * * Description: This file contains the private functions definitions * * for the figures of the Drawing widget. * ************************************************************************/ # include # include # include "DrawingP.h" static struct { int length; char dash_list [4]; } line_styles [ ] = { {1, {1}}, /* solid (not really used) */ {2, {3, 3}}, /* dashed */ {2, {3, 1}}, /* dotted */ {4, {7, 2, 1, 2}}, /* dot-dashed */ {2, {7, 7}}, /* long-dashed */ }; /************************************************************************ Function: DW_CreateFigure Description: Creates a new figure by allocating memory and initializing. ************************************************************************/ Figure DW_CreateFigure (dw, type, flag, arg) DrawingWidget dw; FigureType type; BOOLEAN flag; int arg; { Figure fig; fig = XtNew (struct figure); fig -> type = type; fig -> prev = NULL; fig -> next = NULL; fig -> line_width = dw -> drawing.line_width; fig -> line_style = dw -> drawing.line_style; fig -> fg = dw -> drawing.fg; fig -> visible = True; fig -> group = NULL; fig -> color_data = dw -> drawing.color_data; DW_CacheAddRef (fig -> color_data); switch (fig -> type) { case LineFigure: case PixmapFigure: case BitmapFigure: break; case PolygonFigure: fig -> info.polygon.filled = False; fig -> info.polygon.scaled = flag; fig -> info.polygon.npoints = arg; fig -> info.polygon.region = NULL; fig -> info.polygon.points = (Point *) XtMalloc (sizeof (Point) * arg); fig -> info.polygon.xpoints = (XPoint *) XtMalloc (sizeof (XPoint)*arg); break; case RectangleFigure: fig -> info.rectangle.filled = arg ? True : False; fig -> info.rectangle.scaled = flag; break; case ArcFigure: fig -> info.arc.filled = arg ? True : False; fig -> info.arc.scaled = flag; break; case TextFigure: fig -> info.text.font = dw -> drawing.font; fig -> info.text.scaled = flag; fig -> info.text.font_data = dw -> drawing.font_data; DW_CacheAddRef (fig -> info.text.font_data); break; case GroupFigure: fig -> info.group.fig = (Figure *) XtMalloc (sizeof (Figure) * arg); fig -> info.group.nfigs = arg; break; } return fig; } /************************************************************************ Function: DestroyFigure Description: Destroys an figure by deallocating its memory. ************************************************************************/ void DW_DestroyFigure (fig) Figure fig; { if (fig -> type == PolygonFigure) { XtFree ((char *) fig -> info.polygon.points); XtFree ((char *) fig -> info.polygon.xpoints); if (fig -> info.polygon.region) XDestroyRegion (fig -> info.polygon.region); } else if (fig -> type == TextFigure) { DW_CacheDelRef (fig -> info.text.font_data); XtFree ((char *) fig -> info.text.string); } else if (fig -> type == GroupFigure) XtFree ((char *) fig -> info.group.fig); DW_CacheDelRef (fig -> color_data); XtFree ((char *) fig); } /************************************************************************ Function: DW_AppendFigure Description: Adds a figure to the end of the display list. ************************************************************************/ void DW_AppendFigure (dw, fig) DrawingWidget dw; Figure fig; { fig -> prev = dw -> drawing.tail; if (dw -> drawing.tail != NULL) dw -> drawing.tail -> next = fig; else dw -> drawing.head = fig; dw -> drawing.tail = fig; } /************************************************************************ Function: DW_PrependFigure Description: Adds a figure to the beginning of the display list. ************************************************************************/ void DW_PrependFigure (dw, fig) DrawingWidget dw; Figure fig; { fig -> next = dw -> drawing.head; if (dw -> drawing.head != NULL) dw -> drawing.head -> prev = fig; else dw -> drawing.tail = fig; dw -> drawing.head = fig; } /************************************************************************ Function: DW_DeleteFigure Description: Deletes a figure from the display list. ************************************************************************/ void DW_DeleteFigure (dw, fig) DrawingWidget dw; Figure fig; { if (fig -> next != NULL) fig -> next -> prev = fig -> prev; else dw -> drawing.tail = fig -> prev; if (fig -> prev != NULL) fig -> prev -> next = fig -> next; else dw -> drawing.head = fig -> next; fig -> prev = fig -> next = NULL; } /************************************************************************ Function: DW_ScaleFigure Description: Scale a figure to window coordinates. ************************************************************************/ void DW_ScaleFigure (dw, fig) DrawingWidget dw; Figure fig; { int i; int direction; int ascent; int descent; unsigned dont_care; short minx; short maxx; short miny; short maxy; short x; short y; Window root; Figure member; XCharStruct overall; switch (fig -> type) { case LineFigure: fig -> info.line.xpoints [0].x = XAbs (fig -> info.line.points [0].x); fig -> info.line.xpoints [0].y = YAbs (fig -> info.line.points [0].y); fig -> info.line.xpoints [1].x = XAbs (fig -> info.line.points [1].x); fig -> info.line.xpoints [1].y = YAbs (fig -> info.line.points [1].y); if (fig -> info.line.xpoints [0].x < fig -> info.line.xpoints [1].x) { fig -> x = fig -> info.line.xpoints [0].x; fig -> width = fig -> info.line.xpoints [1].x - fig -> x; } else { fig -> x = fig -> info.line.xpoints [1].x; fig -> width = fig -> info.line.xpoints [0].x - fig -> x; } if (fig -> info.line.xpoints [0].y < fig -> info.line.xpoints [1].y) { fig -> y = fig -> info.line.xpoints [0].y; fig -> height = fig -> info.line.xpoints [1].y - fig -> y; } else { fig -> y = fig -> info.line.xpoints [1].y; fig -> height = fig -> info.line.xpoints [0].y - fig -> y; } break; case PolygonFigure: minx = maxx = XAbs (fig -> info.polygon.points [0].x); miny = maxy = YAbs (fig -> info.polygon.points [0].y); if (fig -> info.polygon.scaled == True) { for (i = 0; i < fig -> info.polygon.npoints; i ++) { x = fig -> info.polygon.xpoints [i].x = XAbs (fig -> info.polygon.points [i].x); y = fig -> info.polygon.xpoints [i].y = YAbs (fig -> info.polygon.points [i].y); if (x < minx) minx = x; else if (x > maxx) maxx = x; if (y < miny) miny = y; else if (y > maxy) maxy = y; } } else { } if (fig -> info.polygon.filled == True) { if (fig -> info.polygon.region) XDestroyRegion (fig -> info.polygon.region); fig -> info.polygon.region = XPolygonRegion ( fig -> info.polygon.xpoints, fig -> info.polygon.npoints, EvenOddRule); } if (minx < maxx) { fig -> x = minx; fig -> width = maxx - minx; } else { fig -> x = maxx; fig -> width = minx - maxx; } if (miny < maxy) { fig -> y = miny; fig -> height = maxy - miny; } else { fig -> y = maxy; fig -> height = miny - maxy; } break; case RectangleFigure: if (fig -> info.rectangle.width > 0) { if (fig -> info.rectangle.scaled == True) fig -> width = XRel (fig -> info.rectangle.width); else fig -> width = Round (fig -> info.rectangle.width); fig -> x = XAbs (fig -> info.rectangle.x); } else { if (fig -> info.rectangle.scaled == True) fig -> width = -XRel (fig -> info.rectangle.width); else fig -> width = Round (-fig -> info.rectangle.width); fig -> x = XAbs (fig -> info.rectangle.x) - fig -> width; } if (fig -> info.rectangle.height > 0) { if (fig -> info.rectangle.scaled == True) fig -> height = YRel (fig -> info.rectangle.height); else fig -> height = Round (fig -> info.rectangle.height); fig -> y = YAbs (fig -> info.rectangle.y) - fig -> height; } else { if (fig -> info.rectangle.scaled == True) fig -> height = -YRel (fig -> info.rectangle.height); else fig -> height = Round (-fig -> info.rectangle.height); fig -> y = YAbs (fig -> info.rectangle.y); } break; case ArcFigure: if (fig -> info.arc.width > 0) if (fig -> info.arc.scaled == True) fig -> width = XRel (fig -> info.arc.width); else fig -> width = Round (fig -> info.arc.width); else if (fig -> info.arc.scaled == True) fig -> width = -XRel (fig -> info.arc.width); else fig -> width = Round (-fig -> info.arc.width); if (fig -> info.arc.height > 0) if (fig -> info.arc.scaled == True) fig -> height = XRel (fig -> info.arc.height); else fig -> height = Round (fig -> info.arc.height); else if (fig -> info.arc.scaled == True) fig -> height = -XRel (fig -> info.arc.height); else fig -> height = Round (-fig -> info.arc.height); fig -> x = XAbs (fig -> info.arc.x) - fig -> width / 2; fig -> y = YAbs (fig -> info.arc.y) - fig -> height / 2; break; case TextFigure: fig -> info.text.x = XAbs (fig -> info.text.rx); fig -> info.text.y = YAbs (fig -> info.text.ry); XTextExtents (fig -> info.text.font, fig -> info.text.string, fig -> info.text.length, &direction, &ascent, &descent, &overall); fig -> x = fig -> info.text.x + overall.lbearing; fig -> y = fig -> info.text.y - overall.ascent; fig -> width = overall.rbearing - overall.lbearing; fig -> height = overall.descent + overall.ascent; break; case GroupFigure: if (fig -> info.group.nfigs > 0) { minx = fig -> info.group.fig [0] -> x; miny = fig -> info.group.fig [0] -> y; maxx = minx + fig -> info.group.fig [0] -> width; maxy = miny + fig -> info.group.fig [0] -> height; } else { minx = miny = maxx = maxy = 0; } for (i = 1; i < fig -> info.group.nfigs; i ++) { member = fig -> info.group.fig [i]; if (minx > member -> x) minx = member -> x; if (miny > member -> y) miny = member -> y; if (maxx < member -> x + member -> width) maxx = member -> x + member -> width; if (maxy < member -> y + member -> height) maxy = member -> y + member -> height; } if (minx < maxx) { fig -> x = minx; fig -> width = maxx - minx; } else { fig -> x = maxx; fig -> width = minx - maxx; } if (miny < maxy) { fig -> y = miny; fig -> height = maxy - miny; } else { fig -> y = maxy; fig -> height = miny - maxy; } break; case PixmapFigure: case BitmapFigure: XGetGeometry (XtDisplay ((Widget) dw), fig -> info.pixmap.pixmap, &root, (int *) &dont_care, (int *) &dont_care, &fig -> width, &fig -> height, &dont_care, &dont_care); fig -> x = XAbs (fig -> info.pixmap.x); fig -> y = YAbs (fig -> info.pixmap.y) - fig -> height; break; } } /************************************************************************ Function: DW_DrawFigure Description: Draws a figure in the window of the specified widget. ************************************************************************/ void DW_DrawFigure (dw, fig) DrawingWidget dw; Figure fig; { Display *display; Window window; GC drawgc; Font font; unsigned long valuemask; XGCValues values; Boolean first_time; if (fig -> visible == False) return; valuemask = 0; display = XtDisplay ((Widget) dw); window = XtWindow ((Widget) dw); drawgc = dw -> drawing.drawgc; first_time = dw -> drawing.last_style == -1 ? True : False; if (first_time || dw -> drawing.last_fg != fig -> fg) { valuemask |= GCForeground; values.foreground = dw -> drawing.last_fg = fig -> fg; } if (first_time || dw -> drawing.last_width != fig -> line_width) { valuemask |= GCLineWidth; values.line_width = dw -> drawing.last_width = fig -> line_width; } if (first_time || dw -> drawing.last_style != fig -> line_style) { dw -> drawing.last_style = fig -> line_style; valuemask |= GCLineStyle; if (fig -> line_style != DW_LineSolid) { values.line_style = LineOnOffDash; XSetDashes (display, dw -> drawing.drawgc, 0, line_styles [fig -> line_style].dash_list, line_styles [fig -> line_style].length); XSetDashes (display, dw -> drawing.intergc, 0, line_styles [fig -> line_style].dash_list, line_styles [fig -> line_style].length); } else values.line_style = LineSolid; } if (valuemask) { XChangeGC (display, drawgc, valuemask, &values); XChangeGC (display, dw -> drawing.intergc, valuemask, &values); } switch (fig -> type) { case LineFigure: XDrawLine (display, window, drawgc, fig -> info.line.xpoints [0].x, fig -> info.line.xpoints [0].y, fig -> info.line.xpoints [1].x, fig -> info.line.xpoints [1].y); break; case PolygonFigure: if (fig -> info.polygon.filled == True) XFillPolygon (display, window, drawgc, fig -> info.polygon.xpoints, fig -> info.polygon.npoints, Complex, CoordModeOrigin); else XDrawLines (display, window, drawgc, fig -> info.polygon.xpoints, fig -> info.polygon.npoints, CoordModeOrigin); break; case RectangleFigure: if (fig -> info.rectangle.filled == True) XFillRectangle (display, window, drawgc, fig -> x, fig -> y, fig -> width, fig -> height); else XDrawRectangle (display, window, drawgc, fig -> x, fig -> y, fig -> width, fig -> height); break; case ArcFigure: if (fig -> info.arc.filled == True) XFillArc (display, window, drawgc, fig -> x, fig -> y, fig -> width, fig -> height, fig -> info.arc.start, fig -> info.arc.length); else XDrawArc (display, window, drawgc, fig -> x, fig -> y, fig -> width, fig -> height, fig -> info.arc.start, fig -> info.arc.length); break; case TextFigure: font = fig -> info.text.font -> fid; if (first_time || dw -> drawing.last_font != font) { dw -> drawing.last_font = font; XSetFont (display, drawgc, font); XSetFont (display, dw -> drawing.intergc, font); } XDrawString (display, window, drawgc, fig -> info.text.x, fig -> info.text.y, fig -> info.text.string, fig -> info.text.length); break; case GroupFigure: break; case PixmapFigure: XCopyArea (display, fig -> info.pixmap.pixmap, window, drawgc, 0, 0, fig -> width, fig -> height, fig -> x, fig -> y); break; case BitmapFigure: XSetFunction (display, drawgc, GXand); XSetBackground (display, drawgc, 255); XCopyPlane (display, fig -> info.pixmap.pixmap, window, drawgc, 0, 0, fig -> width, fig -> height, fig -> x, fig -> y, 1L); XSetFunction (display, drawgc, GXor); XSetBackground (display, drawgc, 0); XCopyPlane (display, fig -> info.pixmap.pixmap, window, drawgc, 0, 0, fig -> width, fig -> height, fig -> x, fig -> y, 1L); if (dw -> drawing.interactive == True) XSetFunction (display, drawgc, GXxor); else XSetFunction (display, drawgc, GXcopy); XSetBackground (display, drawgc, dw -> core.background_pixel); break; } first_time = 0; } /************************************************************************ Function: DW_ClearFigure Description: Clears the area occupied by a figure. ************************************************************************/ void DW_ClearFigure (dw, fig) DrawingWidget dw; Figure fig; { XRectangle rect; if (fig -> visible == False) return; if (dw -> drawing.interactive == True) DW_DrawFigure (dw, fig); else { DW_ClipBox (fig, &rect); rect.x -= fig -> line_width + 1; rect.y -= fig -> line_width + 1; rect.width += 2 * fig -> line_width + 2; rect.height += 2 * fig -> line_width + 2; if (dw -> drawing.redraw == True) XClearArea (XtDisplay ((Widget) dw), XtWindow ((Widget) dw), rect.x, rect.y, rect.width, rect.height, True); else XUnionRectWithRegion (&rect, dw -> drawing.bufferRegion, dw -> drawing.bufferRegion); } } /************************************************************************ Function: DW_Detach Description: Detach a figure from a group. ************************************************************************/ void DW_Detach (dw, fig) DrawingWidget dw; Figure fig; { unsigned i; unsigned j; Figure leader; if (fig -> group != NULL) { leader = fig -> group; for (i = 0; i < leader -> info.group.nfigs; i ++) if (leader -> info.group.fig [i] == fig) { for (j = i + 1; j < leader -> info.group.nfigs; j ++) leader->info.group.fig [j - 1] = leader->info.group.fig [j]; leader -> info.group.nfigs --; break; } DW_ScaleFigure (dw, leader); } } /************************************************************************ Function: DW_Attach Description: Attach a figure to a group. ************************************************************************/ void DW_Attach (dw, fig, group) DrawingWidget dw; Figure fig; Figure group; { if (fig -> group == group) return; if (group != NULL) { group -> info.group.fig = (Figure *) XtRealloc ((char *) group -> info.group.fig, sizeof (Figure) * (group -> info.group.nfigs + 1)); group -> info.group.fig [group -> info.group.nfigs] = fig; group -> info.group.nfigs ++; DW_ScaleFigure (dw, group); } fig -> group = group; }