167 lines
4.2 KiB
C
167 lines
4.2 KiB
C
/*
|
|
* 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, cm);
|
|
}
|
|
return cm;
|
|
}
|
|
|
|
/* CursorBlinkProc --
|
|
* Timer handler to blink the insert cursor on and off.
|
|
*/
|
|
static void
|
|
CursorBlinkProc(ClientData clientData)
|
|
{
|
|
CursorManager *cm = (CursorManager*)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*/
|