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