home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkSquare.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  16.9 KB  |  588 lines  |  [TEXT/CWIE]

  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;  it isn't included in the
  8.  *    normal wish, but it is included in "tktest".
  9.  *
  10.  * Copyright (c) 1991-1994 The Regents of the University of California.
  11.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  12.  *
  13.  * See the file "license.terms" for information on usage and redistribution
  14.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  15.  *
  16.  * SCCS: @(#) tkSquare.c 1.19 97/07/31 09:13:13
  17.  */
  18.  
  19. #include "tkPort.h"
  20. #include "tk.h"
  21.  
  22. /*
  23.  * A data structure of the following type is kept for each square
  24.  * widget managed by this file:
  25.  */
  26.  
  27. typedef struct {
  28.     Tk_Window tkwin;        /* Window that embodies the square.  NULL
  29.                  * means window has been deleted but
  30.                  * widget record hasn't been cleaned up yet. */
  31.     Display *display;        /* X's token for the window's display. */
  32.     Tcl_Interp *interp;        /* Interpreter associated with widget. */
  33.     Tcl_Command widgetCmd;    /* Token for square's widget command. */
  34.     int x, y;            /* Position of square's upper-left corner
  35.                  * within widget. */
  36.     int size;            /* Width and height of square. */
  37.  
  38.     /*
  39.      * Information used when displaying widget:
  40.      */
  41.  
  42.     int borderWidth;        /* Width of 3-D border around whole widget. */
  43.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  44.     Tk_3DBorder fgBorder;    /* For drawing square. */
  45.     int relief;            /* Indicates whether window as a whole is
  46.                  * raised, sunken, or flat. */
  47.     GC gc;            /* Graphics context for copying from
  48.                  * off-screen pixmap onto screen. */
  49.     int doubleBuffer;        /* Non-zero means double-buffer redisplay
  50.                  * with pixmap;  zero means draw straight
  51.                  * onto the display. */
  52.     int updatePending;        /* Non-zero means a call to SquareDisplay
  53.                  * has already been scheduled. */
  54. } Square;
  55.  
  56. /*
  57.  * Information used for argv parsing.
  58.  */
  59.  
  60. static Tk_ConfigSpec configSpecs[] = {
  61.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  62.     "#d9d9d9", Tk_Offset(Square, bgBorder), TK_CONFIG_COLOR_ONLY},
  63.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  64.     "white", Tk_Offset(Square, bgBorder), TK_CONFIG_MONO_ONLY},
  65.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  66.     (char *) NULL, 0, 0},
  67.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  68.     (char *) NULL, 0, 0},
  69.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  70.     "2", Tk_Offset(Square, borderWidth), 0},
  71.     {TK_CONFIG_INT, "-dbl", "doubleBuffer", "DoubleBuffer",
  72.     "1", Tk_Offset(Square, doubleBuffer), 0},
  73.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  74.     (char *) NULL, 0, 0},
  75.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  76.     "#b03060", Tk_Offset(Square, fgBorder), TK_CONFIG_COLOR_ONLY},
  77.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  78.     "black", Tk_Offset(Square, fgBorder), TK_CONFIG_MONO_ONLY},
  79.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  80.     "raised", Tk_Offset(Square, relief), 0},
  81.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  82.     (char *) NULL, 0, 0}
  83. };
  84.  
  85. /*
  86.  * Forward declarations for procedures defined later in this file:
  87.  */
  88.  
  89. int            SquareCmd _ANSI_ARGS_((ClientData clientData,
  90.                 Tcl_Interp *interp, int argc, char **argv));
  91. static void        SquareCmdDeletedProc _ANSI_ARGS_((
  92.                 ClientData clientData));
  93. static int        SquareConfigure _ANSI_ARGS_((Tcl_Interp *interp,
  94.                 Square *squarePtr, int argc, char **argv,
  95.                 int flags));
  96. static void        SquareDestroy _ANSI_ARGS_((char *memPtr));
  97. static void        SquareDisplay _ANSI_ARGS_((ClientData clientData));
  98. static void        KeepInWindow _ANSI_ARGS_((Square *squarePtr));
  99. static void        SquareEventProc _ANSI_ARGS_((ClientData clientData,
  100.                 XEvent *eventPtr));
  101. static int        SquareWidgetCmd _ANSI_ARGS_((ClientData clientData,
  102.                 Tcl_Interp *, int argc, char **argv));
  103.  
  104. /*
  105.  *--------------------------------------------------------------
  106.  *
  107.  * SquareCmd --
  108.  *
  109.  *    This procedure is invoked to process the "square" Tcl
  110.  *    command.  It creates a new "square" widget.
  111.  *
  112.  * Results:
  113.  *    A standard Tcl result.
  114.  *
  115.  * Side effects:
  116.  *    A new widget is created and configured.
  117.  *
  118.  *--------------------------------------------------------------
  119.  */
  120.  
  121. int
  122. SquareCmd(clientData, interp, argc, argv)
  123.     ClientData clientData;    /* Main window associated with
  124.                  * interpreter. */
  125.     Tcl_Interp *interp;        /* Current interpreter. */
  126.     int argc;            /* Number of arguments. */
  127.     char **argv;        /* Argument strings. */
  128. {
  129.     Tk_Window main = (Tk_Window) clientData;
  130.     Square *squarePtr;
  131.     Tk_Window tkwin;
  132.  
  133.     if (argc < 2) {
  134.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  135.         argv[0], " pathName ?options?\"", (char *) NULL);
  136.     return TCL_ERROR;
  137.     }
  138.  
  139.     tkwin = Tk_CreateWindowFromPath(interp, main, argv[1], (char *) NULL);
  140.     if (tkwin == NULL) {
  141.     return TCL_ERROR;
  142.     }
  143.     Tk_SetClass(tkwin, "Square");
  144.  
  145.     /*
  146.      * Allocate and initialize the widget record.
  147.      */
  148.  
  149.     squarePtr = (Square *) ckalloc(sizeof(Square));
  150.     squarePtr->tkwin = tkwin;
  151.     squarePtr->display = Tk_Display(tkwin);
  152.     squarePtr->interp = interp;
  153.     squarePtr->widgetCmd = Tcl_CreateCommand(interp,
  154.         Tk_PathName(squarePtr->tkwin), SquareWidgetCmd,
  155.         (ClientData) squarePtr, SquareCmdDeletedProc);
  156.     squarePtr->x = 0;
  157.     squarePtr->y = 0;
  158.     squarePtr->size = 20;
  159.     squarePtr->borderWidth = 0;
  160.     squarePtr->bgBorder = NULL;
  161.     squarePtr->fgBorder = NULL;
  162.     squarePtr->relief = TK_RELIEF_FLAT;
  163.     squarePtr->gc = None;
  164.     squarePtr->doubleBuffer = 1;
  165.     squarePtr->updatePending = 0;
  166.  
  167.     Tk_CreateEventHandler(squarePtr->tkwin, ExposureMask|StructureNotifyMask,
  168.         SquareEventProc, (ClientData) squarePtr);
  169.     if (SquareConfigure(interp, squarePtr, argc-2, argv+2, 0) != TCL_OK) {
  170.     Tk_DestroyWindow(squarePtr->tkwin);
  171.     return TCL_ERROR;
  172.     }
  173.  
  174.     interp->result = Tk_PathName(squarePtr->tkwin);
  175.     return TCL_OK;
  176. }
  177.  
  178. /*
  179.  *--------------------------------------------------------------
  180.  *
  181.  * SquareWidgetCmd --
  182.  *
  183.  *    This procedure is invoked to process the Tcl command
  184.  *    that corresponds to a widget managed by this module.
  185.  *    See the user documentation for details on what it does.
  186.  *
  187.  * Results:
  188.  *    A standard Tcl result.
  189.  *
  190.  * Side effects:
  191.  *    See the user documentation.
  192.  *
  193.  *--------------------------------------------------------------
  194.  */
  195.  
  196. static int
  197. SquareWidgetCmd(clientData, interp, argc, argv)
  198.     ClientData clientData;        /* Information about square widget. */
  199.     Tcl_Interp *interp;            /* Current interpreter. */
  200.     int argc;                /* Number of arguments. */
  201.     char **argv;            /* Argument strings. */
  202. {
  203.     Square *squarePtr = (Square *) clientData;
  204.     int result = TCL_OK;
  205.     size_t length;
  206.     char c;
  207.  
  208.     if (argc < 2) {
  209.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  210.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  211.     return TCL_ERROR;
  212.     }
  213.     Tcl_Preserve((ClientData) squarePtr);
  214.     c = argv[1][0];
  215.     length = strlen(argv[1]);
  216.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  217.         && (length >= 2)) {
  218.     if (argc != 3) {
  219.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  220.             argv[0], " cget option\"",
  221.             (char *) NULL);
  222.         goto error;
  223.     }
  224.     result = Tk_ConfigureValue(interp, squarePtr->tkwin, configSpecs,
  225.         (char *) squarePtr, argv[2], 0);
  226.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  227.         && (length >= 2)) {
  228.     if (argc == 2) {
  229.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  230.             (char *) squarePtr, (char *) NULL, 0);
  231.     } else if (argc == 3) {
  232.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  233.             (char *) squarePtr, argv[2], 0);
  234.     } else {
  235.         result = SquareConfigure(interp, squarePtr, argc-2, argv+2,
  236.             TK_CONFIG_ARGV_ONLY);
  237.     }
  238.     } else if ((c == 'p') && (strncmp(argv[1], "position", length) == 0)) {
  239.     if ((argc != 2) && (argc != 4)) {
  240.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  241.             argv[0], " position ?x y?\"", (char *) NULL);
  242.         goto error;
  243.     }
  244.     if (argc == 4) {
  245.         if ((Tk_GetPixels(interp, squarePtr->tkwin, argv[2],
  246.             &squarePtr->x) != TCL_OK) || (Tk_GetPixels(interp,
  247.             squarePtr->tkwin, argv[3], &squarePtr->y) != TCL_OK)) {
  248.         goto error;
  249.         }
  250.         KeepInWindow(squarePtr);
  251.     }
  252.     sprintf(interp->result, "%d %d", squarePtr->x, squarePtr->y);
  253.     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
  254.     if ((argc != 2) && (argc != 3)) {
  255.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  256.             argv[0], " size ?amount?\"", (char *) NULL);
  257.         goto error;
  258.     }
  259.     if (argc == 3) {
  260.         int i;
  261.  
  262.         if (Tk_GetPixels(interp, squarePtr->tkwin, argv[2], &i) != TCL_OK) {
  263.         goto error;
  264.         }
  265.         if ((i <= 0) || (i > 100)) {
  266.         Tcl_AppendResult(interp, "bad size \"", argv[2],
  267.             "\"", (char *) NULL);
  268.         goto error;
  269.         }
  270.         squarePtr->size = i;
  271.         KeepInWindow(squarePtr);
  272.     }
  273.     sprintf(interp->result, "%d", squarePtr->size);
  274.     } else {
  275.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  276.         "\": must be cget, configure, position, or size",
  277.         (char *) NULL);
  278.     goto error;
  279.     }
  280.     if (!squarePtr->updatePending) {
  281.     Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  282.     squarePtr->updatePending = 1;
  283.     }
  284.     Tcl_Release((ClientData) squarePtr);
  285.     return result;
  286.  
  287.     error:
  288.     Tcl_Release((ClientData) squarePtr);
  289.     return TCL_ERROR;
  290. }
  291.  
  292. /*
  293.  *----------------------------------------------------------------------
  294.  *
  295.  * SquareConfigure --
  296.  *
  297.  *    This procedure is called to process an argv/argc list in
  298.  *    conjunction with the Tk option database to configure (or
  299.  *    reconfigure) a square widget.
  300.  *
  301.  * Results:
  302.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  303.  *    returned, then interp->result contains an error message.
  304.  *
  305.  * Side effects:
  306.  *    Configuration information, such as colors, border width,
  307.  *    etc. get set for squarePtr;  old resources get freed,
  308.  *    if there were any.
  309.  *
  310.  *----------------------------------------------------------------------
  311.  */
  312.  
  313. static int
  314. SquareConfigure(interp, squarePtr, argc, argv, flags)
  315.     Tcl_Interp *interp;            /* Used for error reporting. */
  316.     Square *squarePtr;            /* Information about widget. */
  317.     int argc;                /* Number of valid entries in argv. */
  318.     char **argv;            /* Arguments. */
  319.     int flags;                /* Flags to pass to
  320.                      * Tk_ConfigureWidget. */
  321. {
  322.     if (Tk_ConfigureWidget(interp, squarePtr->tkwin, configSpecs,
  323.         argc, argv, (char *) squarePtr, flags) != TCL_OK) {
  324.     return TCL_ERROR;
  325.     }
  326.  
  327.     /*
  328.      * Set the background for the window and create a graphics context
  329.      * for use during redisplay.
  330.      */
  331.  
  332.     Tk_SetWindowBackground(squarePtr->tkwin,
  333.         Tk_3DBorderColor(squarePtr->bgBorder)->pixel);
  334.     if ((squarePtr->gc == None) && (squarePtr->doubleBuffer)) {
  335.     XGCValues gcValues;
  336.     gcValues.function = GXcopy;
  337.     gcValues.graphics_exposures = False;
  338.     squarePtr->gc = Tk_GetGC(squarePtr->tkwin,
  339.         GCFunction|GCGraphicsExposures, &gcValues);
  340.     }
  341.  
  342.     /*
  343.      * Register the desired geometry for the window.  Then arrange for
  344.      * the window to be redisplayed.
  345.      */
  346.  
  347.     Tk_GeometryRequest(squarePtr->tkwin, 200, 150);
  348.     Tk_SetInternalBorder(squarePtr->tkwin, squarePtr->borderWidth);
  349.     if (!squarePtr->updatePending) {
  350.     Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  351.     squarePtr->updatePending = 1;
  352.     }
  353.     return TCL_OK;
  354. }
  355.  
  356. /*
  357.  *--------------------------------------------------------------
  358.  *
  359.  * SquareEventProc --
  360.  *
  361.  *    This procedure is invoked by the Tk dispatcher for various
  362.  *    events on squares.
  363.  *
  364.  * Results:
  365.  *    None.
  366.  *
  367.  * Side effects:
  368.  *    When the window gets deleted, internal structures get
  369.  *    cleaned up.  When it gets exposed, it is redisplayed.
  370.  *
  371.  *--------------------------------------------------------------
  372.  */
  373.  
  374. static void
  375. SquareEventProc(clientData, eventPtr)
  376.     ClientData clientData;    /* Information about window. */
  377.     XEvent *eventPtr;        /* Information about event. */
  378. {
  379.     Square *squarePtr = (Square *) clientData;
  380.  
  381.     if (eventPtr->type == Expose) {
  382.     if (!squarePtr->updatePending) {
  383.         Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  384.         squarePtr->updatePending = 1;
  385.     }
  386.     } else if (eventPtr->type == ConfigureNotify) {
  387.     KeepInWindow(squarePtr);
  388.     if (!squarePtr->updatePending) {
  389.         Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  390.         squarePtr->updatePending = 1;
  391.     }
  392.     } else if (eventPtr->type == DestroyNotify) {
  393.     if (squarePtr->tkwin != NULL) {
  394.         squarePtr->tkwin = NULL;
  395.         Tcl_DeleteCommandFromToken(squarePtr->interp,
  396.             squarePtr->widgetCmd);
  397.     }
  398.     if (squarePtr->updatePending) {
  399.         Tcl_CancelIdleCall(SquareDisplay, (ClientData) squarePtr);
  400.     }
  401.     Tcl_EventuallyFree((ClientData) squarePtr, SquareDestroy);
  402.     }
  403. }
  404.  
  405. /*
  406.  *----------------------------------------------------------------------
  407.  *
  408.  * SquareCmdDeletedProc --
  409.  *
  410.  *    This procedure is invoked when a widget command is deleted.  If
  411.  *    the widget isn't already in the process of being destroyed,
  412.  *    this command destroys it.
  413.  *
  414.  * Results:
  415.  *    None.
  416.  *
  417.  * Side effects:
  418.  *    The widget is destroyed.
  419.  *
  420.  *----------------------------------------------------------------------
  421.  */
  422.  
  423. static void
  424. SquareCmdDeletedProc(clientData)
  425.     ClientData clientData;    /* Pointer to widget record for widget. */
  426. {
  427.     Square *squarePtr = (Square *) clientData;
  428.     Tk_Window tkwin = squarePtr->tkwin;
  429.  
  430.     /*
  431.      * This procedure could be invoked either because the window was
  432.      * destroyed and the command was then deleted (in which case tkwin
  433.      * is NULL) or because the command was deleted, and then this procedure
  434.      * destroys the widget.
  435.      */
  436.  
  437.     if (tkwin != NULL) {
  438.     squarePtr->tkwin = NULL;
  439.     Tk_DestroyWindow(tkwin);
  440.     }
  441. }
  442.  
  443. /*
  444.  *--------------------------------------------------------------
  445.  *
  446.  * SquareDisplay --
  447.  *
  448.  *    This procedure redraws the contents of a square window.
  449.  *    It is invoked as a do-when-idle handler, so it only runs
  450.  *    when there's nothing else for the application to do.
  451.  *
  452.  * Results:
  453.  *    None.
  454.  *
  455.  * Side effects:
  456.  *    Information appears on the screen.
  457.  *
  458.  *--------------------------------------------------------------
  459.  */
  460.  
  461. static void
  462. SquareDisplay(clientData)
  463.     ClientData clientData;    /* Information about window. */
  464. {
  465.     Square *squarePtr = (Square *) clientData;
  466.     Tk_Window tkwin = squarePtr->tkwin;
  467.     Pixmap pm = None;
  468.     Drawable d;
  469.  
  470.     squarePtr->updatePending = 0;
  471.     if (!Tk_IsMapped(tkwin)) {
  472.     return;
  473.     }
  474.  
  475.     /*
  476.      * Create a pixmap for double-buffering, if necessary.
  477.      */
  478.  
  479.     if (squarePtr->doubleBuffer) {
  480.     pm = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  481.         Tk_Width(tkwin), Tk_Height(tkwin),
  482.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  483.     d = pm;
  484.     } else {
  485.     d = Tk_WindowId(tkwin);
  486.     }
  487.  
  488.     /*
  489.      * Redraw the widget's background and border.
  490.      */
  491.  
  492.     Tk_Fill3DRectangle(tkwin, d, squarePtr->bgBorder, 0, 0, Tk_Width(tkwin),
  493.         Tk_Height(tkwin), squarePtr->borderWidth, squarePtr->relief);
  494.  
  495.     /*
  496.      * Display the square.
  497.      */
  498.  
  499.     Tk_Fill3DRectangle(tkwin, d, squarePtr->fgBorder, squarePtr->x,
  500.         squarePtr->y, squarePtr->size, squarePtr->size,
  501.         squarePtr->borderWidth, TK_RELIEF_RAISED);
  502.  
  503.     /*
  504.      * If double-buffered, copy to the screen and release the pixmap.
  505.      */
  506.  
  507.     if (squarePtr->doubleBuffer) {
  508.     XCopyArea(Tk_Display(tkwin), pm, Tk_WindowId(tkwin), squarePtr->gc,
  509.         0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
  510.         0, 0);
  511.     Tk_FreePixmap(Tk_Display(tkwin), pm);
  512.     }
  513. }
  514.  
  515. /*
  516.  *----------------------------------------------------------------------
  517.  *
  518.  * SquareDestroy --
  519.  *
  520.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  521.  *    to clean up the internal structure of a square at a safe time
  522.  *    (when no-one is using it anymore).
  523.  *
  524.  * Results:
  525.  *    None.
  526.  *
  527.  * Side effects:
  528.  *    Everything associated with the square is freed up.
  529.  *
  530.  *----------------------------------------------------------------------
  531.  */
  532.  
  533. static void
  534. SquareDestroy(memPtr)
  535.     char *memPtr;        /* Info about square widget. */
  536. {
  537.     Square *squarePtr = (Square *) memPtr;
  538.  
  539.     Tk_FreeOptions(configSpecs, (char *) squarePtr, squarePtr->display, 0);
  540.     if (squarePtr->gc != None) {
  541.     Tk_FreeGC(squarePtr->display, squarePtr->gc);
  542.     }
  543.     ckfree((char *) squarePtr);
  544. }
  545.  
  546. /*
  547.  *----------------------------------------------------------------------
  548.  *
  549.  * KeepInWindow --
  550.  *
  551.  *    Adjust the position of the square if necessary to keep it in
  552.  *    the widget's window.
  553.  *
  554.  * Results:
  555.  *    None.
  556.  *
  557.  * Side effects:
  558.  *    The x and y position of the square are adjusted if necessary
  559.  *    to keep the square in the window.
  560.  *
  561.  *----------------------------------------------------------------------
  562.  */
  563.  
  564. static void
  565. KeepInWindow(squarePtr)
  566.     register Square *squarePtr;        /* Pointer to widget record. */
  567. {
  568.     int i, bd;
  569.     bd = 0;
  570.     if (squarePtr->relief != TK_RELIEF_FLAT) {
  571.     bd = squarePtr->borderWidth;
  572.     }
  573.     i = (Tk_Width(squarePtr->tkwin) - bd) - (squarePtr->x + squarePtr->size);
  574.     if (i < 0) {
  575.     squarePtr->x += i;
  576.     }
  577.     i = (Tk_Height(squarePtr->tkwin) - bd) - (squarePtr->y + squarePtr->size);
  578.     if (i < 0) {
  579.     squarePtr->y += i;
  580.     }
  581.     if (squarePtr->x < bd) {
  582.     squarePtr->x = bd;
  583.     }
  584.     if (squarePtr->y < bd) {
  585.     squarePtr->y = bd;
  586.     }
  587. }
  588.