/* * Copyright 2004, Joe English. * * Usage: * TtkBlinkCursor(corePtr), usually called in a widget's Init hook, * arranges to periodically toggle the corePtr->flags CURSOR_ON bit * on and off (and schedule a redisplay) whenever the widget has focus. * * Note: Widgets may have additional logic to decide whether * to display the cursor or not (e.g., readonly or disabled states); * TtkBlinkCursor() does not account for this. * * TODO: * Add script-level access to configure application-wide blink rate. */ #include "tkInt.h" #include "ttkTheme.h" #include "ttkWidget.h" #define DEF_CURSOR_ON_TIME 600 /* milliseconds */ #define DEF_CURSOR_OFF_TIME 300 /* milliseconds */ /* Interp-specific data for tracking cursors: */ typedef struct { WidgetCore *owner; /* Widget that currently has cursor */ Tcl_TimerToken timer; /* Blink timer */ int onTime; /* #milliseconds to blink cursor on */ int offTime; /* #milliseconds to blink cursor off */ } CursorManager; /* CursorManagerDeleteProc -- * InterpDeleteProc for cursor manager. */ static void CursorManagerDeleteProc(ClientData clientData, Tcl_Interp *interp) { CursorManager *cm = (CursorManager*)clientData; if (cm->timer) { Tcl_DeleteTimerHandler(cm->timer); } ckfree(clientData); } /* GetCursorManager -- * Look up and create if necessary the interp's cursor manager. */ static CursorManager *GetCursorManager(Tcl_Interp *interp) { static const char *cm_key = "ttk::CursorManager"; CursorManager *cm = Tcl_GetAssocData(interp, cm_key,0); if (!cm) { cm = ckalloc(sizeof(*cm)); cm->timer = 0; cm->owner = 0; cm->onTime = DEF_CURSOR_ON_TIME; cm->offTime = DEF_CURSOR_OFF_TIME; Tcl_SetAssocData(interp,cm_key,CursorManagerDeleteProc,(ClientData)cm); } return cm; } /* CursorBlinkProc -- * Timer handler to blink the insert cursor on and off. */ static void CursorBlinkProc(ClientData clientData) { CursorManager *cm = clientData; int blinkTime; if (cm->owner->flags & CURSOR_ON) { cm->owner->flags &= ~CURSOR_ON; blinkTime = cm->offTime; } else { cm->owner->flags |= CURSOR_ON; blinkTime = cm->onTime; } cm->timer = Tcl_CreateTimerHandler(blinkTime, CursorBlinkProc, clientData); TtkRedisplayWidget(cm->owner); } /* LoseCursor -- * Turn cursor off, disable blink timer. */ static void LoseCursor(CursorManager *cm, WidgetCore *corePtr) { if (corePtr->flags & CURSOR_ON) { corePtr->flags &= ~CURSOR_ON; TtkRedisplayWidget(corePtr); } if (cm->owner == corePtr) { cm->owner = NULL; } if (cm->timer) { Tcl_DeleteTimerHandler(cm->timer); cm->timer = 0; } } /* ClaimCursor -- * Claim ownership of the insert cursor and blink on. */ static void ClaimCursor(CursorManager *cm, WidgetCore *corePtr) { if (cm->owner == corePtr) return; if (cm->owner) LoseCursor(cm, cm->owner); corePtr->flags |= CURSOR_ON; TtkRedisplayWidget(corePtr); cm->owner = corePtr; cm->timer = Tcl_CreateTimerHandler(cm->onTime, CursorBlinkProc, cm); } /* * CursorEventProc -- * Event handler for FocusIn and FocusOut events; * claim/lose ownership of the insert cursor when the widget * acquires/loses keyboard focus. */ #define CursorEventMask (FocusChangeMask|StructureNotifyMask) #define RealFocusEvent(d) \ (d == NotifyInferior || d == NotifyAncestor || d == NotifyNonlinear) static void CursorEventProc(ClientData clientData, XEvent *eventPtr) { WidgetCore *corePtr = (WidgetCore *)clientData; CursorManager *cm = GetCursorManager(corePtr->interp); switch (eventPtr->type) { case DestroyNotify: if (cm->owner == corePtr) LoseCursor(cm, corePtr); Tk_DeleteEventHandler( corePtr->tkwin, CursorEventMask, CursorEventProc, clientData); break; case FocusIn: if (RealFocusEvent(eventPtr->xfocus.detail)) ClaimCursor(cm, corePtr); break; case FocusOut: if (RealFocusEvent(eventPtr->xfocus.detail)) LoseCursor(cm, corePtr); break; } } /* * TtkBlinkCursor (main routine) -- * Arrange to blink the cursor on and off whenever the * widget has focus. */ void TtkBlinkCursor(WidgetCore *corePtr) { Tk_CreateEventHandler( corePtr->tkwin, CursorEventMask, CursorEventProc, corePtr); } /*EOF*/