/* $XConsortium: draw.c /main/5 1996/06/13 10:32:01 drk $ */ /* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * */ /* * HISTORY */ /* ** This demo shows an AUTOMATIC scrolled window and some graphics ** done in a DrawingArea. ** ** It lets you draw polylines and buttons, resizes the picture, ** and eventually save the data in an X resource format. ** */ #include #include #include #include /*------------------------------------------------------------- ** Graphic data structure and API */ #define APP_CLASS "XmdDraw" /* very simple data struct, static size, etc... */ #define MAX_POINT 100 typedef struct { XPoint points[MAX_POINT] ; Cardinal num_points ; #define POLYLINE 1 unsigned char type ; } GraphicUnit ; #define MAX_GRAPH 100 typedef struct { GraphicUnit graphics[MAX_GRAPH] ; Cardinal num_graphics ; XPoint anchor_point ; XPoint drag_point ; GC drag_gc ; Boolean in_drag ; Dimension old_width, old_height ; Widget work_area ; Widget textf ; } Graphic ; static char drawTranslations[] = "#replace\n\ ~s ~m ~a Return:DrawingAreaInput() ManagerParentActivate()\n\ Return:DrawingAreaInput() ManagerGadgetSelect()\n\ osfActivate:DrawingAreaInput() ManagerParentActivate()\n\ osfCancel:DrawingAreaInput() ManagerParentCancel()\n\ osfHelp:DrawingAreaInput() ManagerGadgetHelp()\n\ space:DrawingAreaInput() ManagerGadgetSelect()\n\ osfSelect:DrawingAreaInput() ManagerGadgetSelect()\n\ :DrawingAreaInput() ManagerGadgetKeyInput()\n\ :DrawingAreaInput()\n\ :DrawingAreaInput() ManagerGadgetButtonMotion()\n\ :DrawingAreaInput() ManagerGadgetButtonMotion()\n\ :DrawingAreaInput() ManagerGadgetArm()\n\ :DrawingAreaInput() ManagerGadgetActivate()\n\ :DrawingAreaInput() ManagerGadgetDrag()\n\ :DrawingAreaInput()\n\ :DrawingAreaInput()"; /*------------------------------------------------------------- ** Application data stuff: this serve as the file saving format */ typedef struct { char * lines; char * towns; } ApplicationData, * ApplicationDataPtr ; #define XtNlines "lines" #define XtCLines "Lines" #define XtNtowns "towns" #define XtCTowns "Towns" /* if this stuff was as easily initializable, I would put it local to main */ static XtResource app_resources[] = { { XtNlines, XtCLines, XmRString, sizeof(String), XtOffset(ApplicationDataPtr, lines), XmRString, "" }, { XtNtowns, XtCTowns, XmRString, sizeof(String), XtOffset(ApplicationDataPtr, towns), XmRString, "" }, }; /* if this stuff was as easily initializable, I would put it local to main */ /* better to have that in automatic mode */ static String fallback_resources[] = { "*help_manager.helpFile: draw", "*work_area.width:1000", "*work_area.height:1000", "*XmScale.minimum: 100", "*XmScale.maximum: 16000", ".geometry:600x500", NULL } ; /*------------------------------------------------------------- ** forwarded functions */ static void CreateApplication (Widget parent, Graphic *graph); /* Xt callbacks */ static void SaveCB (Widget w, XtPointer client_data, XtPointer call_data); static void OK_WriteCB (Widget w, XtPointer client_data, XtPointer call_data); static void QuitCB (Widget w, XtPointer client_data, XtPointer call_data); static void HelpCB (Widget w, XtPointer client_data, XtPointer call_data); static void DrawCB (Widget w, XtPointer client_data, XtPointer call_data); static void ValueCB (Widget w, XtPointer client_data, XtPointer call_data); static void TravCB (Widget w, XtPointer client_data, XtPointer call_data); static void PushCB (Widget w, XtPointer client_data, XtPointer call_data); static void InitDraw (Graphic *graph, ApplicationData *app_data); static void ReDraw (Graphic *graph, XEvent *event); static void ReSize (Graphic *graph, Dimension widget, Dimension height); static void StartUnit (Graphic *graph, Position x, Position y); static void DragUnit (Graphic *graph, Position x, Position y); static Boolean NearPoint (XPoint point, Position x, Position y); static void EndUnit (Graphic *graph, Position x, Position y); static void DeleteUnit (Graphic *graph, Position x, Position y); /*------------------------------------------------------------- ** Main body */ int main(int argc, char **argv) { XtAppContext app_context; Widget toplevel ; ApplicationData app_data; Graphic legraph ; toplevel = XtAppInitialize(&app_context, APP_CLASS, NULL, 0, &argc, argv, fallback_resources, NULL, 0); /** Plug in Editres protocol */ XmdRegisterEditres(toplevel); XtGetApplicationResources(toplevel, (XtPointer)&app_data, app_resources, XtNumber(app_resources), NULL, 0); CreateApplication (toplevel, &legraph); InitDraw(&legraph, &app_data); XtRealizeWidget(toplevel); XtAppMainLoop(app_context); /*NOTREACHED*/ exit(0); } /*------------------------------------------------------------- ** Create a Main Window with a menubar, a command panel containing ** 2 scales and a textfied, and a workarea. ** Also put in the graphic structure the workarea info and the ** textfield ids. */ static void CreateApplication (Widget parent, Graphic * graph) { Widget main_window, menu_bar, menu_pane, cascade, button, comw, scale ; Arg args[5]; int n ; XtTranslations xlations; /* Create automatic MainWindow. */ n = 0; XtSetArg (args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; main_window = XmCreateMainWindow (parent, "main_window", args, n); XtAddCallback (main_window, XmNtraverseObscuredCallback, TravCB, (XtPointer)graph); XtManageChild (main_window); /* Create MenuBar in MainWindow. */ n = 0; menu_bar = XmCreateMenuBar (main_window, "menu_bar", args, n); XtManageChild (menu_bar); /* Create "File" PulldownMenu with Save and Quit buttons */ n = 0; menu_pane = XmCreatePulldownMenu (menu_bar, "menu_pane", args, n); n = 0; button = XmCreatePushButton (menu_pane, "Save...", args, n); XtManageChild (button); /* pass the graphic id to the save function */ XtAddCallback (button, XmNactivateCallback, SaveCB, (XtPointer)graph); n = 0; button = XmCreatePushButton (menu_pane, "Quit", args, n); XtManageChild (button); XtAddCallback (button, XmNactivateCallback, QuitCB, NULL); n = 0; XtSetArg (args[n], XmNsubMenuId, menu_pane); n++; cascade = XmCreateCascadeButton (menu_bar, "File", args, n); XtManageChild (cascade); /* Create "Help" PulldownMenu with Help button. */ n = 0; menu_pane = XmCreatePulldownMenu (menu_bar, "menu_pane", args, n); n = 0; button = XmCreatePushButton (menu_pane, "Overview", args, n); XtManageChild (button); XtAddCallback (button, XmNactivateCallback, HelpCB, NULL); n = 0; XtSetArg (args[n], XmNsubMenuId, menu_pane); n++; cascade = XmCreateCascadeButton (menu_bar, "Help", args, n); XtManageChild (cascade); n = 0; XtSetArg (args[n], XmNmenuHelpWidget, cascade); n++; XtSetValues (menu_bar, args, n); /* Create work_area in MainWindow */ n = 0; XtSetArg (args[n], XmNresizePolicy, XmRESIZE_NONE); n++ ; XtSetArg (args[n], XmNmarginWidth, 0); n++ ; XtSetArg (args[n], XmNmarginHeight, 0); n++ ; /* hardcode this one, since its is required for the motion handling */ xlations = XtParseTranslationTable(drawTranslations); XtSetArg (args[n], XmNtranslations, xlations); n++; graph->work_area = XmCreateDrawingArea(main_window, "work_area", args, n); XtAddCallback (graph->work_area, XmNexposeCallback, DrawCB, (XtPointer)graph); XtAddCallback (graph->work_area, XmNresizeCallback, DrawCB, (XtPointer)graph); XtAddCallback (graph->work_area, XmNinputCallback, DrawCB, (XtPointer)graph); XtManageChild (graph->work_area); /* Create a commandWindow in MainWindow with text and scales */ n = 0; comw = XmCreateRowColumn(main_window, "comw", args, n); XtManageChild (comw); n = 0; XtSetArg (args[n], XmNcommandWindow, comw); n++; XtSetValues (main_window, args, n); /* find the initial size of the work_area and report to the scales */ n = 0; XtSetArg (args[n], XmNwidth, &graph->old_width); n++; XtSetArg (args[n], XmNheight, &graph->old_height); n++; XtGetValues (graph->work_area, args, n); n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNshowValue, True); n++; XtSetArg (args[n], XmNvalue, graph->old_width); n++; scale = XmCreateScale(comw, "scale_w", args, n); /* scale_w is the name */ XtAddCallback (scale, XmNvalueChangedCallback, ValueCB, (XtPointer)graph->work_area); XtManageChild (scale); n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNshowValue, True); n++; XtSetArg (args[n], XmNvalue, graph->old_height); n++; scale = XmCreateScale(comw, "scale_h", args, n); XtAddCallback (scale, XmNvalueChangedCallback, ValueCB, (XtPointer)graph->work_area); XtManageChild (scale); n = 0; graph->textf = XmCreateTextField(comw, "textf", args, n); XtManageChild (graph->textf); /* Set MainWindow areas */ XmMainWindowSetAreas (main_window, menu_bar, comw, NULL, NULL, graph->work_area); } /*------------------------------------------------------------- ** PushCB - callback for push button inside the workarea */ static void PushCB (Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { XmPushButtonCallbackStruct * pbcs = (XmPushButtonCallbackStruct *) call_data ; if ((pbcs->event->type == ButtonRelease) && (pbcs->event->xbutton.state & ShiftMask)) XtDestroyWidget(w); /* else printf("Activate %s\n",XtName(w));*/ } /*------------------------------------------------------------- ** TravCB - callback for traverseObscure */ static void TravCB (Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { XmTraverseObscuredCallbackStruct * tocs = (XmTraverseObscuredCallbackStruct *) call_data ; Graphic * graph = (Graphic *) client_data ; if (tocs->traversal_destination != graph->work_area) XmScrollVisible(w, tocs->traversal_destination, 20, 20) ; } /*------------------------------------------------------------- ** ValueCB - callback for scales */ static void ValueCB (Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { Arg args[5]; int n ; int value ; Widget workarea = (Widget) client_data ; /* get the value outof the Scale */ n = 0; XtSetArg (args[n], XmNvalue, &value); n++; XtGetValues (w, args, n); n = 0; if (strcmp(XtName(w), "scale_w") == 0 ) { /* width scale */ XtSetArg (args[n], XmNwidth, value); n++; } else { XtSetArg (args[n], XmNheight, value); n++; } XtSetValues (workarea, args, n); } /*------------------------------------------------------------- ** SaveCB - callback for Save button */ static void SaveCB (Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { static Widget fsb_box = NULL ; if (!fsb_box) { fsb_box = XmCreateFileSelectionDialog (w, "Save graphics", NULL, 0); /* just propagate the graphic information */ XtAddCallback (fsb_box, XmNokCallback, OK_WriteCB, client_data); } XtManageChild (fsb_box); } /*------------------------------------------------------------- ** QuitCB - callback for quit button */ static void QuitCB (Widget w, /* widget id */ XtPointer client_data, /* data from applicaiton */ XtPointer call_data) /* data from widget class */ { exit (0); } /*------------------------------------------------------------- ** HelpCB - callback for help button */ static void HelpCB (Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { static Widget help_widget = NULL ; if (!help_widget) help_widget = XmdCreateHelpDialog(w, "help_manager", NULL, 0); XtManageChild(help_widget); } /*------------------------------------------------------------- ** OK_WriteCB - callback for saving the graphics in a file */ static void OK_WriteCB (Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { Widget text ; char * filename ; FILE *out_file ; Cardinal i,j ; Arg args[5]; int n ; Widget * children ; Cardinal num_children ; Graphic * graph = (Graphic *) client_data ; Position x,y ; text = XmFileSelectionBoxGetChild(w, XmDIALOG_TEXT); filename = XmTextGetString(text); if ((out_file=fopen(filename, "w")) == NULL) { printf("Can't open file: %s ", filename); return ; } fprintf(out_file, "draw.lines: "); for (i=0; i < graph->num_graphics; i++) { for (j=0; j < graph->graphics[i].num_points; j++) { fprintf(out_file, "%d_%d", graph->graphics[i].points[j].x, graph->graphics[i].points[j].y) ; if (j == graph->graphics[i].num_points - 1) { if (i != graph->num_graphics - 1) fprintf(out_file, ". ") ; } else fprintf(out_file, ", ") ; } } fprintf(out_file, "\n"); fprintf(out_file, "draw.towns: "); n = 0; XtSetArg (args[n], XmNnumChildren, &num_children); n++; XtSetArg (args[n], XmNchildren, &children); n++; XtGetValues (graph->work_area, args, n); for (i=0; i < num_children; i++) { fprintf(out_file, "%s", XtName(children[i])); if (i != num_children - 1) fprintf(out_file, ", ") ; } fprintf(out_file, "\n"); for (i=0; i < num_children; i++) { n = 0; XtSetArg (args[n], XmNx, &x); n++; XtSetArg (args[n], XmNy, &y); n++; XtGetValues (children[i], args, n); fprintf(out_file, "draw*%s.x: %d\n",XtName(children[i]),x); fprintf(out_file, "draw*%s.y: %d\n",XtName(children[i]),y); } fclose(out_file); } /*------------------------------------------------------------- ** DrawCB - callback for drawing area */ static void DrawCB (Widget w, /* widget id */ XtPointer client_data, /* data from application */ XtPointer call_data) /* data from widget class */ { XmDrawingAreaCallbackStruct * dacs = (XmDrawingAreaCallbackStruct *) call_data ; Arg args[5]; int n ; Dimension width, height ; String name ; Widget newpush ; XSetWindowAttributes xswa; Graphic * graph = (Graphic *) client_data ; static Boolean first_time = True ; switch (dacs->reason) { case XmCR_EXPOSE: if (first_time) { /* Change once the bit gravity of the Drawing Area; default is north west and we want forget, so that resize always generates exposure events */ first_time = False ; xswa.bit_gravity = ForgetGravity ; XChangeWindowAttributes(XtDisplay(w), XtWindow(w), CWBitGravity, &xswa); } ReDraw(graph, dacs->event) ; break ; case XmCR_RESIZE: n = 0; XtSetArg (args[n], XmNwidth, &width); n++; XtSetArg (args[n], XmNheight, &height); n++; XtGetValues (w, args, n); ReSize(graph, width, height) ; break ; case XmCR_INPUT: if (dacs->event->type == ButtonPress) { name = XmTextFieldGetString(graph->textf) ; /* textfield */ if (strcmp ("", name) != 0) { n = 0; XtSetArg (args[n], XmNx, dacs->event->xbutton.x); n++; XtSetArg (args[n], XmNy, dacs->event->xbutton.y); n++; newpush = XmCreatePushButton(w, name, args, n); XtAddCallback (newpush, XmNactivateCallback, PushCB, NULL); XtManageChild (newpush); } else if ((dacs->event->xbutton.state & ShiftMask) && (!graph->in_drag)) { DeleteUnit (graph, dacs->event->xbutton.x, dacs->event->xbutton.y); } else { if (!graph->in_drag) { StartUnit(graph, dacs->event->xbutton.x, dacs->event->xbutton.y); } else { EndUnit(graph, dacs->event->xbutton.x, dacs->event->xbutton.y); } } XtFree(name); } else /* need to get motion events here: app_default should modified DrawingArea translation with both Motion and BtnMotion addition */ if (dacs->event->type == MotionNotify) { /* this one just exits if in_drag is False */ DragUnit(graph, dacs->event->xbutton.x, dacs->event->xbutton.y); } break ; } } /*************************** GRAPHICS **********************************/ static void InitDraw(Graphic * graph, ApplicationData * app_data) { XGCValues val ; Arg args[5]; int n ; Cardinal i ; Dimension width, height ; String pstr, wstr ; int x, y ; Widget newpush ; /* create the gc used for the rudder banding effect */ n = 0; XtSetArg (args[n], XmNforeground, &val.foreground); n++; XtSetArg (args[n], XmNbackground, &val.background); n++; XtGetValues (graph->work_area, args, n); val.foreground = val.foreground ^ val.background ; val.function = GXxor ; graph->drag_gc = XtGetGC(graph->work_area, GCForeground | GCBackground | GCFunction, &val); /* initialize the graphic stuff */ graph->in_drag = False ; graph->num_graphics = 0 ; for (i=0; i < MAX_GRAPH; i++) { graph->graphics[i].num_points = 0 ; } /* polylines syntax: draw.lines: 10_10, 20_30, 28_139. 11_112, 145_60. 211_112, 45_60 would be nice to use nested strtok but the sucker is not reentrant... */ wstr = XtNewString(app_data->lines) ; for(pstr = (char *) strtok(wstr, ".,") ; pstr ; pstr = (char *) strtok( NULL, ".,")) { while (*pstr && isspace(*pstr)) pstr++; if (*pstr == '\0') break; sscanf(pstr, "%d_%d", &x, &y); graph->graphics[graph->num_graphics].points [graph->graphics[graph->num_graphics].num_points].x = x ; graph->graphics[graph->num_graphics].points [graph->graphics[graph->num_graphics].num_points].y = y ; graph->graphics[graph->num_graphics].num_points ++ ; graph->graphics[graph->num_graphics].type = POLYLINE ; /* look in the original to see if it is a new unit */ if (app_data->lines[pstr - wstr + strlen(pstr)] == '.') graph->num_graphics ++ ; } XtFree(wstr); if (strlen(app_data->lines)) graph->num_graphics ++ ; /* Towns syntax: draw.towns: Boston, Woburn, SanJose draw*Boston.x: 30 draw*Boston.y: 30 draw*Woburn.x: 130 draw*Woburn.y: 30 draw*SanJose.x: 30 draw*SanJose.y: 130 */ wstr = XtNewString(app_data->towns) ; for(pstr = (char *) strtok(wstr, ".,") ; pstr ; pstr = (char *) strtok( NULL, ".,")) { while (*pstr && isspace(*pstr)) pstr++; if (*pstr == '\0') break; newpush = XmCreatePushButton(graph->work_area, pstr, NULL, 0); XtAddCallback (newpush, XmNactivateCallback, PushCB, NULL); XtManageChild (newpush); } XtFree(wstr); } static void ReDraw(Graphic * graph, XEvent * event) { Cardinal i ; Widget w = graph->work_area ; /* the expose event region could be used for better performance */ for (i=0; i < graph->num_graphics; i++) { if (graph->graphics[i].type == POLYLINE) XDrawLines(XtDisplay(w), XtWindow(w), XDefaultGC(XtDisplay(w),XDefaultScreen(XtDisplay(w))), graph->graphics[i].points, graph->graphics[i].num_points, CoordModeOrigin); } } static void ReSize(Graphic * graph, Dimension width, Dimension height) { /* Note: very bad design: resize should not modify the data since you lose everything when you resize tiny tiny tiny, but hey, what do you expect for a demo ? This way at least I can use the resource interface for creating the buttons in place (see InitDraw) */ Widget w = graph->work_area ; Cardinal i,j ; Arg args[5]; int n ; Widget * children ; Cardinal num_children ; Position x,y ; float xratio = (float) width / graph->old_width, yratio = (float) height / graph->old_height ; /* reposition and resize the graphic units */ for (i=0; i < graph->num_graphics; i++) { for (j=0; j < graph->graphics[i].num_points; j++) { graph->graphics[i].points[j].x *= xratio ; graph->graphics[i].points[j].y *= yratio ; } } /* reposition the pushbutton children */ /* I can use XtMoveWidget here since it's like being part of the widget resize class method... */ n = 0; XtSetArg (args[n], XmNnumChildren, &num_children); n++; XtSetArg (args[n], XmNchildren, &children); n++; XtGetValues (w, args, n); for (i=0; i < num_children; i++) { n = 0; XtSetArg (args[n], XmNx, &x); n++; XtSetArg (args[n], XmNy, &y); n++; XtGetValues (children[i], args, n); XtMoveWidget(children[i], (Position) (x * xratio), (Position) (y * yratio)); } graph->old_width = width ; graph->old_height = height ; } static void StartUnit(Graphic * graph, Position x, Position y) { Widget w = graph->work_area ; graph->drag_point.x = graph->anchor_point.x = x ; graph->drag_point.y = graph->anchor_point.y = y ; graph->in_drag = True ; XDrawLine(XtDisplay(w), XtWindow(w), graph->drag_gc, graph->anchor_point.x, graph->anchor_point.y, graph->drag_point.x, graph->drag_point.y); } static void DragUnit(Graphic * graph, Position x, Position y) { Widget w = graph->work_area ; if (!graph->in_drag) return ; XDrawLine(XtDisplay(w), XtWindow(w), graph->drag_gc, graph->anchor_point.x, graph->anchor_point.y, graph->drag_point.x, graph->drag_point.y); graph->drag_point.x = x ; graph->drag_point.y = y ; XDrawLine(XtDisplay(w), XtWindow(w), graph->drag_gc, graph->anchor_point.x, graph->anchor_point.y, graph->drag_point.x, graph->drag_point.y); } static Boolean NearPoint (XPoint point, Position x, Position y) { #define ERROR 5 if ((point.x > x - ERROR) && (point.x < x + ERROR) && (point.y > y - ERROR) && (point.y < y + ERROR)) return True ; else return False ; } static void EndUnit(Graphic * graph, Position x, Position y) { Widget w = graph->work_area ; Cardinal num_points ; /* no matter what happens, we need to remove the current rubber band */ XDrawLine(XtDisplay(w), XtWindow(w), graph->drag_gc, graph->anchor_point.x, graph->anchor_point.y, graph->drag_point.x, graph->drag_point.y); /* if the given point if the same as the anchor, we're done with this polyline, exit drag mode and be ready for the next graphic unit, i.e increment num_graphics */ if (NearPoint(graph->anchor_point, x, y)) { graph->in_drag = False ; /* now see if a new unit needs to be created */ if (graph->graphics[graph->num_graphics].num_points) { graph->graphics[graph->num_graphics].type = POLYLINE ; if (graph->num_graphics < MAX_GRAPH) graph->num_graphics ++ ; else printf("The graphic buffer is full, overwrite the last...\n"); } } else { /* draw the real line and store it in the structure */ XDrawLine(XtDisplay(w), XtWindow(w), XDefaultGC(XtDisplay(w),XDefaultScreen(XtDisplay(w))), graph->anchor_point.x, graph->anchor_point.y, x, y); /* first point in a unit is actually special */ num_points = graph->graphics[graph->num_graphics].num_points ; if (num_points == 0) { graph->graphics[graph->num_graphics].points[num_points].x = graph->anchor_point.x ; graph->graphics[graph->num_graphics].points[num_points].y = graph->anchor_point.y ; graph->graphics[graph->num_graphics].num_points ++ ; num_points ++ ; } graph->graphics[graph->num_graphics].points[num_points].x = x ; graph->graphics[graph->num_graphics].points[num_points].y = y ; if (graph->graphics[graph->num_graphics].num_points < MAX_POINT) graph->graphics[graph->num_graphics].num_points ++ ; else printf("The unit buffer is full, overwrite the last...\n"); /* now start the new unit */ graph->drag_point.x = graph->anchor_point.x = x ; graph->drag_point.y = graph->anchor_point.y = y ; XDrawLine(XtDisplay(w), XtWindow(w), graph->drag_gc, graph->anchor_point.x, graph->anchor_point.y, graph->drag_point.x, graph->drag_point.y); } } static void DeleteUnit(Graphic * graph, Position x, Position y) { Widget w = graph->work_area ; Cardinal i,j ; int a = -1 ; /* try to find a unit under this point */ for (i=0; (i < graph->num_graphics) && (a == -1); i++) { for (j=0; j < graph->graphics[i].num_points; j++) { if (NearPoint(graph->graphics[i].points[j], x, y)) { a = i ; break ; } } } if (a != -1) { /* found a unit under the current point, delete and redisplay */ for (i = a ; i < graph->num_graphics ; i++) { graph->graphics[i] = graph->graphics[i+1] ; } graph->num_graphics -- ; XClearArea(XtDisplay(w), XtWindow(w), 0, 0, graph->old_width, graph->old_height, True); } }