home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / lib / tcl / demos / tkSquare.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-29  |  15.4 KB  |  532 lines

  1. /* 
  2.  * tkSquare.c --
  3.  *
  4.  *    This module implements "square" widgets.  A "square" is
  5.  *    a widget that displays a single square that can be moved
  6.  *    around and resized.  This file is intended as an example
  7.  *    of how to build a widget.
  8.  *
  9.  * Copyright 1991-1992 Regents of the University of California.
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /user6/ouster/wish/scripts/demos/RCS/tkSquare.c,v 1.2 92/04/29 11:45:17 ouster Exp $ SPRITE (Berkeley)";
  21. #endif
  22.  
  23. #include "tkConfig.h"
  24. #include "tk.h"
  25.  
  26. /*
  27.  * A data structure of the following type is kept for each square
  28.  * widget managed by this file:
  29.  */
  30.  
  31. typedef struct {
  32.     Tk_Window tkwin;        /* Window that embodies the square.  NULL
  33.                  * means that the window has been destroyed
  34.                  * but the data structures haven't yet been
  35.                  * cleaned up.*/
  36.     Tcl_Interp *interp;        /* Interpreter associated with widget. */
  37.     int x, y;            /* Position of square's upper-left corner
  38.                  * within widget. */
  39.     int size;            /* Width and height of square. */
  40.     int flags;            /* Various flags;  see below for
  41.                  * definitions. */
  42.  
  43.     /*
  44.      * Information used when displaying widget:
  45.      */
  46.  
  47.     int borderWidth;        /* Width of 3-D border around whole widget. */
  48.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  49.     Tk_3DBorder fgBorder;    /* For drawing square. */
  50.     int relief;            /* Indicates whether window as a whole is
  51.                  * raised, sunken, or flat. */
  52.     int doubleBuffer;        /* Non-zero means double-buffer redisplay
  53.                  * with pixmap;  zero means draw straight
  54.                  * onto the display. */
  55. } Square;
  56.  
  57. /*
  58.  * Flag bits for squares:
  59.  *
  60.  * REDRAW_PENDING -        1 means redraw has already been scheduled.
  61.  */
  62.  
  63. #define REDRAW_PENDING        1
  64.  
  65. /*
  66.  * Information used for argv parsing.
  67.  */
  68.  
  69. static Tk_ConfigSpec configSpecs[] = {
  70.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  71.     "#cdb79e", Tk_Offset(Square, bgBorder), TK_CONFIG_COLOR_ONLY},
  72.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  73.     "white", Tk_Offset(Square, bgBorder), TK_CONFIG_MONO_ONLY},
  74.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  75.     (char *) NULL, 0, 0},
  76.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  77.     (char *) NULL, 0, 0},
  78.     {TK_CONFIG_INT, "-borderwidth", "borderWidth", "BorderWidth",
  79.     "2", Tk_Offset(Square, borderWidth), 0},
  80.     {TK_CONFIG_INT, "-dbl", "doubleBuffer", "DoubleBuffer",
  81.     "1", Tk_Offset(Square, doubleBuffer), 0},
  82.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  83.     (char *) NULL, 0, 0},
  84.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  85.     "#b03060", Tk_Offset(Square, fgBorder), TK_CONFIG_COLOR_ONLY},
  86.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  87.     "black", Tk_Offset(Square, fgBorder), TK_CONFIG_MONO_ONLY},
  88.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  89.     "raised", Tk_Offset(Square, relief), 0},
  90.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  91.     (char *) NULL, 0, 0}
  92. };
  93.  
  94. /*
  95.  * Forward declarations for procedures defined later in this file:
  96.  */
  97.  
  98. static int        ConfigureSquare _ANSI_ARGS_((Tcl_Interp *interp,
  99.                 Square *squarePtr, int argc, char **argv,
  100.                 int flags));
  101. static void        DestroySquare _ANSI_ARGS_((ClientData clientData));
  102. static void        DisplaySquare _ANSI_ARGS_((ClientData clientData));
  103. static void        KeepInWindow _ANSI_ARGS_((Square *squarePtr));
  104. static void        SquareEventProc _ANSI_ARGS_((ClientData clientData,
  105.                 XEvent *eventPtr));
  106. static int        SquareWidgetCmd _ANSI_ARGS_((ClientData clientData,
  107.                 Tcl_Interp *, int argc, char **argv));
  108.  
  109. /*
  110.  *--------------------------------------------------------------
  111.  *
  112.  * Tk_SquareCmd --
  113.  *
  114.  *    This procedure is invoked to process the "square" Tcl
  115.  *    command.  It creates a new "square" widget.
  116.  *
  117.  * Results:
  118.  *    A standard Tcl result.
  119.  *
  120.  * Side effects:
  121.  *    A new widget is created and configured.
  122.  *
  123.  *--------------------------------------------------------------
  124.  */
  125.  
  126. int
  127. Tk_SquareCmd(clientData, interp, argc, argv)
  128.     ClientData clientData;    /* Main window associated with
  129.                  * interpreter. */
  130.     Tcl_Interp *interp;        /* Current interpreter. */
  131.     int argc;            /* Number of arguments. */
  132.     char **argv;        /* Argument strings. */
  133. {
  134.     Tk_Window main = (Tk_Window) clientData;
  135.     register Square *squarePtr;
  136.     Tk_Window tkwin;
  137.  
  138.     if (argc < 2) {
  139.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  140.         argv[0], " pathName ?options?\"", (char *) NULL);
  141.     return TCL_ERROR;
  142.     }
  143.  
  144.     tkwin = Tk_CreateWindowFromPath(interp, main, argv[1], (char *) NULL);
  145.     if (tkwin == NULL) {
  146.     return TCL_ERROR;
  147.     }
  148.  
  149.     /*
  150.      * Initialize fields that won't be initialized by ConfigureSquare,
  151.      * or which ConfigureSquare expects to have reasonable values
  152.      * (e.g. resource pointers).
  153.      */
  154.  
  155.     squarePtr = (Square *) ckalloc(sizeof(Square));
  156.     squarePtr->tkwin = tkwin;
  157.     squarePtr->interp = interp;
  158.     squarePtr->x = 0;
  159.     squarePtr->y = 0;
  160.     squarePtr->size = 20;
  161.     squarePtr->bgBorder = NULL;
  162.     squarePtr->fgBorder = NULL;
  163.     squarePtr->flags = 0;
  164.  
  165.     Tk_SetClass(squarePtr->tkwin, "Square");
  166.     Tk_CreateEventHandler(squarePtr->tkwin, ExposureMask|StructureNotifyMask,
  167.         SquareEventProc, (ClientData) squarePtr);
  168.     Tcl_CreateCommand(interp, Tk_PathName(squarePtr->tkwin), SquareWidgetCmd,
  169.         (ClientData) squarePtr, (void (*)()) NULL);
  170.     if (ConfigureSquare(interp, squarePtr, argc-2, argv+2, 0) != TCL_OK) {
  171.     Tk_DestroyWindow(squarePtr->tkwin);
  172.     return TCL_ERROR;
  173.     }
  174.  
  175.     interp->result = Tk_PathName(squarePtr->tkwin);
  176.     return TCL_OK;
  177. }
  178.  
  179. /*
  180.  *----------------------------------------------------------------------
  181.  *
  182.  * ConfigureSquare --
  183.  *
  184.  *    This procedure is called to process an argv/argc list in
  185.  *    conjunction with the Tk option database to configure (or
  186.  *    reconfigure) a square widget.
  187.  *
  188.  * Results:
  189.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  190.  *    returned, then interp->result contains an error message.
  191.  *
  192.  * Side effects:
  193.  *    Configuration information, such as colors, border width,
  194.  *    etc. get set for squarePtr;  old resources get freed,
  195.  *    if there were any.
  196.  *
  197.  *----------------------------------------------------------------------
  198.  */
  199.  
  200. static int
  201. ConfigureSquare(interp, squarePtr, argc, argv, flags)
  202.     Tcl_Interp *interp;            /* Used for error reporting. */
  203.     register Square *squarePtr;        /* Information about widget. */
  204.     int argc;                /* Number of valid entries in argv. */
  205.     char **argv;            /* Arguments. */
  206.     int flags;                /* Flags to pass to
  207.                      * Tk_ConfigureWidget. */
  208. {
  209.     if (Tk_ConfigureWidget(interp, squarePtr->tkwin, configSpecs,
  210.         argc, argv, (char *) squarePtr, flags) != TCL_OK) {
  211.     return TCL_ERROR;
  212.     }
  213.  
  214.     /*
  215.      * A few options need special processing, such as setting the
  216.      * background from a 3-D border.
  217.      */
  218.  
  219.     Tk_SetBackgroundFromBorder(squarePtr->tkwin, squarePtr->bgBorder);
  220.  
  221.     /*
  222.      * Register the desired geometry for the window.  Then arrange for
  223.      * the window to be redisplayed.
  224.      */
  225.  
  226.     Tk_GeometryRequest(squarePtr->tkwin, 200, 150);
  227.     Tk_SetInternalBorder(squarePtr->tkwin, squarePtr->borderWidth);
  228.     if (!(squarePtr->flags & REDRAW_PENDING)) {
  229.     Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr);
  230.     squarePtr->flags |= REDRAW_PENDING;
  231.     }
  232.     return TCL_OK;
  233. }
  234.  
  235. /*
  236.  *--------------------------------------------------------------
  237.  *
  238.  * DisplaySquare --
  239.  *
  240.  *    This procedure redraws the contents of a square window.
  241.  *    It is invoked as a do-when-idle handler, so it only runs
  242.  *    when there's nothing else for the application to do.
  243.  *
  244.  * Results:
  245.  *    None.
  246.  *
  247.  * Side effects:
  248.  *    Information appears on the screen.
  249.  *
  250.  *--------------------------------------------------------------
  251.  */
  252.  
  253. static void
  254. DisplaySquare(clientData)
  255.     ClientData clientData;    /* Information about window. */
  256. {
  257.     register Square *squarePtr = (Square *) clientData;
  258.     register Tk_Window tkwin = squarePtr->tkwin;
  259.     Pixmap pm = None;
  260.     Drawable d;
  261.  
  262.     squarePtr->flags &= ~REDRAW_PENDING;
  263.     if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  264.     return;
  265.     }
  266.  
  267.     /*
  268.      * Create a pixmap for double-buffering, if necessary.
  269.      */
  270.  
  271.     if (squarePtr->doubleBuffer) {
  272.     pm = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  273.         Tk_Width(tkwin), Tk_Height(tkwin),
  274.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  275.     d = pm;
  276.     } else {
  277.     d = Tk_WindowId(tkwin);
  278.     }
  279.  
  280.     /*
  281.      * Redraw the widget's background and border.
  282.      */
  283.  
  284.     Tk_Fill3DRectangle(Tk_Display(tkwin), d, squarePtr->bgBorder,
  285.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  286.         squarePtr->borderWidth, squarePtr->relief);
  287.  
  288.     /*
  289.      * Display the square.
  290.      */
  291.  
  292.     Tk_Fill3DRectangle(Tk_Display(tkwin), d, squarePtr->fgBorder,
  293.         squarePtr->x, squarePtr->y, squarePtr->size, squarePtr->size,
  294.         squarePtr->borderWidth, TK_RELIEF_RAISED);
  295.  
  296.     /*
  297.      * If double-buffered, copy to the screen and release the pixmap.
  298.      */
  299.  
  300.     if (squarePtr->doubleBuffer) {
  301.     XCopyArea(Tk_Display(tkwin), pm, Tk_WindowId(tkwin),
  302.         DefaultGCOfScreen(Tk_Screen(tkwin)), 0, 0,
  303.         Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  304.     XFreePixmap(Tk_Display(tkwin), pm);
  305.     }
  306. }
  307.  
  308. /*
  309.  *--------------------------------------------------------------
  310.  *
  311.  * SquareWidgetCmd --
  312.  *
  313.  *    This procedure is invoked to process the Tcl command
  314.  *    that corresponds to a widget managed by this module.
  315.  *    See the user documentation for details on what it does.
  316.  *
  317.  * Results:
  318.  *    A standard Tcl result.
  319.  *
  320.  * Side effects:
  321.  *    See the user documentation.
  322.  *
  323.  *--------------------------------------------------------------
  324.  */
  325.  
  326. static int
  327. SquareWidgetCmd(clientData, interp, argc, argv)
  328.     ClientData clientData;        /* Information about square widget. */
  329.     Tcl_Interp *interp;            /* Current interpreter. */
  330.     int argc;                /* Number of arguments. */
  331.     char **argv;            /* Argument strings. */
  332. {
  333.     register Square *squarePtr = (Square *) clientData;
  334.     int result = TCL_OK;
  335.     int length;
  336.     char c;
  337.  
  338.     if (argc < 2) {
  339.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  340.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  341.     return TCL_ERROR;
  342.     }
  343.     Tk_Preserve((ClientData) squarePtr);
  344.     c = argv[1][0];
  345.     length = strlen(argv[1]);
  346.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  347.     if (argc == 2) {
  348.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  349.             (char *) squarePtr, (char *) NULL, 0);
  350.     } else if (argc == 3) {
  351.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  352.             (char *) squarePtr, argv[2], 0);
  353.     } else {
  354.         result = ConfigureSquare(interp, squarePtr, argc-2, argv+2,
  355.             TK_CONFIG_ARGV_ONLY);
  356.     }
  357.     } else if ((c == 'p') && (strncmp(argv[1], "position", length) == 0)) {
  358.     if ((argc != 2) && (argc != 4)) {
  359.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  360.             argv[0], " position ?x y?\"", (char *) NULL);
  361.         goto error;
  362.     }
  363.     if (argc == 4) {
  364.         if ((Tcl_GetInt(interp, argv[2], &squarePtr->x) != TCL_OK)
  365.             || (Tcl_GetInt(interp, argv[3], &squarePtr->y) != TCL_OK)) {
  366.         goto error;
  367.         }
  368.         KeepInWindow(squarePtr);
  369.     }
  370.     sprintf(interp->result, "%d %d", squarePtr->x, squarePtr->y);
  371.     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
  372.     if ((argc != 2) && (argc != 3)) {
  373.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  374.             argv[0], " size ?amount?\"", (char *) NULL);
  375.         goto error;
  376.     }
  377.     if (argc == 3) {
  378.         int i;
  379.  
  380.         if (Tcl_GetInt(interp, argv[2], &i) != TCL_OK) {
  381.         goto error;
  382.         }
  383.         if ((i <= 0) || (i > 100)) {
  384.         Tcl_AppendResult(interp, "bad size \"", argv[2],
  385.             "\"", (char *) NULL);
  386.         goto error;
  387.         }
  388.         squarePtr->size = i;
  389.         KeepInWindow(squarePtr);
  390.     }
  391.     sprintf(interp->result, "%d", squarePtr->size);
  392.     } else {
  393.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  394.         "\":  must be configure, position, or size", (char *) NULL);
  395.     goto error;
  396.     }
  397.     if (!(squarePtr->flags & REDRAW_PENDING)) {
  398.     Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr);
  399.     squarePtr->flags |= REDRAW_PENDING;
  400.     }
  401.     Tk_Release((ClientData) squarePtr);
  402.     return result;
  403.  
  404.     error:
  405.     Tk_Release((ClientData) squarePtr);
  406.     return TCL_ERROR;
  407. }
  408.  
  409. /*
  410.  *--------------------------------------------------------------
  411.  *
  412.  * SquareEventProc --
  413.  *
  414.  *    This procedure is invoked by the Tk dispatcher for various
  415.  *    events on squares.
  416.  *
  417.  * Results:
  418.  *    None.
  419.  *
  420.  * Side effects:
  421.  *    When the window gets deleted, internal structures get
  422.  *    cleaned up.  When it gets exposed, it is redisplayed.
  423.  *
  424.  *--------------------------------------------------------------
  425.  */
  426.  
  427. static void
  428. SquareEventProc(clientData, eventPtr)
  429.     ClientData clientData;    /* Information about window. */
  430.     XEvent *eventPtr;        /* Information about event. */
  431. {
  432.     Square *squarePtr = (Square *) clientData;
  433.  
  434.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  435.     if (!(squarePtr->flags & REDRAW_PENDING)) {
  436.         Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr);
  437.         squarePtr->flags |= REDRAW_PENDING;
  438.     }
  439.     } else if (eventPtr->type == ConfigureNotify) {
  440.     KeepInWindow(squarePtr);
  441.     if (!(squarePtr->flags & REDRAW_PENDING)) {
  442.         Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr);
  443.         squarePtr->flags |= REDRAW_PENDING;
  444.     }
  445.     } else if (eventPtr->type == DestroyNotify) {
  446.     Tcl_DeleteCommand(squarePtr->interp, Tk_PathName(squarePtr->tkwin));
  447.     squarePtr->tkwin = NULL;
  448.     if (squarePtr->flags & REDRAW_PENDING) {
  449.         Tk_CancelIdleCall(DisplaySquare, (ClientData) squarePtr);
  450.     }
  451.     Tk_EventuallyFree((ClientData) squarePtr, DestroySquare);
  452.     }
  453. }
  454.  
  455. /*
  456.  *----------------------------------------------------------------------
  457.  *
  458.  * DestroySquare --
  459.  *
  460.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  461.  *    to clean up the internal structure of a square at a safe time
  462.  *    (when no-one is using it anymore).
  463.  *
  464.  * Results:
  465.  *    None.
  466.  *
  467.  * Side effects:
  468.  *    Everything associated with the square is freed up.
  469.  *
  470.  *----------------------------------------------------------------------
  471.  */
  472.  
  473. static void
  474. DestroySquare(clientData)
  475.     ClientData clientData;    /* Info about square widget. */
  476. {
  477.     register Square *squarePtr = (Square *) clientData;
  478.  
  479.     if (squarePtr->bgBorder != NULL) {
  480.     Tk_Free3DBorder(squarePtr->bgBorder);
  481.     }
  482.     if (squarePtr->fgBorder != NULL) {
  483.     Tk_Free3DBorder(squarePtr->fgBorder);
  484.     }
  485.     ckfree((char *) squarePtr);
  486. }
  487.  
  488. /*
  489.  *----------------------------------------------------------------------
  490.  *
  491.  * KeepInWindow --
  492.  *
  493.  *    Adjust the position of the square if necessary to keep it in
  494.  *    the widget's window.
  495.  *
  496.  * Results:
  497.  *    None.
  498.  *
  499.  * Side effects:
  500.  *    The x and y position of the square are adjusted if necessary
  501.  *    to keep the square in the window.
  502.  *
  503.  *----------------------------------------------------------------------
  504.  */
  505.  
  506. static void
  507. KeepInWindow(squarePtr)
  508.     register Square *squarePtr;        /* Pointer to widget record. */
  509. {
  510.     int i, bd;
  511.  
  512.     if (squarePtr->relief == TK_RELIEF_FLAT) {
  513.     bd = 0;
  514.     } else {
  515.     bd = squarePtr->borderWidth;
  516.     }
  517.     i = (Tk_Width(squarePtr->tkwin) - bd) - (squarePtr->x + squarePtr->size);
  518.     if (i < 0) {
  519.     squarePtr->x += i;
  520.     }
  521.     i = (Tk_Height(squarePtr->tkwin) - bd) - (squarePtr->y + squarePtr->size);
  522.     if (i < 0) {
  523.     squarePtr->y += i;
  524.     }
  525.     if (squarePtr->x < bd) {
  526.     squarePtr->x = bd;
  527.     }
  528.     if (squarePtr->y < bd) {
  529.     squarePtr->y = bd;
  530.     }
  531. }
  532.