/* * Copyright 1993-1996 Johannes Sixt * * Permission to use, copy, modify and distribute this software and its * documentation for any purpose other than its commercial exploitation * is hereby granted without fee, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation. The authors * make no representations about the suitability of this software for * any purpose. It is provided "as is" without express or implied warranty. * * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Author: Johannes Sixt */ #include /* needed by objects.h */ #include /* need privates because of XtMoveWidget */ #include #include #include "objects.h" #include "io_trans.h" #include "modify.h" TeXPictObj *selected; /* currently selected object */ typedef struct { Widget wid; /* grip widget */ TeXPictObj *obj; /* object to modify */ int point; /* index of point which to modify */ int set_points; /* number of points of this object */ XPoint *points; /* which point array to operate on */ } GripData; static XPoint points1[MAX_POINTS]; static XPoint points2[MAX_POINTS]; static XPoint lastPos; /* last actual cursor position */ static XPoint ref; /* reference point, simulates cursor pos */ static Dimension gripWidth, gripHeight, gripBW; static int gripXOff, gripYOff; /** Opposite corners point to the same array of points, but use different point offsets. This allows to easily change the object with ObjStore. */ static GripData gd[] = { /* modifying rectangular objects */ { NULL, NULL, 0, 2, points1}, /* UL corner */ { NULL, NULL, 0, 2, points2}, /* UR corner */ { NULL, NULL, 1, 2, points2}, /* LL corner */ { NULL, NULL, 1, 2, points1}, /* LR corner */ #define gripUL gd[0] #define gripUR gd[1] #define gripLL gd[2] #define gripLR gd[3] /* modifying linear objects */ { NULL, NULL, 0, 3, points1}, /* start of line */ { NULL, NULL, 1, 3, points1}, /* end of line */ { NULL, NULL, 2, 3, points1}, /* curvature point */ #define gripStart gd[4] #define gripEnd gd[5] #define gripCurv gd[6] }; static XtTranslations resizeTrans; static char resizeTransTable[] = "\ : GripAction(start-resize) \n\ : GripAction(do-resize) \n\ : GripAction(end-resize) \n\ : GripAction(start-move) \n\ : GripAction(do-move) \n\ : GripAction(end-move)"; static void ModifyCB(Widget w, XtPointer client_data, XtPointer call_data); static void ShowGrips(TeXPictObj *obj); static void ShowGrip(GripData *grip); static void RemoveGrips(TeXPictType type); static void OffsetGrips(int x_off, int y_off); static TeXPictObj *SelectNew(int x, int y); void ModifyInit(void) { Arg arg; int i; resizeTrans = XtParseTranslationTable(resizeTransTable); XtSetArg(arg, XtNtranslations, (XtArgVal) resizeTrans); for (i = 0; i < XtNumber(gd); i++) { gd[i].wid = XtCreateWidget("grip", gripWidgetClass, pboard, &arg, 1); XtAddCallback(gd[i].wid, XtNcallback, ModifyCB, (XtPointer) &gd[i]); XtRealizeWidget(gd[i].wid); } /* retrieve dimensions of one grip, all grips should be equally sized */ XtVaGetValues(gd[0].wid, XtNwidth, (XtArgVal) &gripWidth, XtNheight, (XtArgVal) &gripHeight, XtNborderWidth, (XtArgVal) &gripBW, NULL); gripXOff = gripWidth/2 + gripBW; gripYOff = gripHeight/2 + gripBW; changeMade = False; selected = NULL; RegisterOffset(OffsetGrips); } void PrepareSelect(void) { RemoveGrips(-1); selected = NULL; } void SelectObj(int x, int y) { TeXPictObj *obj; if (objList == NULL) return; obj = SelectNew(x, y); if (obj == selected) return; if (selected != NULL) { RemoveGrips(obj == NULL ? -1 : obj->type); } selected = obj; if (selected == NULL) return; ShowGrips(obj); } /*ARGSUSED*/ void DeleteCB(Widget w, XtPointer client_data, XtPointer call_data) { TeXPictObj *o; if (objList == NULL || selected == NULL) { XBell(XtDisplay(pboard), 0); return; } RemoveGrips((TeXPictType) -1); if (selected == objList) { objList = objList->next; } else { for (o = objList; o->next != selected; o = o->next) ; o->next = selected->next; } ObjDestroy(selected); selected = NULL; changeMade = True; /* redraw */ if (XtIsRealized(pboard)) XClearArea(XtDisplay(pboard), XtWindow(pboard), 0, 0, 0, 0, True); } /*ARGSUSED*/ void CopyCB(Widget w, XtPointer client_data, XtPointer call_data) { TeXPictObj *o; if (objList == NULL || selected == NULL) { XBell(XtDisplay(pboard), 0); return; } RemoveGrips((TeXPictType) -1); /* create a new object */ o = ObjCreate(selected->type); o->next = objList; objList = o; /* copy data */ ObjCopy(selected, o); /* offset new object by (+10,+10) */ ObjOffset(o, 10, 10); selected = o; ShowGrips(o); ObjDraw(o, XtDisplay(pboard), XtWindow(pboard)); changeMade = True; } void RemoveAllGrips(void) { selected = NULL; RemoveGrips(-1); } static void ModifyCB(Widget w, XtPointer client_data, XtPointer call_data) { GripCallData gcd = (GripCallData) call_data; GripData *grip = (GripData *) client_data; int x, y, dx, dy, i; XPoint newPoint; Display *disp = XtDisplay(pboard); Window win = XtWindow(pboard); if (gcd->num_params == 0) return; switch (gcd->event->type) { case ButtonPress: case ButtonRelease: x = gcd->event->xbutton.x; y = gcd->event->xbutton.y; break; case KeyPress: case KeyRelease: x = gcd->event->xkey.x; y = gcd->event->xkey.y; break; case MotionNotify: x = gcd->event->xmotion.x; y = gcd->event->xmotion.y; break; case EnterNotify: case LeaveNotify: x = gcd->event->xcrossing.x; y = gcd->event->xcrossing.y; break; default: /* other events don't have x and y */ return; } if (strncmp(gcd->params[0], "start", 5) == 0) { /* %%%% ObjErase, followed by ObjRubber should come here */ lastPos.x = x; lastPos.y = y; ref.x = grip->points[grip->point].x; ref.y = grip->points[grip->point].y; return; } if (strncmp(gcd->params[0], "do", 2) == 0) { ref.x += x - lastPos.x; ref.y += y - lastPos.y; newPoint = ref; SnapPosition(&newPoint.x, &newPoint.y); if (ref.x != grip->points[grip->point].x || ref.y != grip->points[grip->point].y) { /* redraw necessary */ ObjRubber(grip->obj->type, disp, win, grip->points, grip->set_points); if (strcmp(gcd->params[0] + 3, "resize") == 0) { grip->points[grip->point] = newPoint; ObjValidate(grip->obj->type, grip->points, grip->set_points, grip->point); } else { /* "move" */ dx = newPoint.x - grip->points[grip->point].x; dy = newPoint.y - grip->points[grip->point].y; for (i = 0; i < grip->set_points; i++) { grip->points[i].x += dx; grip->points[i].y += dy; } } ObjRubber(grip->obj->type, disp, win, grip->points, grip->set_points); } lastPos.x = x; lastPos.y = y; return; } if (strncmp(gcd->params[0], "end", 3) == 0) { ObjRubber(grip->obj->type, disp, win, grip->points, grip->set_points); ObjStore(grip->obj, grip->points, grip->set_points); ObjDraw(grip->obj, disp, win); ShowGrips(grip->obj); changeMade = True; return; } } static void ShowGrips(TeXPictObj *obj) { int n_points; switch(obj->type) { case TeXLine: case TeXVector: case TeXBezier: case TeXText: ObjRetrieve(obj, points1, &n_points); gripStart.obj = gripEnd.obj = gripCurv.obj = obj; gripStart.set_points = 1; ShowGrip(&gripStart); if (n_points > 1) { ShowGrip(&gripEnd); gripStart.set_points = gripEnd.set_points = 2; } if (n_points > 2) { ShowGrip(&gripCurv); gripStart.set_points = gripEnd.set_points = gripCurv.set_points = 3; } break; case TeXDisc: case TeXCircle: ObjRetrieve(obj, points1, &n_points); gripEnd.obj = obj; gripEnd.set_points = 2; ShowGrip(&gripEnd); break; case TeXFramedText: case TeXDashedText: case TeXFilled: case TeXOval: ObjRetrieve(obj, points1, &n_points); /* points1 is UL -> LR; calculate UR -> LL into points2 */ points2[0].x = points1[1].x; points2[0].y = points1[0].y; points2[1].x = points1[0].x; points2[1].y = points1[1].y; gripUL.obj = gripUR.obj = gripLL.obj = gripLR.obj = obj; ShowGrip(&gripUL); ShowGrip(&gripUR); ShowGrip(&gripLL); ShowGrip(&gripLR); break; default: ; } } static void ShowGrip(GripData *grip) { XtMoveWidget(grip->wid, grip->points[grip->point].x - gripXOff, grip->points[grip->point].y - gripYOff); XtMapWidget(grip->wid); } /* remove grips if they will not be used by the specified type */ /* %%%% currently removes all grips */ static void RemoveGrips(TeXPictType type) { int i; for (i = 0; i < XtNumber(gd); i++) { XtUnmapWidget(gd[i].wid); } } static void OffsetGrips(int x_off, int y_off) { int i; for (i = 0; i < MAX_POINTS; i++) { points1[i].x += x_off; points1[i].y += y_off; } for (i = 0; i < MAX_POINTS; i++) { points2[i].x += x_off; points2[i].y += y_off; } for (i = 0; i < XtNumber(gd); i++) { XtMoveWidget(gd[i].wid, gd[i].points[gd[i].point].x - gripXOff, gd[i].points[gd[i].point].y - gripYOff); } } static TeXPictObj *SelectNew(int px, int py) { /* We want the following behavior: When several objects are stacked, pressing the select button (button 1) shall cycle through the stack. This is done as follows: We search for the first object which matches the specified coordinates. Then we go through the database to see if the currently selected object would also be a possible selection for the specified coordinates. If it is not, we are done. Otherwise, we search for the next object to be selected "behind" the currently selected one. (If there is none, the one found above is okay, i.e. the cycle begins again.) */ TeXPictObj *firstMatch, *obj; Boolean selectedMatches; /* True if current selection matches */ float x, y; /* zoomed %%%% px, py */ x = (float) px; y = (float) py; firstMatch = (TeXPictObj *) 0; selectedMatches = False; for (obj = objList; obj; obj = obj->next) { if (ObjMatches(obj, x, y)) { if (selectedMatches) { break; /* ASSERT: obj != current */ } if (!firstMatch) { firstMatch = obj; } if (obj == selected) { selectedMatches = True; } } } if (selectedMatches) { return obj ? obj : firstMatch; } else { return firstMatch; } }