/* File:colcontrol.c * Author: Simon Kelley (srk@sanger.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1994 * ------------------------------------------------------------------- * Acedb 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * Acedb 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * Description: package for generic map drawing containing: * columns cotrol * zoom, middle button scroll, locator * findScaleUnits * multiple Map support * column data abstraction * Exported functions: * HISTORY: * Last edited: Jul 30 09:38 2003 (edgrif) * * Oct 13 14:30 1998 (edgrif): Replaced ACEDB defines with function * calls. Removed some acedb specific code to acedbgraph.c * Created: Wed Mar 20 13:40:35 1992 (srk) * CVS info: $Id: colcontrol.c,v 1.88 2003/08/01 08:38:19 edgrif Exp $ *------------------------------------------------------------------- */ #include #include #include #include /************************************************************/ magic_t GRAPH2COLCONTROL_ASSOC = "COLCONTROL"; magic_t GRAPH2COLCONTROL_LOCALS_ASSOC = "COLCONTROL_LOCALS"; static magic_t COLCONTROL_MAGIC = "COLCONTROL"; static magic_t MAPCONTROL_MAGIC = "MAPCONTROL"; /************************************************************/ static void controlDestroyed(void); static void controlLeftDrag(double x, double y); static void controlLeftUp(double x, double y); static void controlPick(int box, double x, double y, int modifier_unused); static void controlSelect(int box, double x, double y); static void controlKybd(int k, int modifier_unused); static void controlMiddleDrag(double x, double y); static void controlMiddleUp(double x, double y); static void controlMiddleDown(double x, double y); /* Used to assign colours to maps */ static int colourOrder[] = { GREEN, YELLOW, CYAN, MAGENTA, LIGHTGRAY }; static COLOUROPT ColButton = { viewWindowCreate, 0, BLACK, WHITE, "Views...", 0 }; /************************************************************/ /* redo the configure routine to be non blocking and use RD's editor routines */ static void controlConfigOK(void) { COLINSTANCE instance; void *locals; if(graphCheckEditors(graphActive(),TRUE)) { graphAssFind(&GRAPH2COLCONTROL_ASSOC, &instance); graphAssFind(&GRAPH2COLCONTROL_LOCALS_ASSOC, &locals); (*(instance->configFinal))(instance, locals, TRUE); /* FALSE in KO case */ instance->configGraph = 0; graphDestroy(); controlDrawControl(instance->map->control); } } /* controlConfigOK */ static void controlConfigKO(void) { COLINSTANCE instance; void *locals; if(!graphAssFind(&GRAPH2COLCONTROL_ASSOC, &instance)) printf("Could not find instance associated with GRAPH2COLCONTROL_ASSOC\n"); if(!graphAssFind(&GRAPH2COLCONTROL_LOCALS_ASSOC, &locals)) printf("Could not find locals associated with GRAPH2COLCONTROL_LOCALS_ASSOC\n"); (*(instance->configFinal))(instance, locals, FALSE); /* FALSE in KO case */ instance->configGraph = 0; graphDestroy(); } /* controlConfigKO */ static void controlConfigApply(void) { COLINSTANCE instance; void *locals; if(!graphAssFind(&GRAPH2COLCONTROL_ASSOC, &instance)) printf("Could not find instance associated with GRAPH2COLCONTROL_ASSOC\n"); if(!graphAssFind(&GRAPH2COLCONTROL_LOCALS_ASSOC, &locals)) printf("Could not find locals associated with GRAPH2COLCONTROL_LOCALS_ASSOC\n"); (*(instance->configFinal))(instance, locals, TRUE); /* FALSE in KO case */ controlDrawControl(instance->map->control); /* instance->configGraph = 0; graphDestroy();*/ } /* controlConfigApply */ Graph controlCreateConfig(COLINSTANCE instance, void *locals,char *text, float width, float height) { Graph g; int width2,height2; if (instance->configGraph) { graphActivate(instance->configGraph); graphPop(); return 0; } g = graphCreate(TEXT_SCROLL,text,0.5,0.4, width, height); instance->configGraph = g; graphAssociate(&GRAPH2COLCONTROL_ASSOC, instance); graphAssociate(&GRAPH2COLCONTROL_LOCALS_ASSOC, locals); graphRegister(DESTROY,controlConfigKO); graphFitBounds(&width2,&height2); graphButton("Apply",controlConfigApply,(float)(width2/2.0)-7.0,height2 - 2.0); graphButton("OK",controlConfigOK,(float)(width2/2.0),height2 - 2.0); graphButton("Cancel",graphDestroy,(float)(width2/2.0)+4.0,height2 - 2.0); return g; } /* There used to be on colControlCreate which had different function */ /* signatures and code according to whether it was compiled with ACEDB or */ /* not. I have now split this into two functions with some duplicated code */ /* which will be gradually be unified.... */ /* */ /* This is the form for ACEDB type code that is going to create its own */ /* display. */ /* */ /* NOTE that this routine is not going to work well unless the application */ /* has registered a displayCreate routine to set up the graph, the default */ /* action is to attempt to display something. */ /* */ COLCONTROL colControlCreate(BOOL isoldgraph, char *name, char *dispName) { int gw, gh; STORE_HANDLE handle = handleCreate(); COLCONTROL control = (COLCONTROL)halloc(sizeof (struct ColControlStruct), handle); if (isoldgraph) { controlDestroyed(); graphClear(); control->graph = graphActive(); } else { /* ACEDB-GRAPH INTERFACE: if display function registered, call it, */ /* otherwise do our own, note that if no function is registered then an */ /* educated guess is made about window size. */ if (getGraphAcedbDisplayCreate() != NULL) control->graph = (getGraphAcedbDisplayCreate())(dispName) ; else control->graph = graphCreate(TEXT_FIT, name, 0.5, 0.5, 0, 0); if (!control->graph) { handleDestroy(handle); return 0; } graphRegister(RESIZE, controlDraw); graphRegister(DESTROY, controlDestroyed); graphRegister(KEYBOARD, controlKybd); graphRegister(PICK, controlPick); graphRegister(MIDDLE_DOWN, controlMiddleDown); } graphRetitle(name); /* Different code */ graphAssociate(&GRAPH2COLCONTROL_ASSOC, control); /* link this map to the window */ control->magic = &COLCONTROL_MAGIC ; control->handle = handle; control->instances = arrayHandleCreate (50, COLINSTANCE, handle); control->boxIndex = arrayHandleCreate(100, COLINSTANCE, handle); control->boxIndex2 = arrayHandleCreate(100, void *, handle); control->maps = arrayHandleCreate (4, MAPCONTROL, handle) ; control->mapTransitions = arrayHandleCreate(100, float, handle); control->bottomBoxes = assHandleCreate(handle); control->currentMap = 0; control->activeBox = 0; control->activeKey = 0; /* Different code */ control->activeInstance = 0; control->viewWindow = 0; control->hideHeader = FALSE; graphFitBounds(&gw, &gh); /* returns ints */ control->graphWidth = gw; /* make floats */ control->graphHeight = gh; return control ; } /* This routine is essentially the same as the one above with a few */ /* differences - labelled in the routine above. */ /* Only coltest.c uses this form of the call within the entire CVS_ACEDB*/ /* tree....if someone outside uses it they can simply change their call */ /* to colControlBasicCreate and all should work. */ /* */ COLCONTROL colControlBasicCreate(BOOL isoldgraph, int type, char *name, float x, float y, float w, float h) { int gw, gh; STORE_HANDLE handle = handleCreate(); COLCONTROL control = (COLCONTROL)halloc(sizeof (struct ColControlStruct), handle); if (isoldgraph) { controlDestroyed(); graphClear(); control->graph = graphActive(); } else { control->graph = graphCreate(type, name, x, y, w, h); if (!control->graph) { handleDestroy(handle); return 0; } graphRegister(RESIZE, controlDraw); graphRegister(DESTROY, controlDestroyed); graphRegister(KEYBOARD, controlKybd); graphRegister(PICK, controlPick); graphRegister(MIDDLE_DOWN, controlMiddleDown); } graphAssociate(&GRAPH2COLCONTROL_ASSOC, control); /* link this map to the window */ control->magic = &COLCONTROL_MAGIC ; control->handle = handle; control->instances = arrayHandleCreate (50, COLINSTANCE, handle); control->boxIndex = arrayHandleCreate(100, COLINSTANCE, handle); control->boxIndex2 = arrayHandleCreate(100, void *, handle); control->maps = arrayHandleCreate (4, MAPCONTROL, handle) ; control->mapTransitions = arrayHandleCreate(100, float, handle); control->bottomBoxes = assHandleCreate(handle); control->currentMap = 0; control->activeBox = 0; control->activeInstance = 0; control->viewWindow = 0; control->hideHeader = FALSE; graphFitBounds(&gw, &gh); /* returns ints */ control->graphWidth = gw; /* make floats */ control->graphHeight = gh; return control; } /* Do the common parts of map creation. The caller must fill in lots more */ /* fields in the map structure before calling controlAddMap() */ /* */ MAPCONTROL mapControlCreate(COLCONTROL control, void (*finaliseFunc)(void *p)) { STORE_HANDLE handle = handleHandleCreate(control->handle); MAPCONTROL map; map = (MAPCONTROL)halloc(sizeof(struct MapControlStruct), handle); blockSetFinalise (map, finaliseFunc); map->handle = handle; map->magic = &MAPCONTROL_MAGIC; map->name = ""; /* default */ map->menu = 0; /* default */ /* ACEDB-GRAPH INTERFACE: set acedb specific map field. */ if (getGraphAcedbResetKey() != NULL) (getGraphAcedbResetKey())(&(map->viewKey)) ; map->beforeDraw = 0; map->drawHeader = 0; map->headerPick = 0; map->convertTrans = 0; map->keyboard = 0; map->topMargin = 0; map->permConversionRoutines = arrayHandleCreate(50, struct ConversionRecord, handle); map->transConversionRoutines = arrayHandleCreate(50, struct ConversionRecord, handle); map->buttons = 0; map->buttonsAddHere = &map->buttons; map->menusOnButtons = arrayHandleCreate(10, MENUOPT *, handle); map->suppressed = FALSE; map->submenus = FALSE; map->cambridgeOptions = TRUE; map->noButtons = FALSE; map->cursor.unit = 1; /* allows calls to set cursor without a scale column */ return map; } COLCONTROL currentColControl(char *caller) { COLCONTROL control; if (!(graphAssFind(&GRAPH2COLCONTROL_ASSOC, &control))) messcrash("%s() could not find COLCONTROL on graph", caller); if (!control) messcrash("%s() received NULL COLCONTROL pointer", caller); if (control->magic != &COLCONTROL_MAGIC) messcrash("%s() received non-magic COLCONTROL pointer", caller); return control; } /* currentColControl */ MAPCONTROL currentMapControl(void) { MAPCONTROL map = currentColControl("currentMapControl")->currentMap; char *caller = "currentMapControl"; if (!map) messcrash("%s() received NULL MAPCONTROL pointer", caller); if (map->magic != &MAPCONTROL_MAGIC) messcrash("%s() received non-magic MAPCONTROL pointer", caller); return map; } /* currentMapControl */ void controlAddMap(COLCONTROL control, MAPCONTROL map) { COLPROTO proto, *protop; int c,m,i; /* find an unused Colour */ for (c=0; colourOrder[c] != LIGHTGRAY; c++) { for (m=0; mmaps); m++) if (arr(control->maps, m, MAPCONTROL)->colour == colourOrder[c]) break; if (m == arrayMax(control->maps)) break; } map->colour = colourOrder[c]; array(control->maps, arrayMax(control->maps), MAPCONTROL) = map; map->control = control; controlAddButton(map, &ColButton, 0); /* the prototypes have map-private data fields, so we first make a private copy of the prototypes, we put this in an array for easy access. */ /* we also put the key correponding to the name in both the original, and copies, to match them again in conteolSetView and controlReadConfig */ map->protoArray = arrayHandleCreate(30, struct ProtoStruct, map->handle); for (protop = map->prototypes; *protop; protop++) { /* ACEDB-GRAPH INTERFACE: If acedb is registered record the name and key */ /* of this map. */ if (getGraphAcedbAddMap() != NULL) (getGraphAcedbAddMap())((*protop)->name, &(*protop)->key) ; array(map->protoArray, arrayMax(map->protoArray), struct ProtoStruct) = **protop; } for (i = 0; iprotoArray); i++) { proto = arrp(map->protoArray, i, struct ProtoStruct); /* call init routines, if they have them. */ if (proto->init) (*proto->init)(proto, map); /* now register any permanent conversion routines */ if (proto->conRoutine) { struct ConversionRecord *c; int i; for(i=0; ipermConversionRoutines); i++) { c = arrp(map->permConversionRoutines, i, struct ConversionRecord ); if (c->convertRoutine == proto->conRoutine && c->params == proto->convertParams) goto done; } /* new one here */ c = arrayp(map->permConversionRoutines, arrayMax(map->permConversionRoutines), struct ConversionRecord); c->convertRoutine = proto->conRoutine; c->params = proto->convertParams; done: proto->convertResults = &c->results; } } map->needConvert = TRUE; if (!control->currentMap) /* first one */ control->currentMap = map ; if (map->topMargin > control->realTopMargin) control->realTopMargin = map->topMargin; } void controlReadConfig(MAPCONTROL map, COLDEFAULT colInit) { int i= 0; while (colInit[i].proto) { int j; COLPROTO proto; COLINSTANCE instance = 0; /* have a reference to the original proto array, find correponding entry in the per-map copy */ for (j=0; jprotoArray); j++) { proto = arrp(map->protoArray, j, struct ProtoStruct); if (proto->key == (colInit[i].proto)->key) instance = controlInstanceCreate(proto, map, colInit[i].displayed, 0, colInit[i].name); } if (instance) array(map->control->instances, arrayMax(map->control->instances), COLINSTANCE) = instance ; i++; } } /* delete the instance from the map */ void controlDeleteInstance(COLCONTROL control, COLINSTANCE instance) { int i,j; Graph old = graphActive(); if (control->activeInstance == instance) { control->activeInstance = 0; /* ACEDB-GRAPH INTERFACE: reset acedb specific field. */ if (getGraphAcedbResetKey() != NULL) (getGraphAcedbResetKey())(&(control->activeKey)) ; } for (i=0,j=0; iinstances); i++) { COLINSTANCE instance1 = arr(control->instances, i, COLINSTANCE); if (instance1 != instance) arr(control->instances, j++, COLINSTANCE) = instance1; } arrayMax(control->instances) = j; if(instance->configGraph && old != instance->configGraph) { /* remove configure menu if it exists */ graphActivate(instance->configGraph); graphDestroy(); graphActivate(old); } handleDestroy(instance->handle); } /* delete a map from the display, if it's the last, destroy the window. */ void controlDeleteMap(COLCONTROL control, MAPCONTROL map) { int i, j; if (arrayMax(control->maps) == 1) { controlDestroy(control); return; } for (i=0; iinstances); i++) { COLINSTANCE instance = arr(control->instances, i, COLINSTANCE); if (instance->map == map) { controlDeleteInstance(control, instance); i--; /* above deletes one */ } } for (i=0, j=0; imaps); i++) { MAPCONTROL map1 = arr(control->maps, i, MAPCONTROL); if (map1 != map) arr(control->maps, j++, MAPCONTROL) = map1; } arrayMax(control->maps) = j; if (control->currentMap == map) /* pick another current map if needed */ control->currentMap = arr(control->maps, j-1, MAPCONTROL); handleDestroy(map->handle); controlDrawControl(control); } BOOL instanceExists(MAPCONTROL map, COLPROTO proto) { /* returns true if an instance of proto already exists on map */ int i; COLCONTROL control = map->control; COLINSTANCE instance; for (i=0; iinstances); i++) { instance =arr(control->instances, i, COLINSTANCE); if (instance->proto == proto && instance->map == map) return TRUE; } return FALSE; } COLINSTANCE controlInstanceCreate(COLPROTO proto, MAPCONTROL map, BOOL displayed, OBJ init, char *name) { COLINSTANCE instance; STORE_HANDLE handle = handleHandleCreate(map->handle); instance = (COLINSTANCE) handleAlloc(proto->destroy, handle, sizeof(struct InstanceStruct)); instance->map = map; instance->handle = handle; instance->proto = proto; instance->name = handleAlloc(0, handle, strlen(name)+1); strcpy(instance->name, name); instance->displayed = displayed; instance->drawInit = 0; /* Next few function are optional */ instance->configure = 0; /* Init so the create routine needn't */ /* ACEDB-GRAPH INTERFACE: set acedb specific map field. */ if (getGraphAcedbResetSave() != NULL) (getGraphAcedbResetSave())(&(instance->save)) ; instance->conversionRegister = 0; instance->setSelectBox = 0; instance->unSelectBox = 0; instance->doColour = 0; instance->pick = 0; if (!(*proto->create)(instance, init)) { handleDestroy(instance->handle) ; return 0; } return instance ; } /* call this to destroy graph and everything - public */ /* can also just destroy graph */ void controlDestroy(COLCONTROL control) { if (graphActivate(control->graph)) { graphDestroy(); control->graph = 0 ; } } /* called by graph Destroy, and by create code when it puts an new control */ /* on an existing graph */ static void controlDestroyed(void) { COLCONTROL control = currentColControl("controlDestroyed"); Graph old = graphActive() ; int i; for (i=0; iinstances); i++) { COLINSTANCE instance = arr(control->instances, i, COLINSTANCE); if(instance->configGraph && instance->configGraph != old) { /* remove configure menu if it exists */ graphActivate(instance->configGraph); graphDestroy(); graphActivate(old); } } graphAssRemove(&GRAPH2COLCONTROL_ASSOC); handleDestroy(control->handle); } BOOL controlSetColByName(MAPCONTROL map, char *name, int mode) { COLCONTROL control = map->control; int i; COLINSTANCE instance; for(i=0; iinstances); i++) { instance = arr(control->instances, i, COLINSTANCE); if (instance->map == map && !strcmp(instance->name, name)) switch (mode) { case 0: instance->displayed = FALSE; return TRUE; case 1: instance->displayed = TRUE; return TRUE; case 2: instance->displayed = !instance->displayed; return TRUE; default: messcrash("Illegal mode in controlSetColByName"); } } return FALSE; } void controlDrawControl(void *c) { COLCONTROL control = (COLCONTROL) c; MAPCONTROL map; COLINSTANCE instance; BOOL active; float offset = 0; float oldOffset; int i, gw, gh; int firstFromBox = 0; if (graphActivate(control->graph)) graphPop(); else return; /* The pathalogical case if no columns crashes later on unless we trap here */ if (arrayMax(control->instances) == 0) return; control->boxIndex = arrayReCreate(control->boxIndex, 100, COLINSTANCE); control->boxIndex2 = arrayReCreate(control->boxIndex2, 100, void *); assClear(control->bottomBoxes); control->activeBox = 0; control->fromBox = 0; controlConvertRegister(control); /* transient conversion */ graphFitBounds(&gw, &gh); control->graphWidth = gw; control->graphHeight = gh; graphClear () ; /* draw the header */ if (control->hideHeader) control->topMargin = 1;/* au lieu de 0 mhmp 23.10.97 */ else { control->topMargin = (*control->currentMap->drawHeader)(control->currentMap) ; control->graphHeight -= 0.6; /* space for map colour bar */ } control->halfGraphHeight = 0.5 * ( control->graphHeight - control->topMargin ); control->lastHeaderBox = graphLastBox(); for (i=0; imaps); i++) { map = array(control->maps, i, MAPCONTROL); if (map->beforeDraw) (*map->beforeDraw)(map); if (map->convertTrans) (*map->convertTrans)(control, map); } for(i=0; iinstances); i++) { instance = array(control->instances, i, COLINSTANCE); if (instance->drawInit) (*instance->drawInit)(instance); } active = (array(control->instances, 0, COLINSTANCE)->map == control->currentMap); for(i=0; iinstances); i++) { instance = array(control->instances, i, COLINSTANCE); if (instance->displayed && !instance->map->suppressed) { oldOffset = offset; if (!control->hideHeader && active != (instance->map != control->currentMap)) /* only when status changes */ { if ((active = (instance->map != control->currentMap))) graphColor(PALEGRAY); else graphColor(WHITE); #ifdef ED_G_NEVER_INCLUDE_THIS_CODE printf("col = %s, graphFillRectangle(%f, %f, %f, %f)\n", instance->name, offset, control->topMargin, offset+200, control->graphHeight) ; #endif /* ED_G_NEVER_INCLUDE_THIS_CODE */ graphFillRectangle(offset, control->topMargin, offset+200, control->graphHeight); graphColor(BLACK); } (*instance->draw)(instance, &offset); if (!firstFromBox && control->fromBox) firstFromBox = control->fromBox; if (!control->hideHeader) { char *cp; char tmp[2]; int bottomBox = graphBoxStart(); int oh = graphTextHeight(0.5); assInsert(control->bottomBoxes, assVoid(bottomBox), instance); graphLine(oldOffset, control->graphHeight, oldOffset, control->graphHeight+0.6); graphLine(offset, control->graphHeight, offset, control->graphHeight+0.6); for (cp = instance->name ; *cp && oldOffsetgraphHeight); } graphBoxEnd(); graphTextHeight(oh); graphBoxDraw(bottomBox, BLACK, instance->map->colour); } } array(control->mapTransitions, i, float) = offset; } graphColor (WHITE) ; #ifdef ED_G_NEVER_INCLUDE_THIS_CODE printf("final graphFillRectangle(%f, %f, %f, %f)\n", offset, control->topMargin, offset+200, control->graphHeight) ; #endif /* ED_G_NEVER_INCLUDE_THIS_CODE */ graphFillRectangle(offset, control->topMargin, offset+200, control->graphHeight); graphColor (BLACK) ; if (control->currentMap->menu) graphMenu(control->currentMap->menu); controlOverlay() ; graphRedraw () ; /* ACEDB-GRAPH INTERFACE: */ if (getGraphAcedbResetKey() != NULL) (getGraphAcedbResetKey())(&(control->from)) ; if (firstFromBox) controlSelectBox(firstFromBox); return; } /* controlDrawControl */ void controlDraw(void) { COLCONTROL control = currentColControl("controlDraw"); controlDrawControl((void *) control); } /* controlDraw */ void controlConvertRegister(COLCONTROL control) { COLINSTANCE instance; MAPCONTROL map; int i; for (i=0; imaps); i++) { map = array(control->maps, i, MAPCONTROL); arrayMax(map->transConversionRoutines) = 0; } for(i=0; iinstances); i++) { instance = array(control->instances, i, COLINSTANCE); if (instance->conversionRegister) (*instance->conversionRegister)(control, instance); } } void **controlConvert(Array conversionRoutines, MAPCONTROL map, void *(*conRoutine)(), void *params) { struct ConversionRecord *c; int i; for(i=0; itransConversionRoutines); i++) { c = arrp(map->transConversionRoutines, i, struct ConversionRecord ); if (c->convertRoutine == conRoutine && c->params == params) return &c->results ; } c = arrayp(map->transConversionRoutines, arrayMax(map->transConversionRoutines), struct ConversionRecord); c->convertRoutine = conRoutine; c->params = params; return &c->results; } void controlConvertTrans(MAPCONTROL map) { int i; struct ConversionRecord *c; for (i=0; itransConversionRoutines); i++) { c = arrp(map->transConversionRoutines, i, struct ConversionRecord); c->results = (*c->convertRoutine)(map, c->params); } } void controlConvertPerm(MAPCONTROL map) { int i; struct ConversionRecord *c; for (i=0; ipermConversionRoutines); i++) { c = arrp(map->permConversionRoutines, i, struct ConversionRecord); c->results = (*c->convertRoutine)(map, c->params); } } void controlAddButton(MAPCONTROL map, COLOUROPT *button, MENUOPT *menu) { COLOUROPT **p = map->buttonsAddHere; *p = (COLOUROPT *)halloc(sizeof(COLOUROPT), map->handle); **p = *button; /* copies whole struct */ (*p)->next = *p; map->buttonsAddHere = &((*p)->next); array(map->menusOnButtons, arrayMax(map->menusOnButtons), MENUOPT *) = menu; return; } /* controlAddButton */ float controlDrawButtons(MAPCONTROL map, float x, float y, float max) { int i; int box = graphColouredButtons(map->buttons, x, y, max); MENUOPT *menu; float y2 ; for (i=0; imenusOnButtons); i++, box++) { menu = arr(map->menusOnButtons, i, MENUOPT *); if (menu) graphBoxMenu(box, menu); /* ACEDB-GRAPH INTERFACE: acedb may need to free a popped up menu. */ if (getGraphAcedbFreemenu() != NULL) { if (i == 0 && map->viewMenu) (getGraphAcedbFreemenu())(box, map->viewMenu) ; } } graphBoxDim (--box, 0, 0, 0, &y2) ; return y2 ; } static double dragOldy; static void controlLeftDrag(double x, double y) { COLCONTROL control = currentColControl("controlLeftDrag"); graphXorLine(0, dragOldy, control->graphWidth, dragOldy); dragOldy = y; graphXorLine(0, y, control->graphWidth, y); return; } /* controlLeftDrag */ static void controlLeftUp(double x, double y) { COLCONTROL control = currentColControl("controlLeftUp"); graphXorLine(0, dragOldy, control->graphWidth, dragOldy); graphRegister(LEFT_DRAG, 0); graphRegister(LEFT_UP, 0); return; } /* controlLeftUp */ static void controlPick(int box, double x, double y, int modifier_unused) { COLCONTROL control = currentColControl("controlPick"); COLINSTANCE instance; float x1,x2,y1,y2; if (box && box <= control->lastHeaderBox) /* picked a header box, despatch to map */ { if (control->currentMap->headerPick) (*control->currentMap->headerPick)(control->currentMap, box, x, y); return; } if (box && assFind(control->bottomBoxes, assVoid(box), &instance)) /* a bottom coloured box, click here to configure */ { if (instance->configure) { if ((*instance->configure)(instance)) { /* ACEDB-GRAPH INTERFACE: set acedb specific map field. */ if (getGraphAcedbResetKey() != NULL) (getGraphAcedbResetKey())(&(instance->map->viewKey)) ; controlDrawControl(control); } } return; } /* Second click: follow it */ if (box && box == control->activeBox) { instance = array(control->boxIndex, box, COLINSTANCE); if (instance && instance->followBox) (*instance->followBox)(instance, box, x, y); return; } /* if (!box) controlMiddleUp(x,y); il rm 12/6/97 srk added for some reason 4/3/97 ??? */ instance = array(control->boxIndex, box, COLINSTANCE); if (instance && instance->pick) { (*instance->pick)(instance, box, x, y); return; } controlSelect(box, x, y); if (box == 0 && control->needRuler) { graphBoxDim(0, &x1, &y1, &x2, &y2); y += y1; dragOldy = y; graphXorLine(0, y, control->graphWidth, y); graphRegister(LEFT_DRAG, (GraphFunc)controlLeftDrag); graphRegister(LEFT_UP, (GraphFunc)controlLeftUp); } return; } /* controlPick */ static void controlSelect(int box, double x, double y) { COLCONTROL control = currentColControl("controlSelect"); COLINSTANCE instance; MAPCONTROL newMap = control->currentMap; BOOL redrawOld = FALSE, redrawNew = FALSE, redrawMap = FALSE; int i; /* first deselect any previous box */ if (control->activeBox) { instance = array(control->boxIndex, control->activeBox, COLINSTANCE); if (instance && instance->unSelectBox) redrawOld = (*instance->unSelectBox)(instance, control->activeBox); } /* Now set the new one as selected */ control->activeInstance = 0; /* ACEDB-GRAPH INTERFACE: set acedb specific field. */ if (getGraphAcedbResetKey() != NULL) (getGraphAcedbResetKey())(&(control->activeKey)) ; if (box == 0 && x > 0) /* Pick in no box, can still change map*/ { /* switch current map if necessary */ for(i=0; iinstances); i++) { if (array(control->mapTransitions, i, float) > x) { newMap = array(control->instances, i, COLINSTANCE)->map; break; } } } else { instance = array(control->boxIndex, box, COLINSTANCE); if (instance && instance->setSelectBox) { control->activeInstance = instance; redrawNew = (*instance->setSelectBox)(instance, box, x, y); newMap = instance->map; } } redrawMap = newMap != control->currentMap; control->currentMap = newMap; if (redrawOld || redrawNew || redrawMap) controlDraw(); else controlOverlay(); return; } /* controlSelect */ void controlOverlay(void) { COLCONTROL control = currentColControl("controlOverlay"); COLINSTANCE instance; int i; control->activeBox = 0; for (i=0; iboxIndex); i++) { instance = array(control->boxIndex, i, COLINSTANCE); if (instance && instance->doColour) (*instance->doColour)(instance, i); /* this call sets control->activeBox */ } return; } /* controlOverlay */ void controlUnselectAll(void) /* Public function to unhighlight the currently highlit thing */ { controlSelect(0, -1, 0); } void controlSelectBox(box) /* Public function to highlight the thing in box */ { controlSelect(box, -1, 0); } static void controlKybd(int k, int modifier_unused) { MAPCONTROL map = currentMapControl(); if (map->keyboard) (*(map->keyboard))(k); } void controlPrint (void) { COLCONTROL control = currentColControl("controlPrint"); MAPCONTROL map = control->currentMap; float oldMin = map->min; float oldMax = map->max; float oldCentre = map->centre; BOOL oldHH = control->hideHeader; float min, max, mag; int nx, ny; ACEIN zone_in; graphFitBounds (&nx, &ny) ; zone_in = messPrompt("Please state the zone you wish to print", messprintf("%g %g", map->min, map->max), "ffz", 0); if (!zone_in) return ; aceInFloat (zone_in, &min) ; aceInFloat (zone_in, &max) ; aceInDestroy (zone_in); if (min >= max) return ; map->min = min; map->max = max; map->centre = (max+min)/2.0; control->hideHeader = TRUE; mag = map->mag > 0 ? map->mag : -map->mag; graphBoundsPrint (nx + 0.2, 1.05 * (max - min) * mag + 5, controlDraw) ; map->min = oldMin; map->max = oldMax; map->centre = oldCentre; control->hideHeader = oldHH; controlDrawControl(control); } /* controlPrint */ /************************************/ /* a few utility functions (public) */ /************************************/ /* set/read box to private data mapping */ void controlRegBox(COLINSTANCE instance, int box, void *private) { COLCONTROL control = instance->map->control; array(control->boxIndex, box, COLINSTANCE) = instance; array(control->boxIndex2, box, void *) = private; } void *controlBoxRegd(COLINSTANCE instance, int box) { return arr(instance->map->control->boxIndex2, box, void *); } /* utility func which helps truncate column drawing: coord is y value in map coords, slop is how far above/below that we will draw, ret is given translated graph co-ord, truncated if needed. returns FALSE is truncation done, TRUE otherwise . */ BOOL controlCheckY(MAPCONTROL map, float coord, float slop, float *ret) { COLCONTROL control = map->control; float gco = MAP2GRAPH(map, coord); if (gco < (control->topMargin + slop + 0.1)) { if (ret) *ret = control->topMargin + 0.1; return FALSE; } if (gco > (control->graphHeight - (0.1 + slop))) { if (ret) *ret = control->graphHeight - 0.1; return FALSE; } if (ret) *ret = gco; return TRUE; } void controlTruncatedLine(COLCONTROL control, float x1, float y1, float x2, float y2) /* x2, y2 is assumed to be between topMargin and graphHeight */ /* x1, y1 can be outside, draw part if line bewteen tm and gh */ { float t = control->topMargin; float b = control->graphHeight; if (y1 > t && y1 < b) graphLine(x1, y1, x2, y2); else if (y1 <= t) { float crossx = x1 + (x2-x1)*(control->topMargin-y1)/(y2-y1); graphLine(crossx, control->topMargin, x2, y2); } else { float crossx = x1 + (x2-x1)*(y1-control->graphHeight)/(y1-y2); graphLine(crossx, control->graphHeight, x2, y2); } } /*************************************************************************/ /* A few columns for use anywhere, which depend only on map information. */ /*************************************************************************/ static void mapLocatorDrawInit(COLINSTANCE instance) { MAPCONTROL map = instance->map; COLCONTROL control = map->control ; float y0, yn; y0 = 1 + control->topMargin ; yn = control->graphHeight - 1; if (map->mag < 0) { map->thumb.offset = map->max ; map->thumb.fac = (y0 - yn) / (map->max - map->min) ; } else { map->thumb.offset = map->min ; map->thumb.fac = (yn - y0) / (map->max - map->min) ; } map->thumb.halfwidth = 0.5 * map->thumb.fac * (control->graphHeight-control->topMargin-2.0) / map->mag; map->thumb.x = 0 ; map->thumb.drawn = FALSE; map->cursor.drawn = FALSE; /* In case scale instance removed */ } static void mapLocatorDraw (COLINSTANCE instance, float *offset) { float y; MAPCONTROL map = instance->map; COLCONTROL control = map->control ; float t, b; *offset += 1; map->thumb.x = *offset; map->thumb.drawn = TRUE; #ifdef ED_G_NEVER_INCLUDE_THIS_CODE printf("final graphFillRectangle(%f, %f, %f, %f)\n", *offset - 0.25, MAP2WHOLE(map, map->min), *offset + 0.25, MAP2WHOLE(map, map->max)); #endif /* ED_G_NEVER_INCLUDE_THIS_CODE */ graphFillRectangle (*offset - 0.25, MAP2WHOLE(map, map->min), *offset + 0.25, MAP2WHOLE(map, map->max)); y = MAP2WHOLE(map, map->centre); map->thumb.box = graphBoxStart(); t = y - map->thumb.halfwidth; b = y + map->thumb.halfwidth; if (t < control->topMargin) t = control->topMargin; if (b > control->graphHeight) b = control->graphHeight; if (b > control->topMargin && t < control->graphHeight) graphRectangle (*offset - 0.5, t, *offset + 0.5, b) ; graphBoxEnd(); graphBoxDraw (map->thumb.box, DARKGREEN, GREEN); array(control->boxIndex, map->thumb.box, COLINSTANCE) = instance; if (map->cursor.drawn && map->hasProjectionLines) { controlTruncatedLine(control, *offset, y - map->thumb.halfwidth, map->cursor.x, control->topMargin+1); controlTruncatedLine(control, *offset, y + map->thumb.halfwidth, map->cursor.x, control->graphHeight-1); } *offset += 1; } static void mapThumbDrag (float *x, float *y, BOOL isDone) { COLCONTROL control = currentColControl("mapThumbDrag"); MAPCONTROL map = control->thumbMap; float top, bottom; /* fix x */ *x = map->thumb.x - 0.5; /* stop the user pulling the thumb bar out of the extent */ top = MAP2WHOLE(map, map->min); bottom = MAP2WHOLE(map, map->max); if (map->thumb.fac < 0 ) { float tmp = bottom; bottom = top; top = tmp; } if (*y < top) *y = top; if ((*y + 2 * map->thumb.halfwidth) > bottom) *y = bottom - 2 * map->thumb.halfwidth; if (isDone) { map->centre = WHOLE2MAP(map, *y + map->thumb.halfwidth); controlDraw(); } } static void mapLocatorPick(COLINSTANCE instance, int box, double x, double y) { instance->map->control->thumbMap = instance->map; graphBoxDrag(box, mapThumbDrag); } /* mapLocatorPick */ struct configMapLocal{ float magConf; BOOL lines; }; static BOOL mapLocatorConfigure (COLINSTANCE instance) { MAPCONTROL map = instance->map; struct configMapLocal *cf = (struct configMapLocal *) messalloc(sizeof(struct configMapLocal)); float line = 2.0; if(controlCreateConfig(instance,cf,"Configure Locator column",0.5,0.15)){ /* initialise data */ cf->magConf = map->magConf; cf->lines = map->hasProjectionLines; graphFloatEditor("Magnification :",&cf->magConf,4.0,line++,0); graphToggleEditor("Show projection Lines",&cf->lines,4.0,line++); graphRedraw(); } return FALSE; } /* mapLocatorConfigure */ static void mapLocatorConfigFinal (COLINSTANCE instance, void *locals, BOOL ok) { MAPCONTROL map = instance->map; struct configMapLocal *cf = locals; if(ok) { map->magConf = cf->magConf; if (map->magConf != 0.0) map->mag = map->magConf; map->hasProjectionLines = cf->lines; } else messfree(cf); return; } /* mapLocatorConfigFinal */ static BOOL mapLocatorCreate (COLINSTANCE instance, OBJ init) { MAPCONTROL map = instance->map; instance->draw = mapLocatorDraw; instance->drawInit = mapLocatorDrawInit; instance->pick = mapLocatorPick; instance->configure = mapLocatorConfigure; instance->configFinal = mapLocatorConfigFinal; map->hasProjectionLines = TRUE; /* default */ map->magConf = map->mag ; /* map->magConf = 0; mhmp 24.10.97*/ /* ACEDB-GRAPH INTERFACE: Call acedb specific code if registered to get */ /* magnification and projection. */ if (getGraphAcedbMapLocate() != NULL) (getGraphAcedbMapLocate())(init, &(instance->save), map) ; return TRUE ; } /* mapLocatorCreate */ static void mapWhole (void *m) { BOOL isNeg ; MAPCONTROL map = (MAPCONTROL) m; map->centre = (map->max + map->min) / 2.0 ; isNeg = (map->mag < 0) ; map->mag = (map->control->graphHeight - map->control->topMargin - 2.0) / (map->max - map->min) ; if (isNeg) map->mag = -map->mag ; controlDraw() ; return; } /* mapWhole */ static void mapZoomIn (void *m) { MAPCONTROL map = (MAPCONTROL) m; map->mag *= 2 ; controlDraw() ; return; } /* mapZoomIn */ static void mapZoomOut (void *m) { MAPCONTROL map = (MAPCONTROL) m; map->mag /= 2 ; controlDraw() ; return; } /* mapZoomOut */ static void mapLocatorInit(COLPROTO proto, MAPCONTROL map) { COLOUROPT *button; button = (COLOUROPT *) messalloc(sizeof(COLOUROPT)); button->text = "Whole"; button->f = mapWhole; button->arg = (void *) map; button->fg = BLACK; button->bg = WHITE; button->next = 0; controlAddButton(map, button, 0); button->text = "Zoom in"; button->f = mapZoomIn; button->arg = (void *) map; button->fg = BLACK; button->bg = WHITE; button->next = 0; controlAddButton(map, button, 0); button->text = "Zoom out"; button->f = mapZoomOut; button->arg = (void *) map; button->fg = BLACK; button->bg = WHITE; button->next = button; /* Terminator */ controlAddButton(map, button, 0); messfree(button); return; } /* mapLocatorInit */ struct ProtoStruct mapLocatorColumn = { mapLocatorInit, mapLocatorCreate, 0, "Locator", 0, TRUE, 0, 0, "The locator column displays a green slider bar which can be used to " "control the position of the map display.\n\n" "The magnification configuration option can be used to set a specific " "factor between map co-ordinates and screen distances in character " "size units. If a configuration is saved with a magnification set, " "that will be used for initial display in preference to defaults " "or information derived from the displayed object.\n\n" "The \"Show projection lines\" toggle controls the lines " "drawn between the green bar on the locator and the ends " "of the scale, if it exists.\n" }; /************************************************************/ /* scroller, to be used from giface/netscape */ static void mapScrollerDrawInit(COLINSTANCE instance) { } static void mapScrollerDraw(COLINSTANCE instance, float *offset) { MAPCONTROL map = instance->map; COLCONTROL control = map->control ; int i, box ; float t, b, y1, y2 ; Array aa = 0 ; *offset += 1.5; box = graphBoxStart () ; t = GRAPH2MAP(map, control->topMargin + 1) ; t = MAP2WHOLE (map, t) ; if (t < control->topMargin + .2) t = control->topMargin + .2 ; b = GRAPH2MAP(map, control->graphHeight-1) ; b = MAP2WHOLE (map, b) ; if (b > control->graphHeight) b = control->graphHeight ; #ifdef ED_G_NEVER_INCLUDE_THIS_CODE printf("final graphFillRectangle(%f, %f, %f, %f)\n", *offset - 0.5, t, *offset + 0.5, b) ; #endif /* ED_G_NEVER_INCLUDE_THIS_CODE */ graphFillRectangle (*offset - 0.5, t, *offset + 0.5, b) ; aa = arrayCreate (8, float) ; i = 0 ; y1 = control->topMargin + 1. ; y2 = control->topMargin + 2. ; if (y1 < t ) { array (aa, i++, float) = *offset ; array (aa, i++, float) = y1 ; array (aa, i++, float) = *offset + .5 ; array (aa, i++, float) = y2 ; array (aa, i++, float) = *offset - .5 ; array (aa, i++, float) = y2 ; array (aa, i++, float) = *offset ; array (aa, i++, float) = y1 ; graphPolygon (aa) ; graphLine (*offset, t, *offset, y2) ; } arrayDestroy (aa) ; aa = arrayCreate (8, float) ; y1 = control->graphHeight - 1. ; y2 = control->graphHeight - 2. ; i = 0 ; if (y1 > b ) { array (aa, i++, float) = *offset ; array (aa, i++, float) = y1 ; array (aa, i++, float) = *offset + .5 ; array (aa, i++, float) = y2 ; array (aa, i++, float) = *offset - .5 ; array (aa, i++, float) = y2 ; array (aa, i++, float) = *offset ; array (aa, i++, float) = y1 ; graphPolygon (aa) ; graphLine (*offset, b, *offset, y2) ; } arrayDestroy (aa) ; graphBoxEnd(); graphBoxDraw (box, DARKGREEN, WHITE); array(control->boxIndex, box, COLINSTANCE) = instance; *offset += 1; } static void mapScrollerInit(COLPROTO proto, MAPCONTROL map) { } static void mapScrollerPick(COLINSTANCE instance, int box, double x, double y) { instance->map->centre = WHOLE2MAP (instance->map, y + instance->map->control->topMargin + 1) ; controlDraw() ; } static BOOL mapScrollerCreate(COLINSTANCE instance, OBJ init) { instance->draw = mapScrollerDraw; instance->drawInit = mapScrollerDrawInit; instance->pick = mapScrollerPick; return TRUE ; } struct ProtoStruct mapScrollerColumn = { mapScrollerInit, mapScrollerCreate, 0, "Scroller", 0, 0, 0, 0, "The scroller column displays a scroll bar which is " " sensitive to the left mouse button and hance may by " " used from Netscpape. \n\n" }; /************************************************************/ void mapControlCursorSet (MAPCONTROL map, float x) { if (x > 0) map->cursor.val = 0.5 + x / map->cursor.unit ; else map->cursor.val = -0.5 + x / map->cursor.unit ; if (map->hasCursor && !map->cursor.box) /* off screen before, draw it now. */ controlDrawControl(map->control); else mapControlCursorShift (map) ; } void mapControlCursorShift (MAPCONTROL map) { float x1, x2, y1, y2, z = mapControlCursorPos(map) ; if (!map->cursor.box) return ; graphBoxDim (map->cursor.box, &x1, &y1, &x2, &y2) ; strcpy (map->cursor.text, messprintf ("%.*f", map->cursor.resolution, z)) ; y1 = MAP2GRAPH(map, mapControlCursorPos(map)) ; graphBoxShift (map->cursor.box, x1, y1-0.5) ; } static void mapControlFindScaleUnit (MAPCONTROL map, float *u, float *sub) { float cutoff = 5 / map->mag ; float unit = *u ; float subunit = *u ; if (cutoff < 0) cutoff = -cutoff ; while (unit < cutoff) { unit *= 2 ; subunit *= 5 ; if (unit >= cutoff) break ; unit *= 2.5000001 ; /* safe rounding */ if (unit >= cutoff) break ; unit *= 2 ; subunit *= 2 ; } subunit /= 10 ; if (subunit > *sub) *sub = subunit ; *u = unit ; } static void mapScaleDrawInit(COLINSTANCE instance) { MAPCONTROL map = instance->map; map->cursor.x = 0; map->cursor.drawn = FALSE; map->thumb.drawn = FALSE; /* In case locator instance removed */ } static void mapScaleDraw (COLINSTANCE instance, float *offset) { MAPCONTROL map = instance->map; float unit = map->scaleUnit; float subunit = unit/10.0; float x, y ; int resolution, max = 0 ; char *cp = 0 ; COLCONTROL control = map->control ; map->cursor.x = *offset+1.5; map->cursor.drawn = TRUE; map->cursor.box = 0; mapControlFindScaleUnit (map, &unit, &subunit) ; if (unit >= 1) resolution = 0 ; else if (unit >= .1) resolution = 1 ; else if (unit >= .01) resolution = 2 ; else if (unit >= .001) resolution = 3 ; else resolution = 0 ; x = GRAPH2MAP(map, control->topMargin+1) ; x = unit * ((((x>=0)^(map->mag<0))?1:0) + (int)(x/unit)) ; while ((y = MAP2GRAPH(map, x)) < control->graphHeight - 0.95) { graphLine (*offset+0.5,y,*offset+1.5,y) ; cp = messprintf ("%-4.*f", resolution, x) ; graphText (cp, *offset+2, y-0.5) ; if (strlen(cp)+3 > max) max = 3+strlen(cp) ; if (map->mag >0) x += unit ; else x -= unit; } x = GRAPH2MAP(map, control->topMargin+1) ; x = subunit * ((((x>=0)^(map->mag<0))?1:0) + (int)(x/subunit)) ; while ((y = MAP2GRAPH(map, x)) < control->graphHeight - 1.5) { graphLine (*offset+1.0,y,*offset+1.5,y) ; if (map->mag >0) x += subunit ; else x -= subunit; } graphLine (*offset+1.5, control->topMargin+1, *offset+1.5, control->graphHeight-1 ) ; if (map->thumb.drawn && map->hasProjectionLines) { controlTruncatedLine(control, map->thumb.x, MAP2WHOLE(map, map->centre) - map->thumb.halfwidth, *offset+1.5, control->topMargin+1) ; controlTruncatedLine(control, map->thumb.x, MAP2WHOLE(map, map->centre) + map->thumb.halfwidth, *offset+1.5, control->graphHeight-1.0) ; } if (map->hasCursor && !control->hideHeader) { float z = mapControlCursorPos(map) ; float y = MAP2GRAPH(map, z) ; /* Jean - your x here was a bug */ float thumbx = map->thumb.drawn ? map->thumb.x : 0; if (y > control->topMargin+1 && y < control->graphHeight -1) { if (map->cursor.unit >= .99) map->cursor.resolution = 0 ; else if (map->cursor.unit >= .099) map->cursor.resolution = 1 ; else if (map->cursor.unit >= .0099) map->cursor.resolution = 2 ; else if (map->cursor.unit >= .00099) map->cursor.resolution = 3 ; else map->cursor.resolution = 0 ; strcpy (map->cursor.text, messprintf ("%.*f", map->cursor.resolution, z)) ; map->cursor.box = graphBoxStart() ; graphLine (thumbx, y, control->graphWidth+1, y); map->cursor.pickBox = graphBoxStart() ; if (max < 3.0+strlen(map->cursor.text)) max = 3.0+strlen(map->cursor.text); graphColor (LIGHTGREEN) ; #ifdef ED_G_NEVER_INCLUDE_THIS_CODE printf("final graphFillRectangle(%f, %f, %f, %f)\n", *offset+0.5, y-0.5, *offset+max-0.5, y+0.5) ; #endif /* ED_G_NEVER_INCLUDE_THIS_CODE */ graphFillRectangle (*offset+0.5, y-0.5, *offset+max-0.5, y+0.5) ; graphColor (BLACK) ; graphTextPtr (map->cursor.text, *offset+0.5, y-0.5, strlen(map->cursor.text)) ; graphBoxEnd () ; graphBoxEnd () ; graphBoxSetPick (map->cursor.box, FALSE) ; /* only pick on .pickBox */ array(control->boxIndex, map->cursor.pickBox, COLINSTANCE) = instance; graphBoxDraw (map->cursor.box, BLACK, TRANSPARENT) ; } } *offset += max ; } static void mapControlCursorDrag (float *x, float *y, BOOL isDone) { COLCONTROL control = currentColControl("mapControlCursorDrag"); MAPCONTROL map = control->thumbMap; *x = map->cursor.x - 1.0; if (*y < control->topMargin + 0.5) *y = control->topMargin + 0.5; if (*y > control->graphHeight - 1.5) *y = control->graphHeight - 1.5; if (isDone) mapControlCursorSet (map, GRAPH2MAP(map, *y+0.5)) ; } static void mapScalePick(COLINSTANCE instance, int box, double x, double y) { instance->map->control->thumbMap = instance->map; graphBoxDrag(box, mapControlCursorDrag); } struct configLocalsName{ float scale,cursor; BOOL showCursor; }; static BOOL mapScaleConfigure(COLINSTANCE instance) { MAPCONTROL map = instance->map; struct configLocalsName *cf = (struct configLocalsName *) messalloc(sizeof(struct configLocalsName)); float line = 2.0; /* internal representation depends on cursor unit */ if(controlCreateConfig(instance,cf,"Configure Scale column",0.5,0.15)){ /* initialise data */ cf->scale = map->scaleUnit; cf->cursor = map->cursor.unit; cf->showCursor = map->hasCursor; graphFloatEditor("Scale unit:",&cf->scale,4.0,line++,0); graphFloatEditor("Cursor unit:",&cf->cursor,4.0,line++,0); graphToggleEditor("Show cursor",&cf->showCursor,4.0,line++); graphRedraw(); } return FALSE; } static void mapScaleConfigFinal(COLINSTANCE instance, void *locals, BOOL ok) { struct configLocalsName *cf = locals; MAPCONTROL map = instance->map; float old = mapControlCursorPos(map); if(ok) { map->hasCursor = cf->showCursor; map->scaleUnit = cf->scale; if (map->scaleUnit <0.0001) map->scaleUnit = 0.0001; map->cursor.unit = cf->cursor; if (map->cursor.unit < 0.001) map->cursor.unit = 0.001; if (old > 0) map->cursor.val = 0.5 + old / map->cursor.unit ; else map->magConf = 0.0; } else messfree(cf); } static BOOL mapScaleCreate(COLINSTANCE instance, OBJ init) { MAPCONTROL map = instance->map; instance->draw = mapScaleDraw; instance->drawInit = mapScaleDrawInit; instance->pick = mapScalePick; instance->configure = mapScaleConfigure; instance->configFinal = mapScaleConfigFinal; map->cursor.val = 0; map->cursor.unit = 1.0; map->scaleUnit = 0.01; map->hasCursor = FALSE; /* ACEDB-GRAPH INTERFACE: Call acedb specific code if registered to get */ /* scale information. */ if (getGraphAcedbScale() != NULL) (getGraphAcedbScale())(init, &(instance->save), map) ; return TRUE ; } struct ProtoStruct mapScaleColumn = { 0, mapScaleCreate, 0, "Scale", 0, TRUE, 0, 0, "The scale column shows a ruler scale with map co-ordinates, and " "optionally a cursor and indicator line.\n\n" "The \"cursor unit\" configuration option controls the distance by " "which the up and down arrows move the cursor.\n\n" "The \"scale unit\" configuration option controls the smallest " "difference between two scale ticks which can be drawn." }; /************************************************************************/ static void spacerDraw(COLINSTANCE instance, float *offset) { SPACERPRIV private = instance->private; COLCONTROL control = instance->map->control; if (private->colour != WHITE) /* white == transparent */ { graphColor(private->colour); #ifdef ED_G_NEVER_INCLUDE_THIS_CODE printf("final graphFillRectangle(%f, %f, %f, %f)\n", *offset, control->topMargin, *offset + private->width, control->graphHeight); #endif /* ED_G_NEVER_INCLUDE_THIS_CODE */ graphFillRectangle(*offset, control->topMargin, *offset + private->width, control->graphHeight); graphColor(BLACK); } *offset += private->width; } struct spacerLocal{ float width; int colour; }; static BOOL spacerConfigure(COLINSTANCE instance) { SPACERPRIV private = instance->private; struct spacerLocal *cf = (struct spacerLocal *) messalloc(sizeof(struct spacerLocal)); float line = 2.0; if(controlCreateConfig(instance,cf,"Configure spacer column",0.5,0.15)){ /* initialise data */ cf->width = private->width; cf->colour = private->colour; graphFloatEditor("Column width:",&cf->width,4.0,line++,0); graphColourEditor(" "," ",&cf->colour,4.0,line); graphRedraw(); } return FALSE; } static void spacerConfigFinal(COLINSTANCE instance, void *locals, BOOL ok){ SPACERPRIV private = instance->private; struct spacerLocal *cf = locals; if(ok){ private->colour = cf->colour; private->width = cf->width; } else messfree(cf); } static BOOL spacerCreate(COLINSTANCE instance, OBJ init) { SPACERPRIV private = (SPACERPRIV)handleAlloc(0, instance->handle, sizeof(struct SpacerPriv)); instance->private = private; instance->draw = spacerDraw; instance->configure = spacerConfigure; instance->configFinal = spacerConfigFinal; private->width = 1.0; private->colour = WHITE; /* ACEDB-GRAPH INTERFACE: Call acedb specific code if registered to get */ /* space information. */ if (getGraphAcedbSpace() != NULL) (getGraphAcedbSpace())(init, &(instance->save), private) ; return TRUE; } struct ProtoStruct spacerColumn = { 0, spacerCreate, 0, "Spacer", 0, FALSE, 0, 0, "The spacer column provides space in the map display, of user specified " "width and colour. The width is specified in units equal to the width of " "a single character on the display" }; /*************************************************/ /************* middle button for thumb **********/ static double oldx, oldy, oldDy ; static BOOL dragFast ; static void controlMiddleDrag (double x, double y) { MAPCONTROL map = currentMapControl(); if(dragFast) { graphXorLine (0, oldy - oldDy, map->thumb.x, oldy - oldDy) ; graphXorLine (0, oldy + oldDy, map->thumb.x, oldy + oldDy) ; } else graphXorLine (map->thumb.x, oldy, map->control->graphWidth, oldy) ; oldy = y ; if(dragFast) { oldDy *= exp ((x - oldx) / 25.) ; oldx = x ; graphXorLine (0, y - oldDy, map->thumb.x, y - oldDy) ; graphXorLine (0, y + oldDy, map->thumb.x, y + oldDy) ; } else graphXorLine (map->thumb.x, y, map->control->graphWidth, y) ; } static void controlMiddleUp (double x, double y) { float x1,x2,y1,y2 ; MAPCONTROL map = currentMapControl(); if (dragFast) { graphBoxDim (map->thumb.box, &x1, &y1, &x2, &y2) ; map->mag *= (y2 - y1) / (2. * oldDy) ; map->centre = WHOLE2MAP(map, y) ; } else map->centre = GRAPH2MAP(map,y) ; controlDraw(); } static void controlMiddleDown (double x, double y) { float x1 = 0, x2 = 0, y1 = 0 , y2 = 1 ; MAPCONTROL map = currentMapControl(); if (map->thumb.box) graphBoxDim (map->thumb.box, &x1, &y1, &x2, &y2) ; oldDy = (y2 - y1) / 2. ; dragFast = (x < map->thumb.x) ; if (dragFast) { graphXorLine (0, y - oldDy, map->thumb.x, y - oldDy) ; graphXorLine (0, y + oldDy, map->thumb.x, y + oldDy) ; } else graphXorLine (map->thumb.x, y, map->control->graphWidth, y) ; oldx = x ; oldy = y ; graphRegister (MIDDLE_DRAG, controlMiddleDrag) ; /* must redo */ graphRegister (MIDDLE_UP, controlMiddleUp) ; }