502 lines
14 KiB
C
502 lines
14 KiB
C
|
/*
|
|||
|
* tkFocus.c --
|
|||
|
*
|
|||
|
* This file contains procedures that manage the input
|
|||
|
* focus for Tk.
|
|||
|
*
|
|||
|
* Copyright (c) 1990-1993 The Regents of the University of California.
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* Permission is hereby granted, without written agreement and without
|
|||
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|||
|
* software and its documentation for any purpose, provided that the
|
|||
|
* above copyright notice and the following two paragraphs appear in
|
|||
|
* all copies of this software.
|
|||
|
*
|
|||
|
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
|||
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
|||
|
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
|
|||
|
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
*
|
|||
|
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
|||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|||
|
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|||
|
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
|||
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|||
|
*/
|
|||
|
|
|||
|
#ifndef lint
|
|||
|
static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkFocus.c,v 1.7 93/06/16 17:16:47 ouster Exp $ SPRITE (Berkeley)";
|
|||
|
#endif
|
|||
|
|
|||
|
#include "tkInt.h"
|
|||
|
#include "tkConfig.h"
|
|||
|
|
|||
|
/*
|
|||
|
* Magic value stored in "send_event" field to mark a FocusIn or FocusOut
|
|||
|
* event generated by this file. It indicates to TkFilterFocusEvent that
|
|||
|
* it shouldn't process this event.
|
|||
|
*/
|
|||
|
|
|||
|
#define GENERATED_EVENT 0x123abcde
|
|||
|
|
|||
|
/*
|
|||
|
* Forward declarations for procedures defined in this file:
|
|||
|
*/
|
|||
|
|
|||
|
static void ChangeFocusTopLevelPtr _ANSI_ARGS_((TkDisplay *dispPtr,
|
|||
|
TkWindow *winPtr, int mode));
|
|||
|
static void QueueFocusEvent _ANSI_ARGS_((TkWindow *winPtr,
|
|||
|
int type, int mode, int detail));
|
|||
|
static void SetFocus _ANSI_ARGS_((TkWindow *winPtr,
|
|||
|
TkWindow *focusPtr));
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_CreateFocusHandler --
|
|||
|
*
|
|||
|
* Arrange for a procedure to be called whenever the focus
|
|||
|
* enters or leaves a given window.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* After this procedure has been invoked, whenever tkwin gets
|
|||
|
* or loses the input focus, proc will be called. It should have
|
|||
|
* the following structure:
|
|||
|
*
|
|||
|
* void
|
|||
|
* proc(clientData, gotFocus)
|
|||
|
* ClientData clientData;
|
|||
|
* int gotFocus;
|
|||
|
* {
|
|||
|
* }
|
|||
|
*
|
|||
|
* The clientData argument to "proc" will be the same as the
|
|||
|
* clientData argument to this procedure. GotFocus will be
|
|||
|
* 1 if tkwin is getting the focus, and 0 if it's losing the
|
|||
|
* focus.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_CreateFocusHandler(tkwin, proc, clientData)
|
|||
|
Tk_Window tkwin; /* Token for window. */
|
|||
|
Tk_FocusProc *proc; /* Procedure to call when tkwin gets
|
|||
|
* or loses the input focus. */
|
|||
|
ClientData clientData; /* Arbitrary value to pass to proc. */
|
|||
|
{
|
|||
|
register TkWindow *winPtr = (TkWindow *) tkwin;
|
|||
|
|
|||
|
winPtr->focusProc = proc;
|
|||
|
winPtr->focusData = clientData;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_FocusCmd --
|
|||
|
*
|
|||
|
* This procedure is invoked to process the "focus" Tcl command.
|
|||
|
* See the user documentation for details on what it does.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A standard Tcl result.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* See the user documentation.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
Tk_FocusCmd(clientData, interp, argc, argv)
|
|||
|
ClientData clientData; /* Main window associated with
|
|||
|
* interpreter. */
|
|||
|
Tcl_Interp *interp; /* Current interpreter. */
|
|||
|
int argc; /* Number of arguments. */
|
|||
|
char **argv; /* Argument strings. */
|
|||
|
{
|
|||
|
Tk_Window tkwin = (Tk_Window) clientData;
|
|||
|
register TkWindow *winPtr = (TkWindow *) clientData;
|
|||
|
register TkWindow *newPtr;
|
|||
|
char c;
|
|||
|
int length;
|
|||
|
|
|||
|
/*
|
|||
|
* If invoked with no arguments, just return the current focus window.
|
|||
|
*/
|
|||
|
|
|||
|
if (argc == 1) {
|
|||
|
if (winPtr->mainPtr->focusPtr == NULL) {
|
|||
|
interp->result = "none";
|
|||
|
} else {
|
|||
|
interp->result = winPtr->mainPtr->focusPtr->pathName;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If invoked with a single argument beginning with "." then focus
|
|||
|
* on that window.
|
|||
|
*/
|
|||
|
|
|||
|
if ((argc == 2) && (argv[1][0] == '.')) {
|
|||
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
|
|||
|
if (newPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
SetFocus(winPtr, newPtr);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
length = strlen(argv[1]);
|
|||
|
c = argv[1][0];
|
|||
|
if ((c == 'd') && (strncmp(argv[1], "default", length) == 0)) {
|
|||
|
if (argc > 3) {
|
|||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
|||
|
argv[0], " default ?window?\"", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (argc == 2) {
|
|||
|
if (winPtr->mainPtr->focusDefaultPtr == NULL) {
|
|||
|
interp->result = "none";
|
|||
|
} else {
|
|||
|
interp->result = winPtr->mainPtr->focusDefaultPtr->pathName;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
if ((argv[2][0] == 'n')
|
|||
|
&& (strncmp(argv[2], "none", strlen(argv[2])) == 0)) {
|
|||
|
newPtr = NULL;
|
|||
|
} else {
|
|||
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
|
|||
|
if (newPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
winPtr->mainPtr->focusDefaultPtr = newPtr;
|
|||
|
} else if ((c == 'n') && (strncmp(argv[1], "none", length) == 0)) {
|
|||
|
if (argc != 2) {
|
|||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
|||
|
argv[0], " none\"", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
SetFocus(winPtr, (TkWindow *) NULL);
|
|||
|
} else {
|
|||
|
Tcl_AppendResult(interp, "bad option \"", argv[1],
|
|||
|
"\": must be default or none", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkFocusFilterEvent --
|
|||
|
*
|
|||
|
* This procedure is invoked by Tk_HandleEvent when it encounters
|
|||
|
* a FocusIn, FocusOut, Enter, or Leave event.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A return value of 1 means that Tk_HandleEvent should process
|
|||
|
* the event normally (i.e. event handlers should be invoked).
|
|||
|
* A return value of 0 means that this event should be ignored.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* An additional event may be generated and processed.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
TkFocusFilterEvent(winPtr, eventPtr)
|
|||
|
register TkWindow *winPtr; /* Window that focus event is directed to. */
|
|||
|
XEvent *eventPtr; /* FocusIn or FocusOut event. */
|
|||
|
{
|
|||
|
if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
|
|||
|
/*
|
|||
|
* If this event was generated by us then just process it
|
|||
|
* normally.
|
|||
|
*/
|
|||
|
|
|||
|
if (eventPtr->xfocus.send_event == GENERATED_EVENT) {
|
|||
|
eventPtr->xfocus.send_event = 1;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Ignore the focus event if any of the following things is
|
|||
|
* true:
|
|||
|
*
|
|||
|
* 1. The event isn't for a top-level window.
|
|||
|
* 2. The event has detail NotifyInferior (which means the
|
|||
|
* focus moved around within the top-level window; it
|
|||
|
* didn't move between the top-level window and the
|
|||
|
* outside world.
|
|||
|
* 3. The event has detail NotifyPointer (I don't really understand
|
|||
|
* what these events are for, but they don't seem to serve
|
|||
|
* any useful purpose).
|
|||
|
*/
|
|||
|
|
|||
|
if (!(winPtr->flags & TK_TOP_LEVEL)
|
|||
|
|| (eventPtr->xfocus.detail == NotifyInferior)
|
|||
|
|| (eventPtr->xfocus.detail == NotifyPointer)) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* This is a useful event. Notify both the top-level window
|
|||
|
* and the window that has been assigned the focus by the Tk
|
|||
|
* "focus" command.
|
|||
|
*/
|
|||
|
|
|||
|
if (eventPtr->type == FocusOut) {
|
|||
|
ChangeFocusTopLevelPtr(winPtr->dispPtr, (TkWindow *) NULL,
|
|||
|
eventPtr->xfocus.mode);
|
|||
|
} else {
|
|||
|
ChangeFocusTopLevelPtr(winPtr->dispPtr, winPtr,
|
|||
|
eventPtr->xfocus.mode);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* This particular event should now be ignored, since we just
|
|||
|
* generated events to notify all of the relevant windows.
|
|||
|
*/
|
|||
|
|
|||
|
return 0;
|
|||
|
} else {
|
|||
|
/*
|
|||
|
* This is an Enter or Leave event. If there's no window manager,
|
|||
|
* or if the window manager is not moving the focus around (e.g.
|
|||
|
* if the disgusting "NoTitleFocus" option has been selected in
|
|||
|
* twm), then we won't get FocusIn or FocusOut events. Instead,
|
|||
|
* watch enter and leave events. If an Enter event arrives for a
|
|||
|
* top-level window with its focus field set, but we don't have a
|
|||
|
* record of a FocusIn event, then simulate one. If a Leave event
|
|||
|
* arrives and focus was set for the window via an Enter event,
|
|||
|
* then simulate a FocusOut event.
|
|||
|
*/
|
|||
|
|
|||
|
if ((eventPtr->type == EnterNotify) && (winPtr->flags & TK_TOP_LEVEL)
|
|||
|
&& eventPtr->xcrossing.focus
|
|||
|
&& (eventPtr->xcrossing.detail != NotifyInferior)
|
|||
|
&& (winPtr->dispPtr->focusTopLevelPtr != winPtr)) {
|
|||
|
ChangeFocusTopLevelPtr(winPtr->dispPtr, winPtr,
|
|||
|
eventPtr->xcrossing.mode);
|
|||
|
winPtr->dispPtr->focussedOnEnter = 1;
|
|||
|
} else if ((eventPtr->type == LeaveNotify)
|
|||
|
&& (winPtr->dispPtr->focussedOnEnter)
|
|||
|
&& (eventPtr->xcrossing.detail != NotifyInferior)
|
|||
|
&& (winPtr->dispPtr->focusTopLevelPtr == winPtr)) {
|
|||
|
ChangeFocusTopLevelPtr(winPtr->dispPtr, (TkWindow *) NULL,
|
|||
|
eventPtr->xcrossing.mode);
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* ChangeFocusTopLevelPtr --
|
|||
|
*
|
|||
|
* This procedure is invoked to change the focusTopLevelPtr field
|
|||
|
* of a display. It notifies the old focus window, if any, and
|
|||
|
* the new one.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Windows get notified, and they can do just about anything
|
|||
|
* as part of the notification.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
ChangeFocusTopLevelPtr(dispPtr, winPtr, mode)
|
|||
|
TkDisplay *dispPtr; /* Display whose focus top-level
|
|||
|
* changed. */
|
|||
|
TkWindow *winPtr; /* Top-level window that is to be the
|
|||
|
* new focus top-level for display.
|
|||
|
* If NULL, clears the old focus
|
|||
|
* window without setting a new one. */
|
|||
|
int mode; /* Mode to use for generated events:
|
|||
|
* NotifyNormal, NotifyGrab, or
|
|||
|
* NotifyUngrab. */
|
|||
|
{
|
|||
|
TkWindow *focusPtr;
|
|||
|
|
|||
|
if (dispPtr->focusTopLevelPtr == winPtr) {
|
|||
|
/*
|
|||
|
* The focus is already where it's supposed to be, so there's
|
|||
|
* nothing else to do.
|
|||
|
*/
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (dispPtr->focusTopLevelPtr != NULL) {
|
|||
|
focusPtr = dispPtr->focusTopLevelPtr->mainPtr->focusPtr;
|
|||
|
if (focusPtr != NULL) {
|
|||
|
QueueFocusEvent(focusPtr, FocusOut, mode, NotifyAncestor);
|
|||
|
}
|
|||
|
QueueFocusEvent(dispPtr->focusTopLevelPtr, FocusOut, mode,
|
|||
|
NotifyVirtual);
|
|||
|
}
|
|||
|
if (winPtr != NULL) {
|
|||
|
focusPtr = winPtr->mainPtr->focusPtr;
|
|||
|
QueueFocusEvent(winPtr, FocusIn, mode, NotifyVirtual);
|
|||
|
if (focusPtr != NULL) {
|
|||
|
QueueFocusEvent(focusPtr, FocusIn, mode, NotifyAncestor);
|
|||
|
}
|
|||
|
}
|
|||
|
dispPtr->focusTopLevelPtr = winPtr;
|
|||
|
dispPtr->focussedOnEnter = 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* SetFocus --
|
|||
|
*
|
|||
|
* This procedure is invoked to change the focus window for
|
|||
|
* an application.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Event handlers may be invoked to process the change of
|
|||
|
* focus.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
SetFocus(winPtr, focusPtr)
|
|||
|
TkWindow *winPtr; /* Window that identifies the application
|
|||
|
* whose focus is to change. */
|
|||
|
TkWindow *focusPtr; /* Window that is to be the new focus for
|
|||
|
* the application. May be NULL. */
|
|||
|
{
|
|||
|
if (winPtr->mainPtr->focusPtr == focusPtr) {
|
|||
|
return;
|
|||
|
}
|
|||
|
if ((winPtr->dispPtr->focusTopLevelPtr != NULL) &&
|
|||
|
(winPtr->mainPtr == winPtr->dispPtr->focusTopLevelPtr->mainPtr)) {
|
|||
|
if (winPtr->mainPtr->focusPtr != NULL) {
|
|||
|
QueueFocusEvent(winPtr->mainPtr->focusPtr, FocusOut,
|
|||
|
NotifyNormal, NotifyAncestor);
|
|||
|
}
|
|||
|
if (focusPtr != NULL) {
|
|||
|
QueueFocusEvent(focusPtr, FocusIn, NotifyNormal, NotifyAncestor);
|
|||
|
}
|
|||
|
}
|
|||
|
winPtr->mainPtr->focusPtr = focusPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* QueueFocusEvent --
|
|||
|
*
|
|||
|
* This procedure implements the mechanics of notifying a window
|
|||
|
* that has just gotten or lost the focus. It generates an
|
|||
|
* appropriate X event, queues it to be process before any other
|
|||
|
* events from the server (but after any other queued events),
|
|||
|
* and also uses the (now obsolete) mechanism of calling a focus
|
|||
|
* procedure.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Depends on the actions associated with the focus event and
|
|||
|
* procedure callback.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
QueueFocusEvent(winPtr, type, mode, detail)
|
|||
|
TkWindow *winPtr; /* Window that's getting or losing focus. */
|
|||
|
int type; /* FocusIn or FocusOut: tells whether
|
|||
|
* winPtr is getting or losing the focus. */
|
|||
|
int mode; /* Mode to use for event: NotifyNormal,
|
|||
|
* NotifyGrab, or NotifyUngrab. */
|
|||
|
int detail; /* Detail to use for event: NotifyAncestor
|
|||
|
* for the ultimate destination of the focus,
|
|||
|
* and NotifyVirtual for the top-level window
|
|||
|
* that actually got the X focus. */
|
|||
|
{
|
|||
|
XEvent event;
|
|||
|
|
|||
|
if (winPtr->flags & TK_ALREADY_DEAD) {
|
|||
|
/*
|
|||
|
* Window is in the process of being destroyed so quit (otherwise
|
|||
|
* the code below may recreate the window!).
|
|||
|
*/
|
|||
|
return;
|
|||
|
}
|
|||
|
Tk_MakeWindowExist((Tk_Window) winPtr);
|
|||
|
|
|||
|
/*
|
|||
|
* Generate an event for the focus change and process the event.
|
|||
|
*/
|
|||
|
|
|||
|
event.type = type;
|
|||
|
event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
|
|||
|
event.xfocus.send_event = GENERATED_EVENT;
|
|||
|
event.xfocus.display = winPtr->display;
|
|||
|
event.xfocus.window = winPtr->window;
|
|||
|
event.xfocus.mode = mode;
|
|||
|
event.xfocus.detail = detail;
|
|||
|
TkQueueEvent(winPtr->dispPtr, &event);
|
|||
|
|
|||
|
if ((detail == NotifyAncestor) && (winPtr->focusProc != NULL)) {
|
|||
|
(*winPtr->focusProc)(winPtr->focusData, (type == FocusIn));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkFocusDeadWindow --
|
|||
|
*
|
|||
|
* This procedure is invoked when it is determined that
|
|||
|
* a window is dead. It cleans up focus-related information
|
|||
|
* about the window.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Various things get cleaned up and recycled.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
TkFocusDeadWindow(winPtr)
|
|||
|
register TkWindow *winPtr; /* Information about the window
|
|||
|
* that is being deleted. */
|
|||
|
{
|
|||
|
if (winPtr->mainPtr != NULL) {
|
|||
|
if (winPtr->mainPtr->focusDefaultPtr == winPtr) {
|
|||
|
winPtr->mainPtr->focusDefaultPtr = NULL;
|
|||
|
}
|
|||
|
if (winPtr->mainPtr->focusPtr == winPtr) {
|
|||
|
SetFocus(winPtr, winPtr->mainPtr->focusDefaultPtr);
|
|||
|
}
|
|||
|
}
|
|||
|
if (winPtr->dispPtr->focusTopLevelPtr == winPtr) {
|
|||
|
winPtr->dispPtr->focusTopLevelPtr = NULL;
|
|||
|
}
|
|||
|
}
|