/* * GRAPH, Version 1.00 - 4 August 1989 * * Copyright 1989, David Gay. All Rights Reserved. * This software is freely redistrubatable. */ #include #include #include #include #include #include "object.h" #include "object/default.h" #include "file.h" #include "graph.h" #include "uio.h" #include "coords.h" #include "grph.h" #include "user/gadgets.h" #include "graphics.h" #include "tracker.h" #include #include #include #define LABELLEN 256 #define LABELHEIGHT 10 #define LABELFONT "topaz.font" #define FONTLIST 2 /* The various justification possibilities */ enum justify { top, centre, bottom }; /* (private) class label, inherits from object */ struct label { struct object o; char text[LABELLEN]; /* What to display, \ for newlines */ BYTE colour; char fname[FONTLEN]; /* font name */ int fheight; /* font height */ double x, y; /* text position */ double dx, dy; /* offset from position, in inches */ enum justify jx, jy; /* justification (x & y axes) */ char selected; /* Temp, etc vars */ char broken[LABELLEN]; /* Text, separated into lines (separated by NULLs) */ int nblines; /* number of lines */ struct Rectangle extent; /* Size of rectangle taken by text */ int bw, bh; /* Size of bitmap (for moving) */ struct Rectangle lims; /* Rectangle taken up on screen */ int x0, y0; /* x & y in screen coords (wthout ddx, ddy) */ struct TextFont *font, *newfont; /* the font (newfont for inform/confirm) * / double oldx, oldy; /* position before move */ int ddx, ddy; /* dx, dy in pixels */ struct Region *ref; struct RastPort bkgd; /* Rastport & bitmap for moves */ struct BitMap bkgd_bm; }; /*-------------------------------------------------------------------------*/ /* label class implementation */ /*-------------------------------------------------------------------------*/ /* Allocates bitmap for saving background while moving */ /* Assumes 2 bit planes */ static int alloc_bkgd_label(struct label *this) { BYTE *data; if (data = AllocMem(2 * RASSIZE(this->bw, this->bh), MEMF_CHIP)) { InitBitMap(&this->bkgd_bm, 2, this->bw, this->bh); this->bkgd_bm.Planes[0] = (PLANEPTR)data; this->bkgd_bm.Planes[1] = (PLANEPTR)(data + RASSIZE(this->bw, this->bh) ); InitRastPort(&this->bkgd); this->bkgd.BitMap = &this->bkgd_bm; } return data != 0; } /* Frees bitmap alloced above */ static void free_bkgd_label(struct label *this) { FreeMem(this->bkgd_bm.Planes[0], 2 * RASSIZE(this->bw, this->bh)); } /* Save scrren background into temp space */ static void save_bkgd_label(struct label *this) { ClipBlit(this->o.g->io.rw->rp, this->lims.MinX, this->lims.MinY, &this->bkg d, 0, 0, this->bw, this->bh, 0xc0); } /* Copies saved background back to the screen */ static void restore_bkgd_label(struct label *this) { ClipBlit(&this->bkgd, 0, 0, this->o.g->io.rw->rp, this->lims.MinX, this->li ms.MinY, this->bw, this->bh, 0xc0); } /* Do all the precalculations */ static void setup_label(struct label *this) { struct TextFont *font = this->font; char *srch, *nl; this->ddx = xinch2dots(this->o.g, this->dx); this->ddy = yinch2dots(this->o.g, this->dy); /* Break up label & calculate rectangle used */ srch = strcpy(this->broken, this->text); this->nblines = 0; this->extent.MaxX = 0; this->extent.MinX = INT_MAX; do { struct TextExtent te; nl = strchr(srch, '\\'); if (nl && *(nl + 1) == '\\') /* add single slash */ movmem(nl + 1, nl, LABELLEN - (nl - this->broken) - 1); else { this->nblines++; if (nl) *nl = '\0'; /* Check space for this line */ TextExtent(srch, font, &te); if (te.te_Extent.MaxX > this->extent.MaxX) this->extent.MaxX = te.t e_Extent.MaxX; if (te.te_Extent.MinX < this->extent.MinX) this->extent.MinX = te.t e_Extent.MinX; } srch = nl + 1; } while (nl); this->extent.MinY = 0; this->extent.MaxY = this->nblines * font->tf_YSize; this->bw = this->extent.MaxX - this->extent.MinX + 1; this->bh = this->extent.MaxY - this->extent.MinY + 1; } /* Work out rectangle used by text */ static void calc_rect(struct label *this) { struct graph *g = this->o.g; struct RWindow *rw = g->io.rw; int delta, x0, y0; /* Find origin */ /* Default */ x0 = (this->x0 = ftol(rw->sx(rw, this->x))) + this->ddx; y0 = (this->y0 = ftol(rw->sy(rw, this->y))) + this->ddy; /* Adjust for justification */ switch (this->jx) { case centre: x0 -= this->bw / 2; break; case bottom: /* right */ x0 -= this->bw; break; } switch (this->jy) { case centre: y0 -= this->bh / 2; break; case bottom: /* right */ y0 -= this->bh; break; } /* Extent defines actual rectangle */ this->lims.MinX = x0 + this->extent.MinX; this->lims.MaxX = x0 + this->extent.MaxX; this->lims.MinY = y0 + this->extent.MinY; this->lims.MaxY = y0 + this->extent.MaxY; /* Make sure that it stays visible */ /* Could one reduce the number of flops ? */ /* Idea: Generalise inform/confirm */ if ((delta = ftol(rw->sx(rw, g->a.x.min)) - this->lims.MinX) > 0 || (delta = ftol(rw->sx(rw, g->a.x.max)) - this->lims.MaxX) < 0) { this->x0 += delta; this->lims.MinX += delta; this->lims.MaxX += delta; } if ((delta = ftol(rw->sy(rw, g->a.y.max)) - this->lims.MinY) > 0 || (delta = ftol(rw->sy(rw, g->a.y.min)) - this->lims.MaxY) < 0) { this->y0 += delta; this->lims.MinY += delta; this->lims.MaxY += delta; } } /* Add the current position to the region */ static void add_region(struct label *this) { if (!this->ref) this->ref = NewRegion(); if (this->ref && !OrRectRegion(this->ref, &this->lims)) { DisposeRegion(this->ref); this->ref = NULL; } if (!this->ref) nomem(this->o.g->io.win); } /* Were we clicked on ? */ static int down_label(struct label *this) { struct graph *g = this->o.g; WORD sx = ftol(g->io.rw->sx(g->io.rw, g->s.x)); WORD sy = ftol(g->io.rw->sy(g->io.rw, g->s.y)); int inside; calc_rect(this); inside = sx >= this->lims.MinX && sx <= this->lims.MaxX && sy >= this->lims.MinY && sy <= this->lims.MaxY; if (inside) { /* Setup internal info for move */ this->o.mx = sx - this->x0; this->o.my = sy - this->y0; if (alloc_bkgd_label(this)) { save_bkgd_label(this); if (this->selected) { SetDrMd(&this->bkgd, COMPLEMENT); RectFill(&this->bkgd, 0, 0, this->bw - 1, this->bh - 1); } this->ref = NULL; add_region(this); /* Original pos. will probably need refreshing */ this->oldx = this->x; this->oldy = this->y; } else /* failed, no mem */ { nomem(this->o.g->io.win); inside = FALSE; } } return inside; } /* Reverse label's rectangle (eg for selection) */ static void reverse_label(struct label *this) { struct RastPort *rp = this->o.g->io.rw->rp; SetDrMd(rp, COMPLEMENT); RectFill(rp, this->lims.MinX, this->lims.MinY, this->lims.MaxX, this->lims. MaxY); } /* Draw label */ static void draw_label(struct label *this, int allow_mes) { struct graph *g = this->o.g; struct RastPort *rp = g->io.rw->rp; char *str; int i; calc_rect(this); SetAPen(rp, this->colour); SetDrMd(rp, JAM1); SetFont(rp, this->font); /* Draw all the lines of text */ Move(rp, this->lims.MinX - this->extent.MinX, this->lims.MinY - this->exten t.MinY + this->font->tf_Baseline); for (str = this->broken, i = this->nblines; i; i--) { int l = strlen(str); Text(rp, str, l); Move(rp, this->lims.MinX - this->extent.MinX, rp->cp_y + this->font->tf _YSize); str += l + 1; /* Onto next string */ } if (this->selected) reverse_label(this); } /* We're now selected */ static void select_label(struct label *this) { this->selected = TRUE; if (this->o.g->ok && this->o.g->io.rw) { calc_rect(this); reverse_label(this); } } /* A quick walk around town ... */ static void move_label(struct label *this) { restore_bkgd_label(this); /* erase label */ this->x = this->o.g->s.x; this->y = this->o.g->s.y; calc_rect(this); save_bkgd_label(this); /* Save background */ draw_label(this, TRUE); /* & draw at new pos */ } /* Mouse buttonb released, refresh needed ? */ static struct Region *up_label(struct label *this) { restore_bkgd_label(this); /* Restore at last pos */ this->x = this->o.g->s.x; this->y = this->o.g->s.y; calc_rect(this); free_bkgd_label(this); if (this->x != this->oldx || this->y != this->oldy) /* we moved */ { add_region(this); /* Refresh at new pos */ return this->ref; } else { draw_label(this, TRUE); /* Just redraw, no refresh needed */ DisposeRegion(this->ref); return NULL; } } /* End of selection, unhighlight */ static struct Region *deselect_label(struct label *this) { this->selected = FALSE; if (this->o.g->ok && this->o.g->io.rw) { calc_rect(this); reverse_label(this); } return NULL; } /* Handle edit requester */ int edit_handler(struct Gadget *gg, ULONG class, struct Requester *req, struct graph *g) { if (gg->GadgetID == FONTLIST) /* User played with font list */ { if (ModifyList(gg, req, req->RWindow, class == GADGETUP) == 2) { /* Double click -> exit */ EndRequest(req, req->RWindow); return TRUE; } return FALSE; } else return std_ghandler(gg, class, req, g); } /* Allow user to edit us */ static int edit_label(struct label *this, struct Region **ref) { struct Requester *req; struct Memory *m; struct Gadget *gl = NULL; int ret = FALSE; char text[LABELLEN], dx[NBLEN], dy[NBLEN], x[NBLEN], y[NBLEN], fname[FONTLE N], fheight[INTLEN], colour[INTLEN]; struct Gadget *leftg, *ctrxg, *rightg, *topg, *ctryg, *bottomg; this->ref = NULL; /* Create requester */ strcpy(text, this->text); double2str(dx, this->dx); double2str(dy, this->dy); double2str(x, this->x); double2str(y, this->y); strcpy(fname, this->fname); remfont(fname); int2str(fheight, this->fheight); int2str(colour, this->colour); if ((m = NewMemory()) && (req = InitReq(50, 15, 395, 166, m)) && SetReqBorder(req, 1, m) && AddIntuiText(&req->ReqText, "Edit Label", 158, 6, m) && AddText(&gl, 0, "Text ", FALSE, text, LABELLEN, TRUE, 0, RELVERIFY, 49, 20, 335, 10, TRUE, m) && AddText(&gl, 0, "X = ", FALSE, x, NBLEN, TRUE, 0, RELVERIFY, 43, 40, 56 , 10, TRUE, m) && AddText(&gl, 0, "dX = ", FALSE, dx, NBLEN, TRUE, 0, RELVERIFY, 147, 40, 56, 10, TRUE, m) && (leftg = AddRadio(&gl, 0, "left", TRUE, SELECTED * (this->jx == top), R ELVERIFY, 16 + 32, 210, 40, 10, 10, m)) && (ctrxg = AddRadio(&gl, 0, "centre", TRUE, SELECTED * (this->jx == centr e), RELVERIFY, 8 + 32, 260, 40, 10, 10, m)) && (rightg = AddRadio(&gl, 0, "right", TRUE, SELECTED * (this->jx == botto m), RELVERIFY, 16 + 8, 326, 40, 10, 10, m)) && AddText(&gl, 0, "Y = ", FALSE, y, NBLEN, TRUE, 0, RELVERIFY, 43, 60, 56 , 10, TRUE, m) && AddText(&gl, 0, "dY = ", FALSE, dy, NBLEN, TRUE, 0, RELVERIFY, 147, 60, 56, 10, TRUE, m) && (topg = AddRadio(&gl, 0, "top", TRUE, SELECTED * (this->jy == top), REL VERIFY, 512 + 1024, 210, 60, 10, 10, m)) && (ctryg = AddRadio(&gl, 0, "centre", TRUE, SELECTED * (this->jy == centr e), RELVERIFY, 256 + 1024, 260, 60, 10, 10, m)) && (bottomg = AddRadio(&gl, 0, "bottom", TRUE, SELECTED * (this->jy == bot tom), RELVERIFY, 256 + 512, 326, 60, 10, 10, m)) && AddText(&gl, 0, "Colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 2 36, 80, 32, 10, TRUE, m) && AddText(&gl, 0, "Size ", FALSE, fheight, INTLEN, TRUE, 0, RELVERIFY, 22 0, 150, 32, 10, TRUE, m) && AddList(&gl, FONTLIST, "Font", &flist, fname, FONTLEN, 0, RELVERIFY, 11 , 80, 160, 80, TRUE, m) && AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 255, 100, 65, 15, FAL SE, m) && AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 255, 130, 65, 15 , FALSE, m)) { SetReqGadgets(req, gl); if (ret = DoRequest(req, this->o.g, edit_handler) && *stpblk(text) != ' \0') { /* Update label */ double new; int fh; struct TextFont *newfont; add_region(this); /* Refresh needed at old pos */ /* New justification, etc */ if ((this->colour = str2int(colour)) == INOVAL) this->colour = 1; strcpy(this->text, text); if ((new = str2double(x)) != NOVAL) this->x = new; if ((new = str2double(y)) != NOVAL) this->y = new; if ((this->dx = str2double(dx)) == NOVAL) this->dx = 0.0; if ((this->dy = str2double(dy)) == NOVAL) this->dy = 0.0; if (leftg->Flags & SELECTED) this->jx = top; else if (ctrxg->Flags & SELECTED) this->jx = centre; else this->jx = bottom; if (topg->Flags & SELECTED) this->jy = top; else if (ctryg->Flags & SELECTED) this->jy = centre; else this->jy = bottom; /* Check font */ addfont(fname); if ((fh = str2int(fheight)) == INOVAL || fh <= 0) fh = LABELHEIGHT; if (newfont = open_font(this->o.g, fname, fh, 0, 0)) { strcpy(this->fname, fname); this->fheight = fh; CloseFont(this->font); this->font = newfont; } else message(this->o.g, "Invalid font specified", (char *)NULL); /* Do precalculation */ setup_label(this); calc_rect(this); add_region(this); /* New position needs refreshing */ } } Free(m); *ref = this->ref; return ret; } /* No vsriables used in labels */ int var_change_label(struct label *this, char *name) { return FALSE; } /* Confirm changes made */ static void confirm_label(struct label *this, int ok) { if (ok) { CloseFont(this->font); this->font = this->newfont; setup_label(this); } else CloseFont(this->newfont); /* failed */ } /* Resolution has changed */ static int inform_label(struct label *this, int ok) { if (this->newfont = open_font(this->o.g, this->fname, this->fheight, 0, 0)) return TRUE; message(this->o.g, "Couldn't open font", (char *)NULL); return FALSE; } /* Write label to file */ static int save_label(struct label *this, FILE *f) { short tag = LABEL_TAG; short end = LABEL_END; return WRITE(f, tag) && WRITE(f, this->text) && WRITE(f, this->colour) && WRITE(f, this->fname) && WRITE(f, this->fheight) && WRITE(f, this->x) && WRITE(f, this->y) && WRITE(f, this->dx) && WRITE(f, this->dy) && WRITE(f, end); } /* free label */ static struct Region *delete_label(struct label *this) { struct Region *ref; /* Label's position will need refreshing */ this->ref = NULL; if (this->o.g->ok && this->o.g->io.rw) add_region(this); ref = this->ref; if (this->font) CloseFont(this->font); FreeMem(this, sizeof(struct label)); return ref; } /* Initialise label structure */ static struct label *make_label(struct graph *g) { struct label *this = AllocMem(sizeof(struct label), 0L); const static struct label def_l = { { { NULL }, NULL, "", TRUE, 0, 0, (void *)delete_label, (void *)select_label, (void *)deselect_label, (void *)down_label, (void *)move_label, (void *)up_label, (void *)edit_label, (void *)draw_label, (void *)notdone, (void *)notdone, (void *)var_change_label, (void *)save_label, (void *)inform_label, (void *)confirm_label }, "", 1, LABELFONT, LABELHEIGHT, NOVAL, NOVAL, 0.0, 0.0, FALSE }; if (this) { *this = def_l; this->o.g = g; /* Default font *must* be there */ if (this->font = open_font(g, LABELFONT, LABELHEIGHT, 0, 0)) { return this; } else message(g, "Couldn't open font", (char *)NULL); FreeMem(this, sizeof(struct label)); } else message(g, "No memory !", (char *)NULL); return NULL; } /* Load label from file */ struct label *load_label(struct graph *g, FILE *f) { struct label *this = make_label(g); if (this) { short tag; if (READ(f, this->text) && READ(f, this->colour) && READ(f, this->fname) && READ(f, this->fheight) && READ(f, this->x) && READ(f, this->y) && READ(f, this->dx) && READ(f, this->dy) && READ(f, tag) && tag == LABEL_END) { struct TextFont *newfont; if (newfont = open_font(this->o.g, this->fname, this->fheight, 0, 0 )) { CloseFont(this->font); this->font = newfont; } else /* Lack of font isn't drastic */ { message(g, "No such font available", this->fname, (char *)NULL) ; strcpy(this->fname, LABELFONT); } setup_label(this); return this; } delete_label(this); } return NULL; } /* Create a new label, at pos. (x,y). Asks user for text */ struct label *new_label(struct graph *g, double x, double y) { struct label *this = make_label(g); if (this) { /* Create requester */ struct Requester *req; struct Memory *m; struct Gadget *gl = NULL; int ret = FALSE; this->x = x; this->y = y; if ((m = NewMemory()) && (req = InitReq(50, 20, 160, 65, m)) && SetReqBorder(req, 1, m) && AddIntuiText(&req->ReqText, "Add Label", 44, 6, m) && AddText(&gl, TRUE, "Text ", FALSE, this->text, LABELLEN, TRUE, 0, R ELVERIFY | ENDGADGET, 49, 20, 100, 10, TRUE, m) && AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 8, 40, 65, 15, FA LSE, m) && AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 88, 40, 65, 15, FALSE, m)) { SetReqGadgets(req, gl); if (ret = DoRequest(req, g, std_ghandler) && *stpblk(this->text) != '\0') { /* Create it */ setup_label(this); if (g->ok && g->io.rw) draw_label(this, TRUE); Free(m); return this; } } Free(m); delete_label(this); } else message(g, "No memory !", (char *)NULL); return NULL; }