/* * CmdRS.c -- * * Commands with names beginning with the letters R through S. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/commands/CmdRS.c,v 1.13 2010/06/24 12:37:15 tim Exp $"; #endif /* not lint */ #include #include #include #include #include #include "tcltk/tclmagic.h" #include "utils/magic.h" #include "utils/stack.h" #include "utils/geometry.h" #include "utils/utils.h" #include "tiles/tile.h" #include "utils/hash.h" #include "utils/styles.h" #include "database/database.h" #include "database/fonts.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "utils/main.h" #include "commands/commands.h" #include "textio/textio.h" #include "graphics/graphics.h" #include "utils/tech.h" #include "drc/drc.h" #include "textio/txcommands.h" #include "utils/malloc.h" #include "utils/netlist.h" #include "netmenu/netmenu.h" #include "select/select.h" #include "utils/signals.h" #include "sim/sim.h" /* C99 compat */ #include "cif/cif.h" #include "lef/lef.h" #include "extract/extract.h" #include "irouter/irouter.h" #include "mzrouter/mzrouter.h" #include "router/router.h" #include "wiring/wiring.h" extern void DisplayWindow(); /* Used by CmdSetLabel() */ Label *DefaultLabel; /* * ---------------------------------------------------------------------------- * * CmdRandom * * Generate a random integer or set the random seed. This is mainly * used for seeding the random number generator for the GDS write * routine when GDS write is assigning a random prefix to ensure * uniqueness of names in a GDS library read from a file instead of * generated from the database. * * Results: * None. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ void CmdRandom(w, cmd) MagWindow *w; TxCommand *cmd; { int value; if (cmd->tx_argc == 1) { /* Return a random number */ #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(random())); #else TxPrintf("%d", random()); #endif } else if ((cmd->tx_argc >= 2) && (!strcmp(cmd->tx_argv[1], "seed"))) { if (cmd->tx_argc == 3) { value = atoi(cmd->tx_argv[2]); } else { value = (int)time(NULL); } srandom(value); } else { TxPrintf("usage: random [seed []]\n"); return; } } #if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE) /* * ---------------------------------------------------------------------------- * * CmdRsim * * Starts Rsim under Magic. * * Results: * None. * * Side effects: * Rsim is forked. * * ---------------------------------------------------------------------------- */ void CmdRsim(w, cmd) MagWindow *w; TxCommand *cmd; { if ((cmd->tx_argc == 1) && (!SimRsimRunning)) { TxPrintf("usage: rsim [options] file\n"); return; } if ((cmd->tx_argc != 1) && (SimRsimRunning)) { TxPrintf("Simulator already running. You cannot start another.\n"); return; } windCheckOnlyWindow(&w, DBWclientID); if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) { TxError("Put the cursor in a layout window.\n"); return; } if (cmd->tx_argc != 1) { cmd->tx_argv[cmd->tx_argc] = (char *) 0; SimStartRsim(cmd->tx_argv); } SimConnectRsim(FALSE); } #endif /* * ---------------------------------------------------------------------------- * * CmdSave -- * * Implement the "save" command. * Writes the EditCell out to a disk file. * * Usage: * save [file] * * Results: * None. * * Side effects: * Writes the cell out to file, if specified, or the file * associated with the cell otherwise. * Updates the caption in the window if the name of the edit * cell has changed. * Clears the modified bit in the cd_flags. * * ---------------------------------------------------------------------------- */ void CmdSave(w, cmd) MagWindow *w; TxCommand *cmd; { CellDef *locDef; if (cmd->tx_argc > 2) { TxError("Usage: %s [file]\n", cmd->tx_argv[0]); return; } /* The "save" command can turn a read-only cell into a writeable */ /* cell, or vice versa. We should have more checks on the status */ /* of the resulting file. . . . For now, we patch things up so */ /* that doing "save" on a read-only cell doesn't crash magic. */ if (EditCellUse == NULL) { locDef = ((CellUse *)w->w_surfaceID)->cu_def; locDef->cd_flags &= ~CDNOEDIT; } else locDef = EditCellUse->cu_def; DBUpdateStamps(locDef); if (cmd->tx_argc == 2) { char *fileName; if (CmdIllegalChars(cmd->tx_argv[1], "[],", "Cell name")) return; cmdSaveCell(locDef, cmd->tx_argv[1], FALSE, TRUE); } else cmdSaveCell(locDef, (char *) NULL, FALSE, TRUE); } /* * ---------------------------------------------------------------------------- * * CmdScaleGrid -- * * This procedure scales magic units with respect to lambda. A scaling * of 2 to 1, for example, makes the magic units half their former size, * allowing magic to read CIF files with components on the half-lambda * grid without the necessity of altering the technology file for the * process. * * Usage: * scalegrid a b (or) scalegrid a/b (or) scalegrid a:b * * "a" and "b" are integers * * Results: * magic internal units are scaled by factor a/b. * * Side Effects: * cifinput and cifoutput scale factors are multiplied by a/b. * All drc widths and distances are multiplied by b/a. * All layout tile coordinates are multiplied by b/a. * The current drawing scale and position are altered to maintain * the current view. * All windows are redrawn in case scaling has caused round-off * truncation of tile coordinates. * Geometry alterations are not undoable! * * ---------------------------------------------------------------------------- */ void CmdScaleGrid(w, cmd) MagWindow *w; TxCommand *cmd; { int scalen, scaled; char *argsep; Rect rootBox; CellDef *rootBoxDef; if ((cmd->tx_argc == 2) || (cmd->tx_argc == 3)) { if (cmd->tx_argc == 2) { if (((argsep = strchr(cmd->tx_argv[1], ':')) != NULL) || ((argsep = strchr(cmd->tx_argv[1], '/')) != NULL)) { *argsep++ = '\0'; if (!StrIsInt(argsep)) goto scalegridusage; else scaled = atoi(argsep); } else goto scalegridusage; } else { if (!StrIsInt(cmd->tx_argv[2])) goto scalegridusage; else scaled = atoi(cmd->tx_argv[2]); } if (!StrIsInt(cmd->tx_argv[1])) goto scalegridusage; else scalen = atoi(cmd->tx_argv[1]); if (scalen <= 0 || scaled <= 0) goto scalegridusage; else if (scalen == scaled) goto scalegridreport; /* Reduce fraction by the greatest common factor */ ReduceFraction(&scalen, &scaled); /* Check against CIF output style to see if we're violating a */ /* minimum grid resolution limit. */ if (CIFTechLimitScale(scalen, scaled) != 0) { TxError("Grid scaling is finer than limit set by the process!\n"); return; } /* Scale cifinput and cifoutput */ CIFTechInputScale(scalen, scaled, TRUE); CIFTechOutputScale(scalen, scaled); /* Scale drc rules */ DRCTechScale(scalen, scaled); /* Scale extract parameters */ ExtTechScale(scalen, scaled); /* Scale wiring parameters */ WireTechScale(scalen, scaled); /* Scale LEF parameters */ #ifdef LEF_MODULE LefTechScale(scalen, scaled); #endif #ifdef ROUTE_MODULE /* Scale core router parameters */ RtrTechScale(scalen, scaled); /* Scale maze router parameters (must come after DRCTechScale) */ MZAfterTech(); IRAfterTech(); #endif /* Scale all tiles */ DBScaleEverything(scaled, scalen); /* Save the current scale factor */ DBLambda[0] *= scalen; DBLambda[1] *= scaled; ReduceFraction(&DBLambda[0], &DBLambda[1]); /* Rescale cursor box */ if (ToolGetBox(&rootBoxDef, &rootBox)) { DBScalePoint(&rootBox.r_ll, scaled, scalen); DBScalePoint(&rootBox.r_ur, scaled, scalen); ToolMoveBox(TOOL_BL, &rootBox.r_ll, FALSE, rootBoxDef); ToolMoveCorner(TOOL_TR, &rootBox.r_ur, FALSE, rootBoxDef); } /* Adjust all window viewing scales and positions and redraw */ WindScale(scaled, scalen); /* This is harsh. Might work to scale all distance measures in */ /* the undo record, but this is simple and direct. */ UndoFlush(); /* TxPrintf("Magic internal unit scaled by %.3f\n", (float)scalen / (float)scaled); */ scalegridreport: TxPrintf("%d Magic internal unit%s = %d Lambda\n", DBLambda[1], (DBLambda[1] == 1) ? "" : "s", DBLambda[0]); return; } scalegridusage: TxError("Usage: scalegrid a b, where a and b are strictly positive integers\n"); return; } /* * ---------------------------------------------------------------------------- * * CmdSee -- * * This procedure is used to enable or disable display of certain * things on the screen. * * Usage: * see [no] stuff * * Stuff consists of mask layers or the keyword "allSame" * * Results: * None. * * Side effects: * The indicated mask layers are enabled or disabled from being * displayed in the current window. * * ---------------------------------------------------------------------------- */ void CmdSee(w, cmd) MagWindow *w; TxCommand *cmd; { int flags; bool off; char *arg; TileType i, j; TileTypeBitMask mask, *rmask; DBWclientRec *crec; windCheckOnlyWindow(&w, DBWclientID); if ((w == NULL) || (w->w_client != DBWclientID)) { TxError("Point to a layout window first.\n"); return; } crec = (DBWclientRec *) w->w_clientData; arg = (char *) NULL; off = FALSE; flags = 0; if (cmd->tx_argc > 1) { if (strcmp(cmd->tx_argv[1], "no") == 0) { off = TRUE; if (cmd->tx_argc > 2) arg = cmd->tx_argv[2]; } else arg = cmd->tx_argv[1]; if ((cmd->tx_argc > 3) || ((cmd->tx_argc == 3) && !off)) { TxError("Usage: see [no] layers|allSame\n"); return; } } /* Figure out which things to set or clear. Don't ever make space * invisible: that doesn't make any sense. */ if (arg != NULL) { if (strcmp(arg, "allSame") == 0) { mask = DBZeroTypeBits; flags = DBW_ALLSAME; } else { if (!CmdParseLayers(arg, &mask)) return; } } else mask = DBAllTypeBits; if (TTMaskHasType(&mask, L_LABEL)) flags |= DBW_SEELABELS; if (TTMaskHasType(&mask, L_CELL)) flags |= DBW_SEECELLS; TTMaskClearType(&mask, L_LABEL); TTMaskClearType(&mask, L_CELL); TTMaskClearType(&mask, TT_SPACE); if (off) { for (i = 0; i < DBNumUserLayers; i++) { if (TTMaskHasType(&mask, i)) TTMaskClearMask(&crec->dbw_visibleLayers, &DBLayerTypeMaskTbl[i]); } for (; i < DBNumTypes; i++) { /* This part handles stacked contact types. The display */ /* routine calls DBTreeSrUniqueTiles(), which displays */ /* stacked contact types only on their home plane. Thus */ /* if we select a contact type, we should also select all */ /* stacking types for that contact that are on the same */ /* plane. */ rmask = DBResidueMask(i); for (j = 0; j < DBNumUserLayers; j++) if (TTMaskHasType(rmask, j)) if (TTMaskHasType(&mask, j)) if (DBPlane(i) == DBPlane(j)) TTMaskClearMask(&crec->dbw_visibleLayers, &DBLayerTypeMaskTbl[i]); } crec->dbw_flags &= ~flags; } else { for (i = 0; i < DBNumUserLayers; i++) { if (TTMaskHasType(&mask, i)) TTMaskSetMask(&crec->dbw_visibleLayers, &DBLayerTypeMaskTbl[i]); } for (; i < DBNumTypes; i++) { rmask = DBResidueMask(i); for (j = 0; j < DBNumUserLayers; j++) if (TTMaskHasType(rmask, j)) if (TTMaskHasType(&mask, j)) if (DBPlane(i) == DBPlane(j)) TTMaskSetMask(&crec->dbw_visibleLayers, &DBLayerTypeMaskTbl[i]); } crec->dbw_flags |= flags; } WindAreaChanged(w, &w->w_screenArea); return; } #define SEL_AREA 0 #define SEL_VISIBLE 1 #define SEL_CELL 2 #define SEL_LABELS 3 #define SEL_INTERSECT 4 #define SEL_CLEAR 5 #define SEL_FLAT 6 #define SEL_HELP 7 #define SEL_KEEP 8 #define SEL_MOVE 9 #define SEL_PICK 10 #define SEL_SAVE 11 #define SEL_FEEDBACK 12 #define SEL_BBOX 13 #define SEL_BOX 14 #define SEL_CHUNK 15 #define SEL_REGION 16 #define SEL_NET 17 #define SEL_SHORT 18 #define SEL_DEFAULT 19 /* * ---------------------------------------------------------------------------- * * cmdSelectArea -- * * This is a utility procedure used by CmdSelect to do area * selection. * * Results: * None. * * Side effects: * The selection is augmented to contain all the information on * layers that is visible under the box, including paint, labels, * and unexpanded subcells. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ void cmdSelectArea(layers, less, option, globmatch) char *layers; /* Which layers are to be selected. */ bool less; int option; /* Option from defined list above */ char *globmatch; /* Optional match string for labels */ { SearchContext scx; TileTypeBitMask mask; int windowMask, xMask; DBWclientRec *crec; MagWindow *window; bzero(&scx, sizeof(SearchContext)); window = ToolGetBoxWindow(&scx.scx_area, &windowMask); if (window == NULL) { TxPrintf("The box isn't in a window.\n"); return; } /* Since the box may actually be in multiple windows, we have to * be a bit careful. If the box is only in one window, then there's * no problem. If it's in more than window, the cursor must * disambiguate the windows. */ xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask; if ((windowMask & ~xMask) != 0) { window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL); xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask; if ((windowMask & xMask) == 0) { TxPrintf("The box is in more than one window; use the cursor\n"); TxPrintf("to select the one you want to select from.\n"); return; } } if (CmdParseLayers(layers, &mask)) { if (TTMaskEqual(&mask, &DBSpaceBits)) (void) CmdParseLayers("*,label", &mask); TTMaskClearType(&mask, TT_SPACE); } else return; if (less) { (void) SelRemoveArea(&scx.scx_area, &mask, globmatch); return; } scx.scx_use = (CellUse *) window->w_surfaceID; scx.scx_trans = GeoIdentityTransform; crec = (DBWclientRec *) window->w_clientData; if (option == SEL_VISIBLE) { int i; for (i = 0; i < DBNumUserLayers; i++) { if((TTMaskHasType(&mask, i)) && !(TTMaskHasType(&crec->dbw_visibleLayers, i))) TTMaskClearType(&mask, i); } } SelectArea(&scx, &mask, crec->dbw_bitmask, globmatch); } /* * ---------------------------------------------------------------------------- * * cmdIntersectArea -- * * This is a utility procedure used by CmdSelect to do area * selection itersect. * * Results: * None. * * Side effects: * The selection is pared down to contain only the part of the * original selection that intersects with the type "layer". * * ---------------------------------------------------------------------------- */ void cmdIntersectArea(layer) char *layer; /* The layer to intersect with */ { TileType ttype; SearchContext scx; int windowMask, xMask; DBWclientRec *crec; MagWindow *window; char *lptr; bool negate = FALSE; bzero(&scx, sizeof(SearchContext)); window = ToolGetBoxWindow(&scx.scx_area, &windowMask); if (window == NULL) { TxPrintf("The box isn't in a window.\n"); return; } /* Since the box may actually be in multiple windows, we have to * be a bit careful. If the box is only in one window, then there's * no problem. If it's in more than window, the cursor must * disambiguate the windows. */ xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask; if ((windowMask & ~xMask) != 0) { window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL); xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask; if ((windowMask & xMask) == 0) { TxPrintf("The box is in more than one window; use the cursor\n"); TxPrintf("to select the one you want to select from.\n"); return; } } scx.scx_use = (CellUse *) window->w_surfaceID; scx.scx_trans = GeoIdentityTransform; crec = (DBWclientRec *) window->w_clientData; /* Special behavior: "!" or "~" in front of layer name intersects */ /* with NOT(layer). */ lptr = layer; if ((*lptr == '~') || (*lptr == '!')) { negate = TRUE; lptr++; } ttype = DBTechNameType(lptr); if (ttype < 0) { TxError("Cannot parse layer type \"%s\".\n", layer); return; } SelectIntersect(&scx, ttype, crec->dbw_bitmask, negate); } /* * ---------------------------------------------------------------------------- * * CmdSelect -- * * Implement the "select" command. * * Usage: * select [option args] * * Results: * None. * * Side effects: * The current selection is modified. See the user documentation * for all the possible things this command can do. * * ---------------------------------------------------------------------------- */ void CmdSelect(w, cmd) MagWindow *w; TxCommand *cmd; { TileTypeBitMask mask; SearchContext scx; DBWclientRec *crec; MagWindow *window; /* The two tables below define the allowable selection options, and * also the message printed out by ":select help" to describe the * options (can't use the same table for both because of the presence * of the "more" option). Note that there's one more entry in the * second table, due to a help message for ":select" with no arguments. */ static char *cmdSelectOption[] = { "area", "visible", "cell", "labels", "intersection", "clear", "flat", "help", "keep", "move", "pick", "save", "feedback", "bbox", "box", "chunk", "region", "net", "short", NULL }; static char *cmdSelectMsg[] = { "[more | less | nocycle] [layers] [at x y]\n" " [de]select paint chunk/region/net under\n" " cursor, or [de]select subcell if cursor\n" " over space", "[more | less] area [layers] [de]select all info under box in layers", "[more | less] visible [layers] [de]select all visible info under box in layers", "[more | less | top] cell [name] [de]select cell under cursor, or \"name\"", "[do | no] labels [do not] select subcell labels", "[more | less] intersection [layers] [de]select intersection of layers", "clear clear selection", "flat flatten the contents of the selection", "help print this message", "keep copy the selection to the layout", "move move selection to a location in the layout", "pick delete selection from layout", "save file save selection on disk in file.mag", "feedback [style] copy selection to feedback", "bbox return the bounding box of the selection", "[more | less] box | chunk | region | net [layers]\n" " [de]select chunk/region/net specified by\n" " the lower left corner of the current box", "short name1 name2 find shorting path between two labels", NULL }; static TileType type = TT_SELECTBASE-1; /* Type of material being pointed at. * Remembered across commands so that when * multiples types are pointed to, consecutive * selections will cycle through them. */ static Rect lastArea = {{-100, -100}, {-200, -200}}; /* Used to remember region around what was * pointed at in the last select command: a * new selection in this area causes the next * bigger thing to be selected. */ static int lastCommand; /* Serial number of last command: the next * bigger thing is only selected when there * are several select commands in a row. */ static Rect chunkSelection; /* Used to remember the size of the last chunk * selected. */ static int level = 0; /* How big a piece to select. See definitions * below. */ static CellUse *lastUse; /* The last cellUse selected. Used to step * through multiple uses underneath the cursor. */ static Point lastIndices; /* The array indices of the last cell selected. * also used to step through multiple uses. */ static bool lessCycle = FALSE, lessCellCycle = FALSE; char path[200], *printPath, **msg, **optionArgs, *feedtext, *pstr, *globmatch; TerminalPath tpath; CellUse *use; CellDef *rootBoxDef; Transform trans, rootTrans, tmp1; Point p, rootPoint, atPoint; Rect r, selarea; ExtRectList *rlist; int option; int feedstyle; int isqual; // Command has a qualifier argument like "more", "less", etc. int primargs; // Number of arguments other than qualifiers bool layerspec; bool degenerate; bool doat = FALSE; bool more = FALSE, less = FALSE, samePlace = TRUE, dotop = FALSE; unsigned char labelpolicy = SEL_DO_LABELS; #ifdef MAGIC_WRAPPER char *tclstr; Tcl_Obj *lobj; #endif /* How close two clicks must be to be considered the same point: */ #define MARGIN 2 globmatch = NULL; bzero(&scx, sizeof(SearchContext)); windCheckOnlyWindow(&w, DBWclientID); if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) { TxError("Put the cursor in a layout window\n"); return; } /* See if "more" was given. If so, just strip off the "more" from * the argument list and set the "more" flag. Similarly for options * "less", "do", "no", "nocycle", "top", and "cell". */ isqual = 0; // Track any qualifier argument if (cmd->tx_argc >= 2) { int arg1len = strlen(cmd->tx_argv[1]); optionArgs = &cmd->tx_argv[1]; if (!strncmp(cmd->tx_argv[1], "more", arg1len)) { more = TRUE; less = FALSE; isqual = 1; optionArgs = &cmd->tx_argv[2]; } else if (!strncmp(cmd->tx_argv[1], "less", arg1len)) { more = FALSE; less = TRUE; isqual = 1; optionArgs = &cmd->tx_argv[2]; } else if (!strncmp(cmd->tx_argv[1], "nocycle", arg1len)) { samePlace = FALSE; labelpolicy = SEL_NO_LABELS; more = FALSE; less = FALSE; isqual = 1; type = TT_SELECTBASE - 1; /* avoid cycling between types */ optionArgs = &cmd->tx_argv[2]; } else if (!strncmp(cmd->tx_argv[1], "same", arg1len)) { /* Force this to be the same as the last selection command, */ /* even if there were other commands in between. */ lastCommand = TxCommandNumber - 1; isqual = 1; optionArgs = &cmd->tx_argv[2]; } else if (!strncmp(cmd->tx_argv[1], "do", arg1len)) { labelpolicy = SEL_DO_LABELS; isqual = 1; optionArgs = &cmd->tx_argv[2]; } else if (!strncmp(cmd->tx_argv[1], "no", arg1len)) { labelpolicy = SEL_NO_LABELS; isqual = 1; optionArgs = &cmd->tx_argv[2]; } else if (!strncmp(cmd->tx_argv[1], "simple", arg1len)) { labelpolicy = SEL_SIMPLE_LABELS; isqual = 1; optionArgs = &cmd->tx_argv[2]; } else if (!strncmp(cmd->tx_argv[1], "top", arg1len)) { if ((cmd->tx_argc >= 3) && !strncmp(cmd->tx_argv[2], "cell", strlen(cmd->tx_argv[2]))) { isqual = 1; dotop = TRUE; optionArgs = &cmd->tx_argv[2]; } } doat = FALSE; if ((cmd->tx_argc - isqual > 3) && !strcmp(cmd->tx_argv[cmd->tx_argc - 3], "at")) { Point editPoint; doat = TRUE; editPoint.p_x = cmdParseCoord(w, cmd->tx_argv[cmd->tx_argc - 2], FALSE, TRUE); editPoint.p_y = cmdParseCoord(w, cmd->tx_argv[cmd->tx_argc - 1], FALSE, FALSE); GeoTransPoint(&EditToRootTransform, &editPoint, &atPoint); /* After registering "doat", ignore the arguments */ cmd->tx_argc -= 3; } } /* Check the option for validity. */ primargs = cmd->tx_argc - isqual; if (primargs == 1) option = SEL_DEFAULT; else { char *fileName; option = Lookup(optionArgs[0], cmdSelectOption); if ((option < 0) && (primargs != 2)) { TxError("\"%s\" isn't a valid select option.\n", cmd->tx_argv[1]); option = SEL_HELP; cmd->tx_argc = 2; primargs = 2; } else if (option < 0) { option = SEL_DEFAULT; if (more || less || (!samePlace)) optionArgs = &cmd->tx_argv[1]; else optionArgs = &cmd->tx_argv[0]; } /* options other than SEL_DEFAULT and the ones that cycle * through (SEL_BOX/CHUNK/REGION/NET) force "level" back * to 0. */ if (option != SEL_BOX && option != SEL_CHUNK && option != SEL_REGION && option != SEL_NET) level = 0; } #ifndef NO_SIM_MODULE SimRecomputeSel = TRUE; #endif switch (option) { /*-------------------------------------------------------------------- * Select everything under the box, perhaps looking only at * particular layers. *-------------------------------------------------------------------- */ case SEL_AREA: if (primargs > 4) { usageError: TxError("Bad arguments:\n select %s\n", cmdSelectMsg[option + 1]); return; } if (primargs == 4) globmatch = optionArgs[2]; /* Label matching by glob */ if (!(more || less)) SelectClear(); if (primargs >= 3) cmdSelectArea(optionArgs[1], less, option, globmatch); else cmdSelectArea("*,label,subcell", less, option, globmatch); return; /*-------------------------------------------------------------------- * Select everything under the box, perhaps looking only at * particular layers, but only if its visible. *-------------------------------------------------------------------- */ case SEL_VISIBLE: if (primargs > 3) goto usageError; if (!(more || less)) SelectClear(); if (primargs == 3) cmdSelectArea(optionArgs[1], less, option, globmatch); else cmdSelectArea("*,label,subcell", less, option, globmatch); return; /*-------------------------------------------------------------------- * Select area that is the intersection of all the specified layers *-------------------------------------------------------------------- */ case SEL_INTERSECT: if (primargs > 3) goto usageError; cmdIntersectArea(optionArgs[1]); return; /*-------------------------------------------------------------------- * Clear out all of the material in the selection. *-------------------------------------------------------------------- */ case SEL_CLEAR: if ((more) || (less) || (primargs > 2)) goto usageError; SelectClear(); return; case SEL_LABELS: SelectDoLabels = labelpolicy; if (SelectDoLabels) TxPrintf("Selection includes subcell labels\n"); else TxPrintf("Selection ignores subcell labels\n"); return; /*-------------------------------------------------------------------- * Print out help information. *-------------------------------------------------------------------- */ case SEL_HELP: TxPrintf("Selection commands are:\n"); for (msg = &(cmdSelectMsg[0]); *msg != NULL; msg++) TxPrintf(" select %s\n", *msg); return; /*-------------------------------------------------------------------- * Print or return the bounding box of the selection *-------------------------------------------------------------------- */ case SEL_BBOX: GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea); #ifdef MAGIC_WRAPPER lobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewIntObj(selarea.r_xbot)); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewIntObj(selarea.r_ybot)); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewIntObj(selarea.r_xtop)); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewIntObj(selarea.r_ytop)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("Select bounding box: %d %d %d %d\n", selarea.r_xbot, selarea.r_ybot, selarea.r_xtop, selarea.r_ytop); #endif return; /*-------------------------------------------------------------------- * Make a copy of the selection at its present loction but do not * clear the selection. *-------------------------------------------------------------------- */ case SEL_KEEP: if ((more) || (less) || (primargs > 2)) goto usageError; SelectAndCopy1(); GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea); DBWHLRedraw(SelectRootDef, &selarea, FALSE); return; /*-------------------------------------------------------------------- * Move the selection relative to the cell def *-------------------------------------------------------------------- */ case SEL_MOVE: if ((more) || (less) || (primargs != 4)) goto usageError; p.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE); p.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE); /* Erase first, then recompute the transform */ GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea); DBWHLRedraw(SelectRootDef, &selarea, TRUE); GeoTransTranslate(p.p_x, p.p_y, &GeoIdentityTransform, &SelectUse->cu_transform); GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea); DBWHLRedraw(SelectRootDef, &selarea, FALSE); return; /*-------------------------------------------------------------------- * Delete the selection from the layout, but don't clear the selection * cell. This allows the selection to be dragged around independently * of the layout. *-------------------------------------------------------------------- */ case SEL_PICK: if ((more) || (less) || (primargs > 2)) goto usageError; SelectDelete("picked", FALSE); DBWHLRedraw(SelectRootDef, &selarea, FALSE); return; /*-------------------------------------------------------------------- * Flatten the contents of the selection cell, but keep the result * within the selection cell. *-------------------------------------------------------------------- */ case SEL_FLAT: if ((more) || (less) || (primargs > 2)) goto usageError; SelectFlat(); return; /*-------------------------------------------------------------------- * Save the selection as a new Magic cell on disk. *-------------------------------------------------------------------- */ case SEL_SAVE: if (primargs != 3) goto usageError; /* Be sure to paint DRC check information into the cell before * saving it! Otherwise DRC problems may not be detected. Also * be sure to adjust labels in the cell. */ DBAdjustLabels(SelectDef, &TiPlaneRect); DBPaintPlane(SelectDef->cd_planes[PL_DRC_CHECK], &SelectDef->cd_bbox, DBStdPaintTbl(TT_CHECKPAINT, PL_DRC_CHECK), (PaintUndoInfo *) NULL); DBUpdateStamps(SelectDef); cmdSaveCell(SelectDef, cmd->tx_argv[2], FALSE, FALSE); return; /*-------------------------------------------------------------------- * Copy the selection into a feedback area for permanent display *-------------------------------------------------------------------- */ case SEL_FEEDBACK: feedtext = NULL; feedstyle = STYLE_ORANGE1; if (primargs > 2) { /* Get style (To do) */ feedstyle = GrGetStyleFromName(cmd->tx_argv[2]); if (feedstyle == -1) { TxError("Unknown style %s\n", cmd->tx_argv[2]); TxError("Use a number or one of the long names in the" " .dstyle file\n"); return; } if (primargs > 3) feedtext = cmd->tx_argv[3]; } SelCopyToFeedback(SelectRootDef, SelectUse, feedstyle, (feedtext == NULL) ? "selection" : feedtext); GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea); DBWHLRedraw(SelectRootDef, &selarea, FALSE); return; /*-------------------------------------------------------------------- * Given a net selection and two labels, determine the shortest * connecting path between the two labels. *-------------------------------------------------------------------- */ case SEL_SHORT: if (primargs != 4) goto usageError; rlist = SelectShort(cmd->tx_argv[2], cmd->tx_argv[3]); if (rlist == NULL) { TxError("No shorting path found between source and destination!\n"); return; } /* Delete selection and replace with contents of rlist */ /* (To do: Alternately just return a list of the contents */ /* of rlist) */ SelectClear(); while (rlist != NULL) { /* Paint rlist back into SelectDef */ DBPaint(SelectDef, &rlist->r_r, rlist->r_type); /* cleanup as we go */ freeMagic(rlist); rlist = rlist->r_next; } /* Force erase and redraw of the selection */ DBReComputeBbox(SelectDef); DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS, (TileTypeBitMask *)NULL); GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea); DBWHLRedraw(SelectRootDef, &selarea, FALSE); break; case SEL_BOX: case SEL_CHUNK: case SEL_REGION: case SEL_NET: if (primargs > 3) goto usageError; if (primargs == 3) layerspec = TRUE; else layerspec = FALSE; goto Okay; /*-------------------------------------------------------------------- * The default case (no args): see what's under the cursor. Select * paint if there is any, else select a cell. In both cases, * multiple clicks cycle through larger and larger selections. The * SEL_CELL option also comes here (to share initialization code) but * quickly branches away. *-------------------------------------------------------------------- */ case SEL_DEFAULT: if (primargs == 2) layerspec = TRUE; else layerspec = FALSE; goto Okay; case SEL_CELL: layerspec = FALSE; degenerate = FALSE; Okay: if (!(more || less)) SelectClear(); if (option == SEL_BOX || option == SEL_CHUNK || option == SEL_REGION || option == SEL_NET) { int windowMask, xMask; if (!ToolGetBox (&rootBoxDef, NULL)) { TxError ("Box tool must be present\n"); return; } window = ToolGetBoxWindow (&scx.scx_area, &windowMask); if (!window) TxError ("Box tool does not exist in any window\n"); xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask; if ((windowMask & ~xMask) != 0) { window = CmdGetRootPoint ((Point *) NULL, (Rect *) NULL); xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask; if ((windowMask & xMask) == 0) { TxError("Box present in multiple windows; use the" "cursor\nto select the one you want\n"); return; } } scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1; scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1; degenerate = TRUE; } else if (doat) { scx.scx_area.r_xbot = atPoint.p_x; scx.scx_area.r_ybot = atPoint.p_y; scx.scx_area.r_xtop = atPoint.p_x + 1; scx.scx_area.r_ytop = atPoint.p_y + 1; windCheckOnlyWindow(&w, DBWclientID); window = w; } else { char *aptr; int i; window = CmdGetRootPoint((Point *) NULL, &scx.scx_area); /* Recast command with "at x y" at the end for logging */ for (i = 0; i < cmd->tx_argc; i++) { aptr = cmd->tx_argv[i] + strlen(cmd->tx_argv[i]); *aptr = ' '; } sprintf(aptr + 1, "at %di %di", scx.scx_area.r_xbot, scx.scx_area.r_ybot); TxRebuildCommand(cmd); } if (window == NULL) return; scx.scx_use = (CellUse *) window->w_surfaceID; scx.scx_trans = GeoIdentityTransform; crec = (DBWclientRec *) window->w_clientData; DBSeeTypesAll(scx.scx_use, &scx.scx_area, crec->dbw_bitmask, &mask); TTMaskAndMask(&mask, &crec->dbw_visibleLayers); TTMaskAndMask(&mask, &DBAllButSpaceAndDRCBits); /* See if we're pointing at the same place as we were the last time * this command was invoked, and if this command immediately follows * another selection comand. If not, it is important to set lastUse * to NULL, otherwise trouble occurs if lastUse is an instance that * was deleted (note that this is not foolproof: deleting an * instance followed by selecting an instance that was occupying the * same space WILL cause a crash). */ if (doat) { if (!GEO_ENCLOSE(&scx.scx_area.r_ll, &lastArea) || ((lastCommand + 1) != TxCommandNumber)) { samePlace = FALSE; lastUse = NULL; } lastArea.r_xbot = scx.scx_area.r_xbot - MARGIN; lastArea.r_ybot = scx.scx_area.r_ybot - MARGIN; lastArea.r_xtop = scx.scx_area.r_xbot + MARGIN; lastArea.r_ytop = scx.scx_area.r_ybot + MARGIN; } else { if (!GEO_ENCLOSE(&cmd->tx_p, &lastArea) || ((lastCommand + 1) != TxCommandNumber)) { samePlace = FALSE; lastUse = NULL; } lastArea.r_xbot = cmd->tx_p.p_x - MARGIN; lastArea.r_ybot = cmd->tx_p.p_y - MARGIN; lastArea.r_xtop = cmd->tx_p.p_x + MARGIN; lastArea.r_ytop = cmd->tx_p.p_y + MARGIN; } lastCommand = TxCommandNumber; /* If there's material under the cursor, select some paint. * Repeated selections at the same place result in first a * chunk being selected, then a region of a particular type, * then a whole net. */ if (!TTMaskIsZero(&mask) && (option != SEL_CELL)) { if (layerspec == TRUE) { /* User specified a layer. Use the smallest type * specified by the user and present under the * box/cursor to begin the selection */ TileTypeBitMask uMask; if (CmdParseLayers (optionArgs[1], &uMask)) { if (TTMaskEqual (&uMask, &DBSpaceBits)) CmdParseLayers ("*,label", &uMask); } else { TxError ("Invalid layer specification\n"); return; } TTMaskAndMask (&mask, &uMask); if (TTMaskIsZero(&mask)) { /* If the area was degenerate (point or line box) */ /* try looking in the other direction before */ /* giving up (added by Tim 3/18/07) */ if (degenerate) { scx.scx_area.r_xtop--; scx.scx_area.r_ytop--; scx.scx_area.r_xbot--; scx.scx_area.r_ybot--; DBSeeTypesAll(scx.scx_use, &scx.scx_area, crec->dbw_bitmask, &mask); TTMaskAndMask(&mask, &crec->dbw_visibleLayers); TTMaskAndMask(&mask, &DBAllButSpaceAndDRCBits); TTMaskAndMask (&mask, &uMask); if (TTMaskIsZero(&mask)) { TxError ("No paint of this type under " "or next to the cursor/box\n"); return; } } else { TxError ("No paint of this type under the cursor/box\n"); return; } } } /* Set connectivity searching level */ if (option == SEL_CHUNK || option == SEL_REGION || option == SEL_NET) { samePlace = TRUE; level = option; if (level != SEL_CHUNK) { int count = 0; for (type++; count <= DBNumUserLayers; type++, count++) { if (type >= DBNumUserLayers) type = TT_SELECTBASE; if (TTMaskHasType(&mask, type)) break; } } } else { if (samePlace && lessCycle == less) { level++; if (level > SEL_NET) level = SEL_CHUNK; } else level = SEL_CHUNK; if ((level == 1) || (level != SEL_CHUNK && !TTMaskHasType (&mask, type))) { /* User specified a new mask, and the current tile * type being expanded is not in the set of types * which the user wants us to use => reset level */ level = SEL_CHUNK; SelectClear(); } } lessCycle = less; if (level == SEL_CHUNK) { /* Pick a tile type to use for selection. If there are * several different types under the cursor, pick one of * them. This code remembers which type was used to * choose last time, so that consecutive selections will * use different types. */ int count = 0; for (type++; count <= DBNumUserLayers; type++, count++) { if (type >= DBNumUserLayers) /* used to be DBNumTypes */ type = TT_SELECTBASE; if (TTMaskHasType(&mask, type)) break; } /* Check for selection between DBNumUserLayers & DBNumTypes */ /* Normally we don't want to use these types; only if one */ /* is the only bit set in the list. */ if (count == DBNumUserLayers) for (type = DBNumUserLayers; type < DBNumTypes; type++) if (TTMaskHasType(&mask, type)) break; /* Sanity check */ if (type == DBNumTypes) return; SelectChunk(&scx, type, crec->dbw_bitmask, &chunkSelection, less); if (!less) DBWSetBox(scx.scx_use->cu_def, &chunkSelection); } if (level == SEL_REGION) { /* If a region has the same size as the preceding chunk, * then we haven't added anything to the selection, so * go on immediately and select the whole net. */ Rect area; SelectRegion(&scx, type, crec->dbw_bitmask, &area, less); if (GEO_SURROUND(&chunkSelection, &area)) level = SEL_NET; } if (level == SEL_NET) { SelectNet(&scx, type, crec->dbw_bitmask, (Rect *) NULL, less); } return; } /*-------------------------------------------------------------------- * We get here either if the SEL_CELL option is requested, or under * the SEL_DEFAULT case where there's no paint under the mouse. In * this case, select a subcell. *-------------------------------------------------------------------- */ if (layerspec == TRUE) { TileTypeBitMask uMask; if (CmdParseLayers (optionArgs[1], &uMask)) { TxError ("No paint of this type under the cursor/box\n"); } else { TxError ("Invalid layer specification\n"); } return; } if (primargs > 3) if (strcmp(cmd->tx_argv[cmd->tx_argc - 3], "at")) goto usageError; /* If an explicit cell use id is provided, look for that cell * and select it. In this case, defeat all of the "multiple * click" code. */ if (((primargs == 3) || (primargs == 6)) && (optionArgs == &cmd->tx_argv[2]) && (more == FALSE) && (less == FALSE)) { use = lastUse = scx.scx_use; p.p_x = scx.scx_use->cu_xlo; p.p_y = scx.scx_use->cu_ylo; trans = GeoIdentityTransform; printPath = scx.scx_use->cu_id; } else if ((primargs == 3) || (primargs == 6)) { SearchContext scx2; bzero(&scx2, sizeof(SearchContext)); DBTreeFindUse(optionArgs[1], scx.scx_use, &scx2); use = scx2.scx_use; if (use == NULL) { TxError("Couldn't find a cell use named \"%s\"\n", optionArgs[1]); return; } trans = scx2.scx_trans; p.p_x = scx2.scx_x; p.p_y = scx2.scx_y; printPath = optionArgs[1]; samePlace = FALSE; lastArea.r_xbot = lastArea.r_ybot = -1000; lastArea.r_xtop = lastArea.r_ytop = -1000; } else { /* Find the cell underneath the cursor. If this is a * second or later click at the same position, select * the "next" cell underneath the point (see comments * in DBSelectCell() for what "next" means). */ tpath.tp_first = tpath.tp_next = path; tpath.tp_last = &path[(sizeof path) - 2]; if ((lastUse == scx.scx_use) || !samePlace || (lessCellCycle != less)) lastUse = NULL; lessCellCycle = less; use = DBSelectCell(scx.scx_use, lastUse, &lastIndices, &scx.scx_area, crec->dbw_bitmask, &trans, &p, &tpath); /* Use the window's root cell if nothing else is found, */ /* or if the command was "select top cell". */ if ((dotop == TRUE) || (use == NULL)) { use = lastUse = scx.scx_use; p.p_x = scx.scx_use->cu_xlo; p.p_y = scx.scx_use->cu_ylo; trans = GeoIdentityTransform; printPath = scx.scx_use->cu_id; } else { printPath = strchr(path, '/'); if (printPath == NULL) printPath = path; else printPath++; } } lastUse = use; lastIndices = p; /* The translation stuff is funny, since we got one * element of the array, but not necessarily the * lower-left element. To get the transform for the * array as a whole, subtract off for the indx of * the element. */ GeoInvertTrans(DBGetArrayTransform(use, p.p_x, p.p_y), &tmp1); GeoTransTrans(&tmp1, &trans, &rootTrans); if (less) SelectRemoveCellUse(use, &rootTrans); else SelectCell(use, scx.scx_use->cu_def, &rootTrans, samePlace); GeoTransRect(&trans, &use->cu_def->cd_bbox, &r); DBWSetBox(scx.scx_use->cu_def, &r); #ifdef MAGIC_WRAPPER /* Remove any backslash escapes so that Tcl_escape() doesn't * double-escape them. */ for (pstr = printPath; *pstr != '\0';) if ((*pstr == '\\') && ((*(pstr + 1) == '[') || (*(pstr + 1) == ']'))) memmove(pstr, pstr + 1, 1 + strlen(pstr + 1)); else pstr++; tclstr = Tcl_escape(printPath); Tcl_SetResult(magicinterp, tclstr, TCL_DYNAMIC); #else TxPrintf("Selected cell is %s (%s)\n", use->cu_def->cd_name, printPath); #endif return; } } /* *----------------------------------------------------------------------- * Callback functions used with CmdSetLabel to change individual labels *----------------------------------------------------------------------- */ int cmdLabelTextFunc(label, cellUse, transform, text) Label *label; CellUse *cellUse; Transform *transform; char *text; { Label *newlab; CellDef *cellDef = cellUse->cu_def; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif if (text == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj(label->lab_text, -1)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%s\n", label->lab_text); #endif } else if (strcmp(text, label->lab_text)) { newlab = DBPutFontLabel(cellDef, &label->lab_rect, label->lab_font, label->lab_size, label->lab_rotate, &label->lab_offset, label->lab_just, text, label->lab_type, label->lab_flags, label->lab_port); DBEraseLabelsByContent(cellDef, &label->lab_rect, -1, label->lab_text); DBWLabelChanged(cellDef, newlab, DBW_ALLWINDOWS); DBWHLRedraw(SelectRootDef, &SelectDef->cd_extended, TRUE); DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS, &DBAllButSpaceBits); DBCellSetModified(cellDef, TRUE); } return 0; } int cmdLabelRotateFunc(label, cellUse, transform, value) Label *label; CellUse *cellUse; Transform *transform; int *value; { CellDef *cellDef = cellUse->cu_def; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif Rect selarea; if (value == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewIntObj(label->lab_rotate)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%d\n", label->lab_rotate); #endif } else if (label->lab_rotate != *value) { DBUndoEraseLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); label->lab_rotate = *value; DBFontLabelSetBBox(label); DBUndoPutLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); DBCellSetModified(cellDef, TRUE); } return 0; } int cmdLabelSizeFunc(label, cellUse, transform, value) Label *label; CellUse *cellUse; Transform *transform; int *value; { CellDef *cellDef = cellUse->cu_def; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif if (value == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewDoubleObj((double)label->lab_size / 8.0)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%g\n", (double)label->lab_size / 8.0); #endif } else if (label->lab_size != *value) { DBUndoEraseLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); label->lab_size = *value; DBFontLabelSetBBox(label); DBUndoPutLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); DBCellSetModified(cellDef, TRUE); } return 0; } int cmdLabelJustFunc(label, cellUse, transform, value) Label *label; CellUse *cellUse; Transform *transform; int *value; { CellDef *cellDef = cellUse->cu_def; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif if (value == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj(GeoPosToName(label->lab_just), -1)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%s\n", GeoPosToName(label->lab_just)); #endif } else if (label->lab_just != *value) { DBUndoEraseLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); label->lab_just = *value; DBFontLabelSetBBox(label); DBUndoPutLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); DBCellSetModified(cellDef, TRUE); } return 0; } int cmdLabelLayerFunc(label, cellUse, transform, value) Label *label; CellUse *cellUse; Transform *transform; int *value; { CellDef *cellDef = cellUse->cu_def; TileType ttype; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif if (value == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj(DBTypeLongNameTbl[label->lab_type], -1)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%s\n", DBTypeLongNameTbl[label->lab_type]); #endif } else { ttype = (TileType)(*value); if (label->lab_type != ttype) { DBUndoEraseLabel(cellDef, label); label->lab_type = ttype; DBUndoPutLabel(cellDef, label); DBCellSetModified(cellDef, TRUE); } } return 0; } int cmdLabelStickyFunc(label, cellUse, transform, value) Label *label; CellUse *cellUse; Transform *transform; int *value; { CellDef *cellDef = cellUse->cu_def; int newvalue; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif if (value == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewBooleanObj((label->lab_flags & LABEL_STICKY) ? 1 : 0)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%s\n", (label->lab_flags & LABEL_STICKY) ? "true" : "false"); #endif } else { newvalue = label->lab_flags; newvalue &= ~LABEL_STICKY; newvalue |= *value; if (newvalue != label->lab_flags) { /* Label does not change appearance, just need to record the change */ DBUndoEraseLabel(cellDef, label); label->lab_flags = newvalue; DBUndoPutLabel(cellDef, label); DBCellSetModified(cellDef, TRUE); } } return 0; } int cmdLabelOffsetFunc(label, cellUse, transform, point) Label *label; CellUse *cellUse; Transform *transform; Point *point; { CellDef *cellDef = cellUse->cu_def; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj, *pobj; #endif if (point == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); pobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(magicinterp, lobj, pobj); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewDoubleObj((double)label->lab_offset.p_x / 8.0)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewDoubleObj((double)label->lab_offset.p_y / 8.0)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%g %g\n", (double)(label->lab_offset.p_x) / 8.0, (double)(label->lab_offset.p_y) / 8.0); #endif } else if (!GEO_SAMEPOINT(label->lab_offset, *point)) { DBUndoEraseLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); label->lab_offset = *point; DBFontLabelSetBBox(label); DBUndoPutLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); DBCellSetModified(cellDef, TRUE); } return 0; } int cmdLabelRectFunc(label, cellUse, transform, rect) Label *label; CellUse *cellUse; Transform *transform; Rect *rect; { CellDef *cellDef = cellUse->cu_def; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj, *pobj; #endif if (rect == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); pobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(magicinterp, lobj, pobj); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj((double)label->lab_rect.r_xbot)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj((double)label->lab_rect.r_ybot)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj((double)label->lab_rect.r_xtop)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj((double)label->lab_rect.r_ytop)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%d %d %d %d\n", label->lab_rect.r_xbot, label->lab_rect.r_ybot, label->lab_rect.r_xtop, label->lab_rect.r_ytop); #endif } else if (!GEO_SAMERECT(label->lab_rect, *rect)) { DBUndoEraseLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); label->lab_rect = *rect; DBFontLabelSetBBox(label); DBUndoPutLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); DBCellSetModified(cellDef, TRUE); } return 0; } int cmdLabelFontFunc(label, cellUse, transform, font) Label *label; CellUse *cellUse; Transform *transform; int *font; { CellDef *cellDef = cellUse->cu_def; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif if (font == NULL) { #ifdef MAGIC_WRAPPER lobj = Tcl_GetObjResult(magicinterp); if (label->lab_font == -1) Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj("default", 7)); else Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj(DBFontList[label->lab_font]->mf_name, -1)); Tcl_SetObjResult(magicinterp, lobj); #else if (label->lab_font == -1) TxPrintf("default\n"); else TxPrintf("%s\n", DBFontList[label->lab_font]->mf_name); #endif } else if (label->lab_font != *font) { DBUndoEraseLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); label->lab_font = *font; if ((*font > -1) && (label->lab_size == 0)) label->lab_size = DBLambda[1]; DBFontLabelSetBBox(label); DBUndoPutLabel(cellDef, label); DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS); DBCellSetModified(cellDef, TRUE); } return 0; } /* * ---------------------------------------------------------------------------- * * CmdSetLabel -- * * Implement the "setlabel" command. * Query or change properties of a (selected) label in the edit cell * * Usage: * setlabel [-default] option [name] * * Option may be one of: * text * font * justify * size * offset * rotate * layer * * Results: * None. * * Side effects: * Modified label entry. The "setlabel font" command may load font * information if the requested font is not already in the font list. * "setlabel font " can be used without any select to load fonts * from a startup script. * * Use with "-default" causes the DefaultLabel structure to be created * (if not existing already) and set with the given value. Subsequent * use of the "label" command will start with the given defaults, then * apply whatever non-default values are specified in the command. * * ---------------------------------------------------------------------------- */ #define SETLABEL_BOX 0 #define SETLABEL_FONT 1 #define SETLABEL_FONTLIST 2 #define SETLABEL_JUSTIFY 3 #define SETLABEL_LAYER 4 #define SETLABEL_OFFSET 5 #define SETLABEL_ROTATE 6 #define SETLABEL_SIZE 7 #define SETLABEL_STICKY 8 #define SETLABEL_TEXT 9 #define SETLABEL_HELP 10 void CmdSetLabel(w, cmd) MagWindow *w; TxCommand *cmd; { int pos = -1, font = -1, size = 0, rotate = 0, flags = 0; int locargc, argstart = 1; char **msg; Point offset; Rect rect; TileType ttype; int option; bool doDefault = FALSE; #ifdef MAGIC_WRAPPER Tcl_Obj *lobj; #endif static char *cmdLabelYesNo[] = { "no", "false", "off", "0", "yes", "true", "on", "1", 0 }; static char *cmdLabelSetOption[] = { "box change/get label box", "font change/get label font", "fontlist list available fonts", "justify change/get label justification", "layer change/get layer type", "offset change/get label offset", "rotate change/get label rotation", "size change/get label size", "sticky [true|false] change/get sticky property", "text change/get label text", "help print this help info", NULL }; locargc = cmd->tx_argc; if (locargc > 2) { if (!strncmp(cmd->tx_argv[1], "-def", 4)) { if (DefaultLabel == NULL) { DefaultLabel = (Label *)mallocMagic(sizeof(Label)); /* Set default defaults (lab_text is ignored) */ DefaultLabel->lab_just = -1; DefaultLabel->lab_size = 0; DefaultLabel->lab_font = -1; DefaultLabel->lab_rotate = 0; DefaultLabel->lab_flags = 0; DefaultLabel->lab_offset.p_x = 0; DefaultLabel->lab_offset.p_y = 0; DefaultLabel->lab_type = (TileType)(-1); } doDefault = TRUE; locargc--; argstart++; } } if (locargc < 2) option = SETLABEL_HELP; else option = Lookup(cmd->tx_argv[argstart], cmdLabelSetOption); if ((option != SETLABEL_BOX) && (locargc > 4)) option = SETLABEL_HELP; else if ((option == SETLABEL_BOX) && (locargc > 6)) option = SETLABEL_HELP; switch (option) { case SETLABEL_FONTLIST: #ifdef MAGIC_WRAPPER lobj = Tcl_NewListObj(0, NULL); for (font = 0; font < DBNumFonts; font++) Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj(DBFontList[font]->mf_name, -1)); Tcl_SetObjResult(magicinterp, lobj); #else for (font = 0; font < DBNumFonts; font++) TxPrintf("%s ", DBFontList[font]->mf_name); TxPrintf("\n"); #endif break; case SETLABEL_TEXT: if (doDefault) { TxError("Cannot set a default label text.\n"); } else if (EditCellUse) { SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelTextFunc, (locargc == 3) ? (ClientData)cmd->tx_argv[argstart + 1] : (ClientData)NULL); } break; case SETLABEL_FONT: if (locargc >= 2 && locargc <= 4) { /* This option is used to see the font name corresponding * to a font number. */ if ((locargc == 3) && StrIsInt(cmd->tx_argv[argstart + 1])) { int font = atoi(cmd->tx_argv[argstart + 1]); if (font < -1 || font >= DBNumFonts) { if (DBNumFonts == 0) TxError("No vector outline fonts are loaded.\n"); else TxError("Font index out of range (0 to %d)\n", DBNumFonts - 1); } else if (font == -1) TxPrintf("default\n"); else TxPrintf("%s\n", DBFontList[font]->mf_name); } else if ((locargc == 3 || locargc == 4) && !StrIsInt(cmd->tx_argv[argstart + 1])) { font = DBNameToFont(cmd->tx_argv[argstart + 1]); if (font < -1) { float scale = 1.0; if ((locargc == 4) && StrIsNumeric(cmd->tx_argv[argstart + 2])) scale = (float)atof(cmd->tx_argv[argstart + 2]); if (DBLoadFont(cmd->tx_argv[argstart + 1], scale) != 0) TxError("Error loading font \"%s\"\n", cmd->tx_argv[argstart + 1]); font = DBNameToFont(cmd->tx_argv[argstart + 1]); if (font < -1) break; } } if (doDefault) { if (locargc == 2) { font = DefaultLabel->lab_font; if (font == -1) #ifdef MAGIC_WRAPPER Tcl_SetResult(magicinterp, "default", TCL_VOLATILE); #else TxPrintf("default\n"); #endif else #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewStringObj(DBFontList[font]->mf_name, -1)); #else TxPrintf("%s\n", DBFontList[font]->mf_name); #endif } else DefaultLabel->lab_font = font; } else if (EditCellUse) { SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelFontFunc, (locargc == 3) ? (ClientData)&font : (ClientData)NULL); } } break; case SETLABEL_JUSTIFY: if (locargc == 3) { pos = GeoNameToPos(cmd->tx_argv[argstart + 1], FALSE, TRUE); if (pos < 0) break; } if (doDefault) { if (locargc == 2) { #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewStringObj(GeoPosToName(DefaultLabel->lab_just), -1)); #else TxPrintf("%s\n", GeoPosToName(DefaultLabel->lab_just)); #endif } else DefaultLabel->lab_just = pos; } else if (EditCellUse) { SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelJustFunc, (locargc == 3) ? (ClientData)&pos : (ClientData)NULL); } break; case SETLABEL_SIZE: if (locargc == 3) { if (StrIsNumeric(cmd->tx_argv[argstart + 1])) size = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8); if (size <= 0) break; } if (doDefault) { if (locargc == 2) { #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(DefaultLabel->lab_size)); #else TxPrintf("%d\n", DefaultLabel->lab_size); #endif } else DefaultLabel->lab_size = size; } else if (EditCellUse) { SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelSizeFunc, (locargc == 3) ? (ClientData)&size : (ClientData)NULL); } break; case SETLABEL_OFFSET: if (locargc == 3) { char *yp; if ((yp = strchr(cmd->tx_argv[argstart + 1], ' ')) != NULL) { *yp = '\0'; offset.p_x = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8); offset.p_y = cmdScaleCoord(w, yp + 1, TRUE, FALSE, 8); } else { TxError("Usage: setlabel offset \n"); return; } } else if (locargc == 4) { offset.p_x = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8); offset.p_y = cmdScaleCoord(w, cmd->tx_argv[argstart + 2], TRUE, FALSE, 8); } if (doDefault) { if (locargc == 2) { #ifdef MAGIC_WRAPPER lobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewIntObj(DefaultLabel->lab_offset.p_x)); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewIntObj(DefaultLabel->lab_offset.p_y)); Tcl_SetObjResult(magicinterp, lobj); #else TxPrintf("%d %d\n", DefaultLabel->lab_offset.p_x, DefaultLabel->lab_offset.p_y); #endif } else DefaultLabel->lab_offset = offset; } else if (EditCellUse) { SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelOffsetFunc, (locargc != 2) ? (ClientData)&offset : (ClientData)NULL); } break; case SETLABEL_BOX: if (doDefault) { TxError("Cannot set a default label box.\n"); } else if (EditCellUse) { if (locargc == 3) { /* Useful with [box values] */ char *yp = cmd->tx_argv[argstart + 1]; rect.r_xbot = cmdScaleCoord(w, yp, TRUE, TRUE, 1); yp = strchr(yp, ' '); if (yp != NULL) { *yp = '\0'; yp++; while (isspace(*yp) && (*yp != '\0')) yp++; rect.r_ybot = cmdScaleCoord(w, yp, TRUE, FALSE, 1); yp = strchr(yp, ' '); if (yp != NULL) { *yp = '\0'; yp++; while (isspace(*yp) && (*yp != '\0')) yp++; rect.r_xtop = cmdScaleCoord(w, yp, TRUE, TRUE, 1); yp = strchr(yp, ' '); if (yp != NULL) { *yp = '\0'; yp++; while (isspace(*yp) && (*yp != '\0')) yp++; rect.r_ytop = cmdScaleCoord(w, yp, TRUE, FALSE, 1); } else TxError("Label box requires four coordinates.\n"); } else { rect.r_xtop = rect.r_xbot; rect.r_ytop = rect.r_ybot; yp = cmd->tx_argv[argstart + 1]; } } else TxError("Label box requires four coordinates.\n"); if (yp == NULL) break; /* Error condition */ } else if (locargc == 6) { rect.r_xbot = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 1); rect.r_ybot = cmdScaleCoord(w, cmd->tx_argv[argstart + 2], TRUE, FALSE, 1); rect.r_xtop = cmdScaleCoord(w, cmd->tx_argv[argstart + 3], TRUE, TRUE, 1); rect.r_ytop = cmdScaleCoord(w, cmd->tx_argv[argstart + 4], TRUE, FALSE, 1); } SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelRectFunc, ((locargc == 6) || (locargc == 3)) ? (ClientData)&rect : (ClientData)NULL); } break; case SETLABEL_ROTATE: if (locargc == 3) { if (StrIsInt(cmd->tx_argv[argstart + 1])) rotate = atoi(cmd->tx_argv[argstart + 1]); } if (doDefault) { if (locargc == 2) { #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(DefaultLabel->lab_rotate)); #else TxPrintf("%d\n", DefaultLabel->lab_rotate); #endif } else DefaultLabel->lab_rotate = rotate; } else if (EditCellUse) { SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelRotateFunc, (locargc == 3) ? (ClientData)&rotate : (ClientData)NULL); } break; case SETLABEL_STICKY: if (locargc == 3) { option = Lookup(cmd->tx_argv[argstart + 1], cmdLabelYesNo); if (option < 0) { TxError("Unknown sticky option \"%s\"\n", cmd->tx_argv[argstart + 1]); break; } flags = (option <= 3) ? 0 : LABEL_STICKY; } if (doDefault) { if (locargc == 2) { #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj((DefaultLabel->lab_flags & LABEL_STICKY) ? TRUE : FALSE)); #else TxPrintf("%d\n", (DefaultLabel->lab_flags & LABEL_STICKY) ? 1 : 0); #endif } else DefaultLabel->lab_flags = flags; } else if (EditCellUse) { SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelStickyFunc, (locargc == 3) ? (ClientData)&flags : (ClientData)NULL); } break; case SETLABEL_LAYER: if (locargc == 3) { if (!strcasecmp(cmd->tx_argv[argstart + 1], "default")) ttype = -1; else { ttype = DBTechNoisyNameType(cmd->tx_argv[argstart + 1]); if (ttype < 0) break; } } if (doDefault) { if (locargc == 2) { if (DefaultLabel->lab_type == (TileType)(-1)) #ifdef MAGIC_WRAPPER Tcl_SetResult(magicinterp, "default", TCL_VOLATILE); #else TxPrintf("default\n"); #endif else #ifdef MAGIC_WRAPPER Tcl_SetResult(magicinterp, DBTypeLongNameTbl[DefaultLabel->lab_type], TCL_VOLATILE); #else TxPrintf("%s\n", DBTypeLongNameTbl[DefaultLabel->lab_type]); #endif } else DefaultLabel->lab_type = ttype; } else if (EditCellUse) { SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL, cmdLabelLayerFunc, (locargc == 3) ? (ClientData)&ttype : (ClientData)NULL); } break; case SETLABEL_HELP: TxError("Usage: setlabel [option], where [option] is one of:\n"); for (msg = &(cmdLabelSetOption[0]); *msg != NULL; msg++) { TxError(" %s\n", *msg); } break; default: TxError("Unknown setlabel option \"%s\"\n", cmd->tx_argv[argstart]); break; } } /* * ---------------------------------------------------------------------------- * * CmdSideways -- * * Implement the "sideways" command. * * Usage: * sideways * * Results: * None. * * Side effects: * The selection and box are flipped left-to-right, using the * center of the selection as the axis for flipping. * * ---------------------------------------------------------------------------- */ void CmdSideways(w, cmd) MagWindow *w; TxCommand *cmd; { Transform trans; Rect rootBox, bbox; CellDef *rootDef; if (cmd->tx_argc != 1) { TxError("Usage: %s\n", cmd->tx_argv[0]); return; } if (!ToolGetEditBox((Rect *)NULL)) return; /* To flip the selection sideways, first flip it around the * y-axis, then move it back so its lower-left corner is in * the same place that it used to be. */ GeoTransRect(&GeoSidewaysTransform, &SelectDef->cd_bbox, &bbox); GeoTranslateTrans(&GeoSidewaysTransform, SelectDef->cd_bbox.r_xbot - bbox.r_xbot, SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans); SelectTransform(&trans); /* Flip the box, if it exists and is in the same window as the * selection. */ if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef)) { Rect newBox; GeoTransRect(&trans, &rootBox, &newBox); DBWSetBox(rootDef, &newBox); } return; } /* * ---------------------------------------------------------------------------- * * CmdShell * * Implement the "shell" or "!" command. * * Usage: * shell [command] * * Results: * Executes the command in a unix shell * * Side effects: * May alter unix files * * ---------------------------------------------------------------------------- */ void CmdShell(w, cmd) MagWindow *w; TxCommand *cmd; { int i, cmdlength; char *command; if (cmd->tx_argc != 1) { cmdlength = 1; for (i = 1; i < cmd->tx_argc; i++) { cmdlength = cmdlength + strlen(cmd->tx_argv[i]) + 1; } command = mallocMagic((unsigned) (cmdlength)); (void) strcpy(command, cmd->tx_argv[1]); for (i = 2; i < cmd->tx_argc; i++) { strcat(command, " "); strcat(command, cmd->tx_argv[i]); } system(command); freeMagic(command); } } /* * ---------------------------------------------------------------------------- * * CmdSgraph * * Implement the "sgraph" command. * * Usage: * sgraph [off|add|delete|debug] * sgraph [show|auto] [vertical|horizontal] * * Results: * None. * * Side effects: * Calls the 'stretchgraph' module, if present. * * ---------------------------------------------------------------------------- */ #ifdef LLNL int (*CmdStretchCmd)() = NULL; /* ARGSUSED */ void CmdSgraph(w, cmd) MagWindow *w; TxCommand *cmd; { if (CmdStretchCmd != NULL) { (*CmdStretchCmd)(w, cmd); } else { TxError("Sorry, the sgraph command doesn't work in this version.\n"); TxError("(Magic was not linked with stretchgraph module.)\n"); } } #endif /* LLNL */ #if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE) /* * ---------------------------------------------------------------------------- * * CmdStartRsim * * This command starts Rsim under Magic, escapes Rsim, and returns * back to Magic. * * Results: * Rsim is forked from Magic. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ void CmdStartRsim(w, cmd) MagWindow *w; TxCommand *cmd; { static char rsimstr[] = "rsim"; if ((cmd->tx_argc == 1) && (!SimRsimRunning)) { TxPrintf("usage: startrsim [options] file\n"); return; } if ((cmd->tx_argc != 1) && (SimRsimRunning)) { TxPrintf("Simulator already running. You cannont start another.\n"); return; } windCheckOnlyWindow(&w, DBWclientID); if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) { TxError("Put the cursor in a layout window.\n"); return; } /* change argv[0] to be "rsim" and send it to Rsim_start */ cmd->tx_argv[0] = rsimstr; if (cmd->tx_argc != 1) { cmd->tx_argv[cmd->tx_argc] = (char *) 0; SimStartRsim(cmd->tx_argv); } SimConnectRsim(TRUE); } /* * ---------------------------------------------------------------------------- * * CmdSimCmd * * Applies the given rsim command to the currently selected nodes. * * Results: * Whatever rsim replys to the commands input. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ void CmdSimCmd(w, cmd) MagWindow *w; TxCommand *cmd; { static char cmdbuf[200]; char *strptr; char *nodeCmd; int i; if (!SimRsimRunning) { TxPrintf("You must first start the simulator by using the rsim command.\n"); return; } if (cmd->tx_argc == 1) { TxPrintf("usage: simcmd command [options]\n"); return; } windCheckOnlyWindow(&w, DBWclientID); if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) { TxError("Put the cursor in a layout window.\n"); return; } /* check to see whether to apply the command to each node selected, * or whether to just ship the command to rsim without any node * names. */ nodeCmd = SimGetNodeCommand( cmd->tx_argv[1] ); strcpy( cmdbuf, (nodeCmd != NULL) ? nodeCmd : cmd->tx_argv[1] ); strptr = cmdbuf + strlen(cmdbuf); *strptr++ = ' '; *strptr = '\0'; for (i = 2; i <= cmd->tx_argc - 1; i++) { strcpy(strptr, cmd->tx_argv[i]); strcat(strptr, " "); strptr += strlen(strptr) + 1; } if (nodeCmd != NULL) { SimSelection(cmdbuf); } else { SimRsimIt(cmdbuf, ""); while (TRUE) { if (!SimGetReplyLine(&strptr)) { break; } if (!strptr) { break; } TxPrintf("%s\n", strptr); } } } #endif /* * ---------------------------------------------------------------------------- * * CmdSnap -- * * Set the box snapping to align either with the nearest user-defined grid, * the nearest integer lambda value, or turn snapping off (aligns to internal * units). * * Results: * None. * * Side effects: * See above. * * ---------------------------------------------------------------------------- */ #define SNAP_OFF 0 #define SNAP_INTERNAL 1 #define SNAP_LAMBDA 2 #define SNAP_GRID 3 #define SNAP_USER 4 #define SNAP_ON 5 #define SNAP_LIST 6 void CmdSnap(w, cmd) MagWindow *w; TxCommand *cmd; { static char *names[] = { "off", "internal", "lambda", "grid", "user", "on", "list", 0 }; int n = SNAP_LIST; DBWclientRec *crec; if (cmd->tx_argc < 2) goto printit; n = Lookup(cmd->tx_argv[1], names); if (n < 0) { TxPrintf("Usage: snap [internal | lambda | user]\n"); return; } switch (n) { case SNAP_OFF: case SNAP_INTERNAL: DBWSnapToGrid = DBW_SNAP_INTERNAL; return; case SNAP_LAMBDA: DBWSnapToGrid = DBW_SNAP_LAMBDA; return; case SNAP_GRID: case SNAP_USER: case SNAP_ON: DBWSnapToGrid = DBW_SNAP_USER; return; } printit: if (n == SNAP_LIST) /* list */ #ifdef MAGIC_WRAPPER Tcl_SetResult(magicinterp, (DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" : ((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"), TCL_VOLATILE); #else TxPrintf("%s\n", (DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" : ((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user")); #endif else TxPrintf("Box is aligned to %s grid\n", (DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" : ((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user")); } /* * ---------------------------------------------------------------------------- * * CmdSplit -- * * Split a tile with a diagonal (nonmanhattan geometry) * * Results: * None. * * Side effects: * Modifies the geometry of the edit cell. * * ---------------------------------------------------------------------------- */ void CmdSplit(w, cmd) MagWindow *w; TxCommand *cmd; { Rect editRect, expRect; TileTypeBitMask mask1, mask2, *cmask; TileType t, dir, side, diag; int pNum, direction; PaintUndoInfo ui; windCheckOnlyWindow(&w, DBWclientID); if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) { TxError("Put the cursor in a layout window\n"); return; } if (cmd->tx_argc != 3 && cmd->tx_argc != 4) { TxError("Usage: %s dir layer [layer2]\n", cmd->tx_argv[0]); return; } if (!ToolGetEditBox(&editRect)) return; if (!CmdParseLayers(cmd->tx_argv[2], &mask1)) return; /* Remove any inactive layers */ TTMaskAndMask(&mask1, &DBActiveLayerBits); if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0) return; if (GEO_RECTNULL(&editRect)) return; /* Nothing to do */ if (cmd->tx_argc == 4) { if (!CmdParseLayers(cmd->tx_argv[3], &mask2)) return; TTMaskClearType(&mask2, TT_SPACE); } else TTMaskZero(&mask2); TTMaskClearType(&mask1, TT_SPACE); direction = (direction >> 1) - 1; dir = (direction & 0x1) ? 0 : TT_DIRECTION; for (t = TT_SPACE + 1; t < DBNumTypes; t++) { side = (direction & 0x2) ? 0 : TT_SIDE; for (cmask = &mask1; cmask != NULL; cmask = ((cmask == &mask1) ? &mask2 : NULL)) { if (cmask == &mask2) side = (side) ? 0 : TT_SIDE; diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform); if (TTMaskHasType(cmask, t)) { EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP; ui.pu_def = EditCellUse->cu_def; for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++) if (DBPaintOnPlane(t, pNum)) { ui.pu_pNum = pNum; DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum], diag, &editRect, DBStdPaintTbl(t, pNum), &ui); GEO_EXPAND(&editRect, 1, &expRect); DBMergeNMTiles(EditCellUse->cu_def->cd_planes[pNum], &expRect, &ui); } } } } SelectClear(); DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask1); DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask2); DBReComputeBbox(EditCellUse->cu_def); DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect); } /* * ---------------------------------------------------------------------------- * * CmdSplitErase -- * * Erase a diagonal section from a tile (nonmanhattan geometry) * * Results: * None. * * Side effects: * Modifies the geometry of the edit cell. * * ---------------------------------------------------------------------------- */ void CmdSplitErase(w, cmd) MagWindow *w; TxCommand *cmd; { Rect editRect, expRect; TileTypeBitMask mask; TileType t, dir, side, diag; int pNum, direction; PaintUndoInfo ui; windCheckOnlyWindow(&w, DBWclientID); if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) { TxError("Put the cursor in a layout window\n"); return; } if (cmd->tx_argc != 2 && cmd->tx_argc != 3) { TxError("Usage: %s dir [layer]\n", cmd->tx_argv[0]); return; } if (!ToolGetEditBox(&editRect)) return; if (GEO_RECTNULL(&editRect)) return; /* Nothing to do */ if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0) return; if (cmd->tx_argc == 2) (void) CmdParseLayers("*", &mask); else if (!CmdParseLayers(cmd->tx_argv[2], &mask)) return; if (TTMaskEqual(&mask, &DBSpaceBits)) (void) CmdParseLayers("*,label", &mask); TTMaskClearType(&mask, TT_SPACE); if (TTMaskIsZero(&mask)) return; /* Remove any inactive layers */ TTMaskAndMask(&mask, &DBActiveLayerBits); direction = (direction >> 1) - 1; dir = (direction & 0x1) ? 0 : TT_DIRECTION; for (t = TT_SPACE + 1; t < DBNumTypes; t++) { side = (direction & 0x2) ? 0 : TT_SIDE; diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform); if (TTMaskHasType(&mask, t)) { EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP; ui.pu_def = EditCellUse->cu_def; for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++) if (DBPaintOnPlane(t, pNum)) { ui.pu_pNum = pNum; DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum], diag, &editRect, DBStdEraseTbl(t, pNum), &ui); GEO_EXPAND(&editRect, 1, &expRect); DBMergeNMTiles(EditCellUse->cu_def->cd_planes[pNum], &expRect, &ui); } } } SelectClear(); DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask); DBReComputeBbox(EditCellUse->cu_def); DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect); } /* * ---------------------------------------------------------------------------- * * CmdStretch -- * * Implement the "stretch" command. * * Usage: * stretch [direction [distance]] * * Results: * None. * * Side effects: * Moves everything that's currently selected, erases material that * the selection would sweep over, and fills in material behind the * selection. * * ---------------------------------------------------------------------------- */ void CmdStretch(w, cmd) MagWindow *w; TxCommand *cmd; { Transform t; Rect rootBox, newBox; CellDef *rootDef; int xdelta, ydelta; if (cmd->tx_argc > 3) { TxError("Usage: %s [direction [amount]]\n", cmd->tx_argv[0]); return; } if (cmd->tx_argc > 1) { int indx, amountx, amounty; if (!ToolGetEditBox((Rect *)NULL)) return; indx = GeoNameToPos(cmd->tx_argv[1], TRUE, TRUE); if (indx < 0) return; if (cmd->tx_argc >= 3) { switch (indx) { case GEO_EAST: case GEO_WEST: amountx = cmdParseCoord(w, cmd->tx_argv[2], TRUE, TRUE); amounty = 0; break; case GEO_NORTH: case GEO_SOUTH: amountx = 0; amounty = cmdParseCoord(w, cmd->tx_argv[2], TRUE, FALSE); break; default: amountx = amounty = 0; /* Should not happen */ break; } } else { amountx = cmdParseCoord(w, "1l", TRUE, TRUE); amounty = cmdParseCoord(w, "1l", TRUE, FALSE); } switch (indx) { case GEO_NORTH: xdelta = 0; ydelta = amounty; break; case GEO_SOUTH: xdelta = 0; ydelta = -amounty; break; case GEO_EAST: xdelta = amountx; ydelta = 0; break; case GEO_WEST: xdelta = -amountx; ydelta = 0; break; default: ASSERT(FALSE, "Bad direction in CmdStretch"); return; } GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t); /* Move the box by the same amount as the selection, if the * box exists. */ if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef)) { GeoTransRect(&t, &rootBox, &newBox); DBWSetBox(rootDef, &newBox); } } else { /* Use the displacement between the box lower-left corner and * the point as the transform. Round off to a Manhattan distance. */ Point rootPoint; MagWindow *window; int absX, absY; if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef)) { TxError("\"Stretch\" uses the box lower-left corner as a place\n"); TxError(" to pick up the selection for stretching, but the\n"); TxError(" box isn't in a window containing the selection.\n"); return; } window = ToolGetPoint(&rootPoint, (Rect *) NULL); if ((window == NULL) || (EditRootDef != ((CellUse *) window->w_surfaceID)->cu_def)) { TxError("\"Stretch\" uses the point as the place to put down a\n"); TxError(" the selection, but the point doesn't point to the\n"); TxError(" edit cell.\n"); return; } xdelta = rootPoint.p_x - rootBox.r_xbot; ydelta = rootPoint.p_y - rootBox.r_ybot; if (xdelta < 0) absX = -xdelta; else absX = xdelta; if (ydelta < 0) absY = -ydelta; else absY = ydelta; if (absY <= absX) ydelta = 0; else xdelta = 0; GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t); GeoTransRect(&t, &rootBox, &newBox); DBWSetBox(rootDef, &newBox); /* Recast the command in the usual 3-argument form for logging */ if (ydelta > 0) sprintf(cmd->tx_argstring, "stretch n %di", ydelta); else if (ydelta < 0) sprintf(cmd->tx_argstring, "stretch s %di", -ydelta); else if (xdelta > 0) sprintf(cmd->tx_argstring, "stretch e %di", xdelta); else sprintf(cmd->tx_argstring, "stretch w %di", -xdelta); TxRebuildCommand(cmd); } SelectStretch(xdelta, ydelta); }