/*
* tkFocus.c (CTk) --
*
* This file contains procedures that manage the input
* focus for Tk.
*
* Copyright (c) 1990-1994 The Regents of the University of California.
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
* Copyright (c) 1994-1995 Cleveland Clinic Foundation
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* @(#) $Id: ctk.shar,v 1.50 1996/01/15 14:47:16 andrewm Exp andrewm $
*/
#include "tkInt.h"
#include "tkPort.h"
/*
* Hash table mapping top-level windows to their local focus (a descendant
* window). Both key and values are window pointers. There is an
* entry for every top-level window that has ever recieved the focus.
*/
static Tcl_HashTable focusTable;
/*
* Has files static data been initialized?
*/
static int initialized = 0;
/*
*--------------------------------------------------------------
*
* 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;
TkWindow *winPtr = (TkWindow *) clientData;
TkWindow *newPtr, *focusWinPtr, *topLevelPtr;
char c;
size_t length;
Tcl_HashEntry *hPtr;
/*
* If invoked with no arguments, just return the current focus window.
*/
if (argc == 1) {
focusWinPtr = TkGetFocus(winPtr);
if (focusWinPtr != NULL) {
Tcl_SetResult(interp,focusWinPtr->pathName,TCL_VOLATILE);
}
return TCL_OK;
}
/*
* If invoked with a single argument beginning with "." then focus
* on that window.
*/
if (argc == 2) {
if (argv[1][0] == 0) {
return TCL_OK;
}
if (argv[1][0] == '.') {
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
if (newPtr == NULL) {
return TCL_ERROR;
}
if (!(newPtr->flags & TK_ALREADY_DEAD)) {
CtkSetFocus(newPtr);
}
return TCL_OK;
}
}
length = strlen(argv[1]);
c = argv[1][1];
if ((c == 'd') && (strncmp(argv[1], "-displayof", length) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " -displayof window\"", (char *) NULL);
return TCL_ERROR;
}
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
if (newPtr == NULL) {
return TCL_ERROR;
}
newPtr = TkGetFocus(newPtr);
if (newPtr != NULL) {
Tcl_SetResult(interp,newPtr->pathName,TCL_VOLATILE);
}
} else if ((c == 'f') && (strncmp(argv[1], "-force", length) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " -force window\"", (char *) NULL);
return TCL_ERROR;
}
if (argv[2][0] == 0) {
return TCL_OK;
}
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
if (newPtr == NULL) {
return TCL_ERROR;
}
CtkSetFocus(newPtr);
} else if ((c == 'l') && (strncmp(argv[1], "-lastfor", length) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " -lastfor window\"", (char *) NULL);
return TCL_ERROR;
}
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
if (newPtr == NULL) {
return TCL_ERROR;
}
topLevelPtr = Ctk_TopLevel(newPtr);
hPtr = Tcl_FindHashEntry(&focusTable, (char *) topLevelPtr);
if (hPtr && (newPtr = (TkWindow *) Tcl_GetHashValue(hPtr))) {
Tcl_SetResult(interp,topLevelPtr->pathName,TCL_VOLATILE);
}
} else {
Tcl_AppendResult(interp, "bad option \"", argv[1],
"\": must be -displayof, -force, or -lastfor", (char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* CtkSetFocus --
*
* This procedure is invoked to change the focus window for a
* given display in a given application.
*
* Results:
* None.
*
* Side effects:
* Event handlers may be invoked to process the change of
* focus.
*
*----------------------------------------------------------------------
*/
void
CtkSetFocus(winPtr)
TkWindow *winPtr;
{
TkWindow *focusPtr = winPtr->dispPtr->focusPtr;
Ctk_Event event;
Tcl_HashEntry *hPtr;
int new;
if (!initialized) {
Tcl_InitHashTable(&focusTable, TCL_ONE_WORD_KEYS);
initialized = 1;
}
if (winPtr == (TkWindow *)NULL || (winPtr->flags & TK_ALREADY_DEAD)) {
panic("Attempt to set focus to null/dead window");
}
if (Tk_IsTopLevel(winPtr)) {
/*
* Window is a top-level.
* Change focus destination to local focus of top-level.
*/
hPtr = Tcl_FindHashEntry(&focusTable, (char *) winPtr);
if (hPtr && Tcl_GetHashValue(hPtr)) {
winPtr = (TkWindow *) Tcl_GetHashValue(hPtr);
}
} else {
/*
* Set local focus of winPtr's top-level to winPtr.
*/
hPtr = Tcl_CreateHashEntry(&focusTable, (char *) Ctk_TopLevel(winPtr),
&new);
Tcl_SetHashValue(hPtr, (ClientData) winPtr);
}
if (winPtr != focusPtr) {
if (focusPtr && !(focusPtr->flags & TK_ALREADY_DEAD)) {
event.type = CTK_UNFOCUS_EVENT;
event.window = focusPtr;
Tk_HandleEvent(&event);
}
winPtr->dispPtr->focusPtr = winPtr;
Ctk_SetCursor(winPtr, 0, 0);
event.type = CTK_FOCUS_EVENT;
event.window = winPtr;
Tk_HandleEvent(&event);
}
}
/*
*----------------------------------------------------------------------
*
* TkGetFocus --
*
* Given a window, this procedure returns the current focus
* window for its application and display.
*
* Results:
* The return value is a pointer to the window that currently
* has the input focus for the specified application and
* display, or NULL if none.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
TkWindow *
TkGetFocus(winPtr)
TkWindow *winPtr;
{
return winPtr->dispPtr->focusPtr;
}
/*
*----------------------------------------------------------------------
*
* 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:
* The input focus for the window's display may change.
*
*----------------------------------------------------------------------
*/
void
TkFocusDeadWindow(winPtr)
TkWindow *winPtr;
{
if (initialized) {
/*
* Remove window from focusTable. Delete hash entry if winPtr
* is a top-level. Clear hash entry value if winPtr has a local
* focus.
*/
Tcl_HashEntry *hPtr;
TkWindow *focusPtr;
if (Tk_IsTopLevel(winPtr)) {
hPtr = Tcl_FindHashEntry(&focusTable, (char *) winPtr);
if (hPtr) Tcl_DeleteHashEntry(hPtr);
} else {
hPtr = Tcl_FindHashEntry(&focusTable,
(char *) Ctk_TopLevel(winPtr));
if (hPtr && winPtr == (TkWindow *) Tcl_GetHashValue(hPtr)) {
Tcl_SetHashValue(hPtr, (ClientData) (TkWindow *) NULL);
}
}
}
if (winPtr == winPtr->dispPtr->focusPtr) {
/*
* This window has the focus, try to pass focus first to
* window's top-level, then to topmost visible top-level,
* then to main top-level. If none of these exist
* then give up - the application will have exited
* before any more key events will be processed).
*/
TkWindow *newFocusPtr = Ctk_TopLevel(winPtr);
if (!(newFocusPtr->flags & TK_ALREADY_DEAD)) goto gotfocus;
for (newFocusPtr = Ctk_TopChild(winPtr->dispPtr->rootPtr);
newFocusPtr != NULL;
newFocusPtr = Ctk_PriorSibling(newFocusPtr)) {
if (!(newFocusPtr->flags & TK_ALREADY_DEAD)
&& (newFocusPtr->flags & CTK_DISPLAYED)) {
goto gotfocus;
}
}
newFocusPtr = Ctk_BottomChild(winPtr->dispPtr->rootPtr);
if (newFocusPtr && !(newFocusPtr->flags & TK_ALREADY_DEAD)) {
gotfocus:
CtkSetFocus(newFocusPtr);
}
}
}