home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkFocus.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  14.4 KB  |  502 lines

  1. /* 
  2.  * tkFocus.c --
  3.  *
  4.  *    This file contains procedures that manage the input
  5.  *    focus for Tk.
  6.  *
  7.  * Copyright (c) 1990-1993 The Regents of the University of California.
  8.  * All rights reserved.
  9.  *
  10.  * Permission is hereby granted, without written agreement and without
  11.  * license or royalty fees, to use, copy, modify, and distribute this
  12.  * software and its documentation for any purpose, provided that the
  13.  * above copyright notice and the following two paragraphs appear in
  14.  * all copies of this software.
  15.  * 
  16.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  17.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  18.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  19.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  20.  *
  21.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  22.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  23.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  24.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  25.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  26.  */
  27.  
  28. #ifndef lint
  29. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkFocus.c,v 1.7 93/06/16 17:16:47 ouster Exp $ SPRITE (Berkeley)";
  30. #endif
  31.  
  32. #include "tkInt.h"
  33. #include "tkConfig.h"
  34.  
  35. /*
  36.  * Magic value stored in "send_event" field to mark a FocusIn or FocusOut
  37.  * event generated by this file.  It indicates to TkFilterFocusEvent that
  38.  * it shouldn't process this event.
  39.  */
  40.  
  41. #define GENERATED_EVENT 0x123abcde
  42.  
  43. /*
  44.  * Forward declarations for procedures defined in this file:
  45.  */
  46.  
  47. static void        ChangeFocusTopLevelPtr _ANSI_ARGS_((TkDisplay *dispPtr,
  48.                 TkWindow *winPtr, int mode));
  49. static void        QueueFocusEvent _ANSI_ARGS_((TkWindow *winPtr,
  50.                 int type, int mode, int detail));
  51. static void        SetFocus _ANSI_ARGS_((TkWindow *winPtr,
  52.                 TkWindow *focusPtr));
  53.  
  54. /*
  55.  *--------------------------------------------------------------
  56.  *
  57.  * Tk_CreateFocusHandler --
  58.  *
  59.  *    Arrange for a procedure to be called whenever the focus
  60.  *    enters or leaves a given window.
  61.  *
  62.  * Results:
  63.  *    None.
  64.  *
  65.  * Side effects:
  66.  *    After this procedure has been invoked, whenever tkwin gets
  67.  *    or loses the input focus, proc will be called.  It should have
  68.  *    the following structure:
  69.  *
  70.  *    void
  71.  *    proc(clientData, gotFocus)
  72.  *        ClientData clientData;
  73.  *        int gotFocus;
  74.  *    {
  75.  *    }
  76.  *
  77.  *    The clientData argument to "proc" will be the same as the
  78.  *    clientData argument to this procedure.  GotFocus will be
  79.  *    1 if tkwin is getting the focus, and 0 if it's losing the
  80.  *    focus.
  81.  *
  82.  *--------------------------------------------------------------
  83.  */
  84.  
  85. void
  86. Tk_CreateFocusHandler(tkwin, proc, clientData)
  87.     Tk_Window tkwin;        /* Token for window. */
  88.     Tk_FocusProc *proc;        /* Procedure to call when tkwin gets
  89.                  * or loses the input focus. */
  90.     ClientData clientData;    /* Arbitrary value to pass to proc. */
  91. {
  92.     register TkWindow *winPtr = (TkWindow *) tkwin;
  93.  
  94.     winPtr->focusProc = proc;
  95.     winPtr->focusData = clientData;
  96. }
  97.  
  98. /*
  99.  *--------------------------------------------------------------
  100.  *
  101.  * Tk_FocusCmd --
  102.  *
  103.  *    This procedure is invoked to process the "focus" Tcl command.
  104.  *    See the user documentation for details on what it does.
  105.  *
  106.  * Results:
  107.  *    A standard Tcl result.
  108.  *
  109.  * Side effects:
  110.  *    See the user documentation.
  111.  *
  112.  *--------------------------------------------------------------
  113.  */
  114.  
  115. int
  116. Tk_FocusCmd(clientData, interp, argc, argv)
  117.     ClientData clientData;    /* Main window associated with
  118.                  * interpreter. */
  119.     Tcl_Interp *interp;        /* Current interpreter. */
  120.     int argc;            /* Number of arguments. */
  121.     char **argv;        /* Argument strings. */
  122. {
  123.     Tk_Window tkwin = (Tk_Window) clientData;
  124.     register TkWindow *winPtr = (TkWindow *) clientData;
  125.     register TkWindow *newPtr;
  126.     char c;
  127.     int length;
  128.  
  129.     /*
  130.      * If invoked with no arguments, just return the current focus window.
  131.      */
  132.  
  133.     if (argc == 1) {
  134.     if (winPtr->mainPtr->focusPtr == NULL) {
  135.         interp->result = "none";
  136.     } else {
  137.         interp->result = winPtr->mainPtr->focusPtr->pathName;
  138.     }
  139.     return TCL_OK;
  140.     }
  141.  
  142.     /*
  143.      * If invoked with a single argument beginning with "." then focus
  144.      * on that window.
  145.      */
  146.  
  147.     if ((argc == 2) && (argv[1][0] == '.')) {
  148.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
  149.     if (newPtr == NULL) {
  150.         return TCL_ERROR;
  151.     }
  152.     SetFocus(winPtr, newPtr);
  153.     return TCL_OK;
  154.     }
  155.  
  156.     length = strlen(argv[1]);
  157.     c = argv[1][0];
  158.     if ((c == 'd') && (strncmp(argv[1], "default", length) == 0)) {
  159.     if (argc > 3) {
  160.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  161.             argv[0], " default ?window?\"", (char *) NULL);
  162.         return TCL_ERROR;
  163.     }
  164.     if (argc == 2) {
  165.         if (winPtr->mainPtr->focusDefaultPtr == NULL) {
  166.         interp->result = "none";
  167.         } else {
  168.         interp->result = winPtr->mainPtr->focusDefaultPtr->pathName;
  169.         }
  170.         return TCL_OK;
  171.     }
  172.     if ((argv[2][0] == 'n')
  173.         && (strncmp(argv[2], "none", strlen(argv[2])) == 0)) {
  174.         newPtr = NULL;
  175.     } else {
  176.         newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  177.         if (newPtr == NULL) {
  178.         return TCL_ERROR;
  179.         }
  180.     }
  181.     winPtr->mainPtr->focusDefaultPtr = newPtr;
  182.     } else if ((c == 'n') && (strncmp(argv[1], "none", length) == 0)) {
  183.     if (argc != 2) {
  184.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  185.             argv[0], " none\"", (char *) NULL);
  186.         return TCL_ERROR;
  187.     }
  188.     SetFocus(winPtr, (TkWindow *) NULL);
  189.     } else {
  190.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  191.         "\": must be default or none", (char *) NULL);
  192.     return TCL_ERROR;
  193.     }
  194.     return TCL_OK;
  195. }
  196.  
  197. /*
  198.  *--------------------------------------------------------------
  199.  *
  200.  * TkFocusFilterEvent --
  201.  *
  202.  *    This procedure is invoked by Tk_HandleEvent when it encounters
  203.  *    a FocusIn, FocusOut, Enter, or Leave event.
  204.  *
  205.  * Results:
  206.  *    A return value of 1 means that Tk_HandleEvent should process
  207.  *    the event normally (i.e. event handlers should be invoked).
  208.  *    A return value of 0 means that this event should be ignored.
  209.  *
  210.  * Side effects:
  211.  *    An additional event may be generated and processed.
  212.  *
  213.  *--------------------------------------------------------------
  214.  */
  215.  
  216. int
  217. TkFocusFilterEvent(winPtr, eventPtr)
  218.     register TkWindow *winPtr;    /* Window that focus event is directed to. */
  219.     XEvent *eventPtr;        /* FocusIn or FocusOut event. */
  220. {
  221.     if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
  222.     /*
  223.      * If this event was generated by us then just process it
  224.      * normally.
  225.      */
  226.  
  227.     if (eventPtr->xfocus.send_event == GENERATED_EVENT) {
  228.         eventPtr->xfocus.send_event = 1;
  229.         return 1;
  230.     }
  231.  
  232.     /*
  233.      * Ignore the focus event if any of the following things is
  234.      * true:
  235.      *
  236.      * 1. The event isn't for a top-level window.
  237.      * 2. The event has detail NotifyInferior (which means the
  238.      *    focus moved around within the top-level window;  it
  239.      *    didn't move between the top-level window and the
  240.      *    outside world.
  241.      * 3. The event has detail NotifyPointer (I don't really understand
  242.      *    what these events are for, but they don't seem to serve
  243.      *    any useful purpose).
  244.      */
  245.  
  246.     if (!(winPtr->flags & TK_TOP_LEVEL)
  247.         || (eventPtr->xfocus.detail == NotifyInferior)
  248.         || (eventPtr->xfocus.detail == NotifyPointer)) {
  249.         return 0;
  250.     }
  251.  
  252.     /*
  253.      * This is a useful event.  Notify both the top-level window
  254.      * and the window that has been assigned the focus by the Tk
  255.      * "focus" command.
  256.      */
  257.  
  258.     if (eventPtr->type == FocusOut) {
  259.         ChangeFocusTopLevelPtr(winPtr->dispPtr, (TkWindow *) NULL,
  260.             eventPtr->xfocus.mode);
  261.     } else {
  262.         ChangeFocusTopLevelPtr(winPtr->dispPtr, winPtr,
  263.             eventPtr->xfocus.mode);
  264.     }
  265.     
  266.     /*
  267.      * This particular event should now be ignored, since we just
  268.      * generated events to notify all of the relevant windows.
  269.      */
  270.     
  271.     return 0;
  272.     } else {
  273.     /*
  274.      * This is an Enter or Leave event.  If there's no window manager,
  275.      * or if the window manager is not moving the focus around (e.g.
  276.      * if the disgusting "NoTitleFocus" option has been selected in
  277.      * twm), then we won't get FocusIn or FocusOut events.  Instead,
  278.      * watch enter and leave events.  If an Enter event arrives for a
  279.      * top-level window with its focus field set, but we don't have a
  280.      * record of a FocusIn event, then simulate one.  If a Leave event
  281.      * arrives and focus was set for the window via an Enter event,
  282.      * then simulate a FocusOut event.
  283.      */
  284.  
  285.     if ((eventPtr->type == EnterNotify) && (winPtr->flags & TK_TOP_LEVEL)
  286.         && eventPtr->xcrossing.focus
  287.         && (eventPtr->xcrossing.detail != NotifyInferior)
  288.         && (winPtr->dispPtr->focusTopLevelPtr != winPtr)) {
  289.         ChangeFocusTopLevelPtr(winPtr->dispPtr, winPtr,
  290.             eventPtr->xcrossing.mode);
  291.         winPtr->dispPtr->focussedOnEnter = 1;
  292.     } else if ((eventPtr->type == LeaveNotify)
  293.         && (winPtr->dispPtr->focussedOnEnter)
  294.         && (eventPtr->xcrossing.detail != NotifyInferior)
  295.         && (winPtr->dispPtr->focusTopLevelPtr == winPtr)) {
  296.         ChangeFocusTopLevelPtr(winPtr->dispPtr, (TkWindow *) NULL,
  297.             eventPtr->xcrossing.mode);
  298.     }
  299.     return 1;
  300.     }
  301. }
  302.  
  303. /*
  304.  *----------------------------------------------------------------------
  305.  *
  306.  * ChangeFocusTopLevelPtr --
  307.  *
  308.  *    This procedure is invoked to change the focusTopLevelPtr field
  309.  *    of a display.  It notifies the old focus window, if any, and
  310.  *    the new one.
  311.  *
  312.  * Results:
  313.  *    None.
  314.  *
  315.  * Side effects:
  316.  *    Windows get notified, and they can do just about anything
  317.  *    as part of the notification.
  318.  *
  319.  *----------------------------------------------------------------------
  320.  */
  321.  
  322. static void
  323. ChangeFocusTopLevelPtr(dispPtr, winPtr, mode)
  324.     TkDisplay *dispPtr;            /* Display whose focus top-level
  325.                      * changed. */
  326.     TkWindow *winPtr;            /* Top-level window that is to be the
  327.                      * new focus top-level for display.
  328.                      * If NULL, clears the old focus
  329.                      * window without setting a new one. */
  330.     int mode;                /* Mode to use for generated events:
  331.                      * NotifyNormal, NotifyGrab, or
  332.                      * NotifyUngrab. */
  333. {
  334.     TkWindow *focusPtr;
  335.  
  336.     if (dispPtr->focusTopLevelPtr == winPtr) {
  337.     /*
  338.      * The focus is already where it's supposed to be, so there's
  339.      * nothing else to do.
  340.      */
  341.     return;
  342.     }
  343.  
  344.     if (dispPtr->focusTopLevelPtr != NULL) {
  345.     focusPtr = dispPtr->focusTopLevelPtr->mainPtr->focusPtr;
  346.     if (focusPtr != NULL) {
  347.         QueueFocusEvent(focusPtr, FocusOut, mode, NotifyAncestor);
  348.     }
  349.     QueueFocusEvent(dispPtr->focusTopLevelPtr, FocusOut, mode,
  350.         NotifyVirtual);
  351.     }
  352.     if (winPtr != NULL) {
  353.     focusPtr = winPtr->mainPtr->focusPtr;
  354.     QueueFocusEvent(winPtr, FocusIn, mode, NotifyVirtual);
  355.     if (focusPtr != NULL) {
  356.         QueueFocusEvent(focusPtr, FocusIn, mode, NotifyAncestor);
  357.     }
  358.     }
  359.     dispPtr->focusTopLevelPtr = winPtr;
  360.     dispPtr->focussedOnEnter = 0;
  361. }
  362.  
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  * SetFocus --
  367.  *
  368.  *    This procedure is invoked to change the focus window for
  369.  *    an application.
  370.  *
  371.  * Results:
  372.  *    None.
  373.  *
  374.  * Side effects:
  375.  *    Event handlers may be invoked to process the change of
  376.  *    focus.
  377.  *
  378.  *----------------------------------------------------------------------
  379.  */
  380.  
  381. static void
  382. SetFocus(winPtr, focusPtr)
  383.     TkWindow *winPtr;        /* Window that identifies the application
  384.                  * whose focus is to change. */
  385.     TkWindow *focusPtr;        /* Window that is to be the new focus for
  386.                  * the application.  May be NULL. */
  387. {
  388.     if (winPtr->mainPtr->focusPtr == focusPtr) {
  389.     return;
  390.     }
  391.     if ((winPtr->dispPtr->focusTopLevelPtr != NULL) &&
  392.         (winPtr->mainPtr == winPtr->dispPtr->focusTopLevelPtr->mainPtr)) {
  393.     if (winPtr->mainPtr->focusPtr != NULL) {
  394.         QueueFocusEvent(winPtr->mainPtr->focusPtr, FocusOut,
  395.             NotifyNormal, NotifyAncestor);
  396.     }
  397.     if (focusPtr != NULL) {
  398.         QueueFocusEvent(focusPtr, FocusIn, NotifyNormal, NotifyAncestor);
  399.     }
  400.     }
  401.     winPtr->mainPtr->focusPtr = focusPtr;
  402. }
  403.  
  404. /*
  405.  *----------------------------------------------------------------------
  406.  *
  407.  * QueueFocusEvent --
  408.  *
  409.  *    This procedure implements the mechanics of notifying a window
  410.  *    that has just gotten or lost the focus.  It generates an
  411.  *    appropriate X event, queues it to be process before any other
  412.  *    events from the server (but after any other queued events),
  413.  *    and also uses the (now obsolete) mechanism of calling a focus
  414.  *    procedure.
  415.  *
  416.  * Results:
  417.  *    None.
  418.  *
  419.  * Side effects:
  420.  *    Depends on the actions associated with the focus event and
  421.  *    procedure callback.
  422.  *
  423.  *----------------------------------------------------------------------
  424.  */
  425.  
  426. static void
  427. QueueFocusEvent(winPtr, type, mode, detail)
  428.     TkWindow *winPtr;        /* Window that's getting or losing focus. */
  429.     int type;            /* FocusIn or FocusOut:  tells whether
  430.                  * winPtr is getting or losing the focus. */
  431.     int mode;            /* Mode to use for event: NotifyNormal,
  432.                  * NotifyGrab, or NotifyUngrab. */
  433.     int detail;            /* Detail to use for event:  NotifyAncestor
  434.                  * for the ultimate destination of the focus,
  435.                  * and NotifyVirtual for the top-level window
  436.                  * that actually got the X focus. */
  437. {
  438.     XEvent event;
  439.  
  440.     if (winPtr->flags & TK_ALREADY_DEAD) {
  441.     /*
  442.      * Window is in the process of being destroyed so quit (otherwise
  443.      * the code below may recreate the window!).
  444.      */
  445.     return;
  446.     }
  447.     Tk_MakeWindowExist((Tk_Window) winPtr);
  448.  
  449.     /*
  450.      * Generate an event for the focus change and process the event.
  451.      */
  452.  
  453.     event.type = type;
  454.     event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
  455.     event.xfocus.send_event = GENERATED_EVENT;
  456.     event.xfocus.display = winPtr->display;
  457.     event.xfocus.window = winPtr->window;
  458.     event.xfocus.mode = mode;
  459.     event.xfocus.detail = detail;
  460.     TkQueueEvent(winPtr->dispPtr, &event);
  461.  
  462.     if ((detail == NotifyAncestor) && (winPtr->focusProc != NULL)) {
  463.     (*winPtr->focusProc)(winPtr->focusData, (type == FocusIn));
  464.     }
  465. }
  466.  
  467. /*
  468.  *----------------------------------------------------------------------
  469.  *
  470.  * TkFocusDeadWindow --
  471.  *
  472.  *    This procedure is invoked when it is determined that
  473.  *    a window is dead.  It cleans up focus-related information
  474.  *    about the window.
  475.  *
  476.  * Results:
  477.  *    None.
  478.  *
  479.  * Side effects:
  480.  *    Various things get cleaned up and recycled.
  481.  *
  482.  *----------------------------------------------------------------------
  483.  */
  484.  
  485. void
  486. TkFocusDeadWindow(winPtr)
  487.     register TkWindow *winPtr;        /* Information about the window
  488.                      * that is being deleted. */
  489. {
  490.     if (winPtr->mainPtr != NULL) {
  491.     if (winPtr->mainPtr->focusDefaultPtr == winPtr) {
  492.         winPtr->mainPtr->focusDefaultPtr = NULL;
  493.     }
  494.     if (winPtr->mainPtr->focusPtr == winPtr) {
  495.         SetFocus(winPtr, winPtr->mainPtr->focusDefaultPtr);
  496.     }
  497.     }
  498.     if (winPtr->dispPtr->focusTopLevelPtr == winPtr) {
  499.     winPtr->dispPtr->focusTopLevelPtr = NULL;
  500.     }
  501. }
  502.