/* windMain.c - * * This package implements overlapping windows for the * Magic VLSI layout system. * * Design: * Windows are structures that are kept in a doubly linked list. * Windows near the front of the list are on top of windows further * towards the tail of the list. Each window has some information * about what is in the window, as well as the size of the window * (both unclipped and clipped to accommodate windows that overlay it). * Transforms control what portion of the window's contents show up * on the screen, and at what magnification. * * Each window is owned by a client (the database, a menu package, etc.). * The window package is notified of a new client by the AddClient * call. The client supplies routines to redisplay the contents of * a window, and to do other things with the window (such as delete it). * Each window is a view onto a surface maintained by the client. The * client may be asked to redisplay any part of this surface at any time. * The client must also supply each window with an ID that uniquely * identifies the surface. * * There are currently two types of window packages supported: Magic * windows (implemented here) and Sun Windows. In Magic windows, all * windows use the screen's coordinate system. In Sun Windows, each * window has it's own coordinate system with (0, 0) being at the lower * left corner. Also, under Sun Windows some of the screen managment * stuff (such as clipping to obscuring areas and drawing of the * screen background color) is ignored by us. * * * ********************************************************************* * * 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$"; #endif /* not lint */ #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "graphics/glyphs.h" #include "windows/windows.h" #include "windows/windInt.h" #include "utils/stack.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "textio/textio.h" #include "graphics/graphics.h" #include "utils/malloc.h" #include "utils/utils.h" #include "textio/txcommands.h" /* The type of windows that this package will implement */ int WindPackageType = WIND_MAGIC_WINDOWS; /* The size of our scroll bars -- may be set externally (see windows.h) */ int WindScrollBarWidth = 7; /* ------ Internal variables that are global within the window package ----- */ clientRec *windFirstClientRec = NULL; /* the head of the linked list * of clients */ MagWindow *windTopWindow = NULL; /* the topmost window */ MagWindow *windBottomWindow = NULL; /* ...and the bottom window */ extern Plane *windRedisplayArea; /* See windDisplay.c for details. */ /* * ---------------------------------------------------------------------------- * WindInit -- * * Initialize the window package. No windows are created, but the * package will be initialized so that it can do these things in the * future. * * Results: * None. * * Side effects: * Variables internal to the window package are initialized. * ---------------------------------------------------------------------------- */ void WindInit() { extern Stack *windGrabberStack; Rect ts; char glyphName[30]; windClientInit(); windGrabberStack = StackNew(2); windRedisplayArea = DBNewPlane((ClientData) TT_SPACE); sprintf(glyphName, "windows%d", WindScrollBarWidth); if (!GrReadGlyphs(glyphName, ".", SysLibPath, &windGlyphs)) MainExit(1); GrTextSize("XWyqP", GR_TEXT_DEFAULT, &ts); windCaptionPixels = ts.r_ytop - ts.r_ybot + 3; WindAreaChanged((MagWindow *) NULL, (Rect *) NULL); } /* * ---------------------------------------------------------------------------- * WindAddClient -- * * Add a new client of the window package. The client must supply a * set of routines, as described below. * * Results: * A unique ID (of type WindClient) is returned. * This is used to identify the client in future calls to the window * package. * * Routines supplied: * * ( A new window was just created for this client. Do things to * initialize the window, such as filling in the caption and making * the contents be empty. The client may refuse to create a new * window by returning FALSE, otherwise the client should return * TRUE. The client will get passed argc and argv, with the command * name stripped off. The client may do whatever it wants with this. * It may even modify parts of the window record -- such as changing * the window's location on the screen.) * * bool * create(w, argc, argv) * MagWindow *w; * int argc; * char *argv[]; * { * } * * ( One of the client's windows is about to be deleted. Do whatever * needs to be done, such as freeing up dynamically allocated data * structures. Fields manipulated by the window package, such as * the caption, should not be freed by the client. The client should * normally return TRUE. If the client returns FALSE, the window * manager will refuse the request to delete the window.) * * bool * delete(w) * MagWindow *w; * { * } * * ( Redisplay an area of the screen. The client is passed the window, * the area in his coordinate system, and a clipping rectangle in * screen coordinates. ) * * redisplay(w, clientArea, screenArea) * MagWindow *w; * Rect *clientArea, *screenArea; * { * } * * * ( The window is about to be moved or resized. This procedure will * be called twice. * * The first time (with 'final' == FALSE), the window * will be passed in 'w' as it is now and a suggested new w_screenarea * is passed in 'newpos'. The client is free to modify 'newpos' to * be whatever screen location it desires. The routine should not * pass 'w' to any window procedure such as windMove since 'w' has * the old transform, etc. instead of the new one. * * On the second call ('final' == TRUE), the window 'w' has all of * it's fields updated, newpos is equal to w->w_frameArea, and the * client is free to do things like windMove which require a window * as an argument. It should not modify newpos. * * reposition(w, newpos, final) * MagWindow *w; * Rect *newpos -- new w_framearea (screen area of window) * bool final; * { * } * * * ( A command has been issued to this window. The client should * process it. It is split into Unix-style argc and argv words. ) * * command(w, client, cmd) * MagWindow *w; * TxCommand *cmd; * { * } * * ( A command has just finished. Update any screen info that may have * been changed as a result. ) * * update() * { * } * * Side effects: * Internal tables are expanded to include the new client. * ---------------------------------------------------------------------------- */ WindClient WindAddClient(clientName, create, delete, redisplay, command, update, exitproc, reposition, icon) char *clientName; /* A textual name for the client. This * name will be visable in the user * interface as the name to use to switch * a window over to a new client */ bool (*create)(); bool (*delete)(); void (*redisplay)(); void (*command)(); void (*update)(); bool (*exitproc)(); void (*reposition)(); GrGlyph *icon; /* An icon to draw when the window is closed. * (currently for Sun Windows only). */ { clientRec *res; ASSERT( (clientName != NULL), "WindAddClient"); ASSERT( (command != NULL), "WindAddClient"); res = (clientRec *) mallocMagic(sizeof(clientRec)); res->w_clientName = clientName; res->w_create = create; res->w_delete = delete; res->w_redisplay = redisplay; res->w_command = command; res->w_update = update; res->w_exit = exitproc; res->w_reposition = reposition; res->w_icon = icon; res->w_nextClient = windFirstClientRec; /* The command and function tables are dynamically allocated. */ /* Commands and functions should be registered with the client */ /* using the WindAddCommand() function. */ res->w_commandTable = (char **)mallocMagic(sizeof(char *)); *(res->w_commandTable) = NULL; res->w_functionTable = (void (**)())mallocMagic(sizeof(void (*)())); *(res->w_functionTable) = NULL; windFirstClientRec = res; return (WindClient) res; } /* * ---------------------------------------------------------------------------- * WindGetClient -- * * Looks up the unique ID of a client of the window package. * * Results: * A variable of type WindClient is returned if the client was found, * otherwise NULL is returned. * * Side effects: * None. * ---------------------------------------------------------------------------- */ WindClient WindGetClient(clientName, exact) char *clientName; /* the textual name of the client */ bool exact; /* must the name be exact, or are abbreviations allowed */ { clientRec *cr, *found; int length; /* Accept only an exact match */ if (exact) { for (cr = windFirstClientRec; cr != (clientRec *) NULL; cr = cr->w_nextClient) if (!strcmp(clientName, cr->w_clientName)) return (WindClient)cr; return (WindClient) NULL; } /* Accept any unique abbreviation */ found = NULL; length = strlen(clientName); for (cr = windFirstClientRec; cr != (clientRec *) NULL; cr = cr->w_nextClient) { if (!strncmp(clientName, cr->w_clientName, length)) { if (found != NULL) return (WindClient) NULL; found = cr; } } return (WindClient) found; } /* * ---------------------------------------------------------------------------- * WindNextClient -- * * Return the w_nextClient record to the caller as a WindClient * variable. If "client" is 0, pass the first client record. * This allows the calling routine to enumerate all the known * clients. * * Results: * Type WindClient is returned. If the end of the list is * reached, (WindClient)NULL (0) is returned. * * Side effects: * None. * ---------------------------------------------------------------------------- */ WindClient WindNextClient(client) WindClient client; { clientRec *cr = (clientRec *)client; int length; if (cr == NULL) return (WindClient)windFirstClientRec; else return (WindClient)(cr->w_nextClient); } /* * ---------------------------------------------------------------------------- * WindPrintClientList -- * * Print the name of each client of the window package. * * Results: * None. * * Side effects: * None. * ---------------------------------------------------------------------------- */ void WindPrintClientList(wizard) bool wizard; /* If true print the names of ALL clients, even those * that don't have user-visable windows */ { clientRec *cr; for (cr = windFirstClientRec; cr != (clientRec *) NULL; cr = cr->w_nextClient) { if (wizard || (cr->w_clientName[0] != '*')) TxError(" %s\n", cr->w_clientName); } } /* * ---------------------------------------------------------------------------- * WindExecute -- * * Execute the command associated with a windClient * * Results: * Returns the command index on success. Returns -1 if the * command was not found in the client's command list. Returns * -2 if the procedure was sent an empty command. * * Side effects: * Whatever is done by the command execution. * * ---------------------------------------------------------------------------- */ int WindExecute(w, rc, cmd) MagWindow *w; WindClient rc; TxCommand *cmd; { int cmdNum; clientRec *client = (clientRec *) rc; char **commandTable = client->w_commandTable; void (**functionTable)() = client->w_functionTable; if (cmd->tx_argc > 0) { cmdNum = Lookup(cmd->tx_argv[0], commandTable); if (cmdNum >= 0) { (*functionTable[cmdNum])(w, cmd); return cmdNum; } return -1; } return -2; } /* * ---------------------------------------------------------------------------- * WindAddCommand -- * * Add a command to the indicated client. The command is passed * in "text", which also contains the (1-line) help text for the * command. "func" is a function pointer, and "volatile" is TRUE * if the command "text" is dynamically allocated and must be * copied before adding to the client. * * Results: * None * * Side effects: * The memory allocated to the command and function pointers may * be reallocated and the entries in the client record updated. * * ---------------------------------------------------------------------------- */ void WindAddCommand(rc, text, func, dynamic) WindClient rc; char *text; void (*func)(); bool dynamic; { int cidx, numCommands = 0; clientRec *client = (clientRec *) rc; char **commandTable = client->w_commandTable; void (**functionTable)() = client->w_functionTable; char **newcmdTable; void (**newfnTable)(); /* Find the number of commands and functions, increment by one, and */ /* Allocate a new array of pointers. */ while (commandTable[numCommands] != NULL) numCommands++; numCommands++; newcmdTable = (char **)mallocMagic((numCommands + 1) * sizeof(char *)); newfnTable = (void (**)())mallocMagic((numCommands + 1) * sizeof(void (*)())); /* Copy the old values, inserting the new command in alphabetical */ /* order. */ for (cidx = 0; (commandTable[cidx] != NULL) && (strcmp(commandTable[cidx], text) < 0); cidx++) { newcmdTable[cidx] = commandTable[cidx]; newfnTable[cidx] = functionTable[cidx]; } if (dynamic) newcmdTable[cidx] = StrDup(NULL, text); else newcmdTable[cidx] = text; newfnTable[cidx] = func; for (; commandTable[cidx] != NULL; cidx++) { newcmdTable[cidx + 1] = commandTable[cidx]; newfnTable[cidx + 1] = functionTable[cidx]; } newcmdTable[cidx + 1] = NULL; /* Release memory for the original pointers, and replace the */ /* pointers in the client record. */ freeMagic(commandTable); freeMagic(functionTable); client->w_commandTable = newcmdTable; client->w_functionTable = newfnTable; } /* * ---------------------------------------------------------------------------- * WindReplaceCommand -- * * Change the function for the indicated command. This routine * is mainly used by the Tcl module interface, where commands * are registered with the command interpreter pointing to an * "auto" function which loads the module, then calls this routine * to replace the auto-load function with the real one. * * Note that this routine matches to the length of "command", then * checks one character beyond in the command table to ensure that * we don't inadvertently change a command which happens to be a * substring of the intended command. In cases where this is * intended (e.g., "ext" and "extract"), the routine must be called * separately for each command string. * * Results: * 0 on success, -1 if the command was not found. * * Side effects: * The clientRec structure for the DBWind interface is altered. * * ---------------------------------------------------------------------------- */ int WindReplaceCommand(rc, command, newfunc) WindClient rc; char *command; void (*newfunc)(); { int cidx, clen; clientRec *client = (clientRec *) rc; char **commandTable = client->w_commandTable; void (**functionTable)() = client->w_functionTable; clen = strlen(command); for (cidx = 0; commandTable[cidx] != NULL; cidx++) if (!strncmp(commandTable[cidx], command, clen)) if (!isalnum(*(commandTable[cidx] + clen))) { functionTable[cidx] = newfunc; return 0; } return -1; } /* * ---------------------------------------------------------------------------- * WindGetCommandTable -- * * For functions wishing to parse the command table of a client * directly, this routine returns a pointer to the top of the * table. The only purpose of this routine is to export the * w_commandTable value inside the clientRec structure, which is * not itself exported. * * Results: * A pointer to the top of the command table of the indicated * client. * * Side effects: * None. * ---------------------------------------------------------------------------- */ char ** WindGetCommandTable(rc) WindClient rc; { clientRec *client = (clientRec *) rc; return client->w_commandTable; }