1/* 2 * $Id$ 3 * 4 * Copyright 2004, Joe English. 5 * 6 * Usage: 7 * TtkBlinkCursor(corePtr), usually called in a widget's Init hook, 8 * arranges to periodically toggle the corePtr->flags CURSOR_ON bit 9 * on and off (and schedule a redisplay) whenever the widget has focus. 10 * 11 * Note: Widgets may have additional logic to decide whether 12 * to display the cursor or not (e.g., readonly or disabled states); 13 * TtkBlinkCursor() does not account for this. 14 * 15 * TODO: 16 * Add script-level access to configure application-wide blink rate. 17 */ 18 19#include <tk.h> 20#include "ttkTheme.h" 21#include "ttkWidget.h" 22 23#define DEF_CURSOR_ON_TIME 600 /* milliseconds */ 24#define DEF_CURSOR_OFF_TIME 300 /* milliseconds */ 25 26/* Interp-specific data for tracking cursors: 27 */ 28typedef struct 29{ 30 WidgetCore *owner; /* Widget that currently has cursor */ 31 Tcl_TimerToken timer; /* Blink timer */ 32 int onTime; /* #milliseconds to blink cursor on */ 33 int offTime; /* #milliseconds to blink cursor off */ 34} CursorManager; 35 36/* CursorManagerDeleteProc -- 37 * InterpDeleteProc for cursor manager. 38 */ 39static void CursorManagerDeleteProc(ClientData clientData, Tcl_Interp *interp) 40{ 41 CursorManager *cm = (CursorManager*)clientData; 42 if (cm->timer) { 43 Tcl_DeleteTimerHandler(cm->timer); 44 } 45 ckfree(clientData); 46} 47 48/* GetCursorManager -- 49 * Look up and create if necessary the interp's cursor manager. 50 */ 51static CursorManager *GetCursorManager(Tcl_Interp *interp) 52{ 53 static const char *cm_key = "ttk::CursorManager"; 54 CursorManager *cm = (CursorManager *) Tcl_GetAssocData(interp, cm_key,0); 55 56 if (!cm) { 57 cm = (CursorManager*)ckalloc(sizeof(*cm)); 58 cm->timer = 0; 59 cm->owner = 0; 60 cm->onTime = DEF_CURSOR_ON_TIME; 61 cm->offTime = DEF_CURSOR_OFF_TIME; 62 Tcl_SetAssocData(interp,cm_key,CursorManagerDeleteProc,(ClientData)cm); 63 } 64 return cm; 65} 66 67/* CursorBlinkProc -- 68 * Timer handler to blink the insert cursor on and off. 69 */ 70static void 71CursorBlinkProc(ClientData clientData) 72{ 73 CursorManager *cm = (CursorManager*)clientData; 74 int blinkTime; 75 76 if (cm->owner->flags & CURSOR_ON) { 77 cm->owner->flags &= ~CURSOR_ON; 78 blinkTime = cm->offTime; 79 } else { 80 cm->owner->flags |= CURSOR_ON; 81 blinkTime = cm->onTime; 82 } 83 cm->timer = Tcl_CreateTimerHandler(blinkTime, CursorBlinkProc, clientData); 84 TtkRedisplayWidget(cm->owner); 85} 86 87/* LoseCursor -- 88 * Turn cursor off, disable blink timer. 89 */ 90static void LoseCursor(CursorManager *cm, WidgetCore *corePtr) 91{ 92 if (corePtr->flags & CURSOR_ON) { 93 corePtr->flags &= ~CURSOR_ON; 94 TtkRedisplayWidget(corePtr); 95 } 96 if (cm->owner == corePtr) { 97 cm->owner = NULL; 98 } 99 if (cm->timer) { 100 Tcl_DeleteTimerHandler(cm->timer); 101 cm->timer = 0; 102 } 103} 104 105/* ClaimCursor -- 106 * Claim ownership of the insert cursor and blink on. 107 */ 108static void ClaimCursor(CursorManager *cm, WidgetCore *corePtr) 109{ 110 if (cm->owner == corePtr) 111 return; 112 if (cm->owner) 113 LoseCursor(cm, cm->owner); 114 115 corePtr->flags |= CURSOR_ON; 116 TtkRedisplayWidget(corePtr); 117 118 cm->owner = corePtr; 119 cm->timer = Tcl_CreateTimerHandler(cm->onTime, CursorBlinkProc, cm); 120} 121 122/* 123 * CursorEventProc -- 124 * Event handler for FocusIn and FocusOut events; 125 * claim/lose ownership of the insert cursor when the widget 126 * acquires/loses keyboard focus. 127 */ 128 129#define CursorEventMask (FocusChangeMask|StructureNotifyMask) 130#define RealFocusEvent(d) \ 131 (d == NotifyInferior || d == NotifyAncestor || d == NotifyNonlinear) 132 133static void 134CursorEventProc(ClientData clientData, XEvent *eventPtr) 135{ 136 WidgetCore *corePtr = (WidgetCore *)clientData; 137 CursorManager *cm = GetCursorManager(corePtr->interp); 138 139 switch (eventPtr->type) { 140 case DestroyNotify: 141 if (cm->owner == corePtr) 142 LoseCursor(cm, corePtr); 143 Tk_DeleteEventHandler( 144 corePtr->tkwin, CursorEventMask, CursorEventProc, clientData); 145 break; 146 case FocusIn: 147 if (RealFocusEvent(eventPtr->xfocus.detail)) 148 ClaimCursor(cm, corePtr); 149 break; 150 case FocusOut: 151 if (RealFocusEvent(eventPtr->xfocus.detail)) 152 LoseCursor(cm, corePtr); 153 break; 154 } 155} 156 157/* 158 * TtkBlinkCursor (main routine) -- 159 * Arrange to blink the cursor on and off whenever the 160 * widget has focus. 161 */ 162void TtkBlinkCursor(WidgetCore *corePtr) 163{ 164 Tk_CreateEventHandler( 165 corePtr->tkwin, CursorEventMask, CursorEventProc, corePtr); 166} 167 168/*EOF*/ 169