1/* 2 * tkMacOSXScale.c -- 3 * 4 * This file implements the Macintosh specific portion of the 5 * scale widget. 6 * 7 * Copyright (c) 1996 by Sun Microsystems, Inc. 8 * Copyright (c) 1998-2000 by Scriptics Corporation. 9 * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net> 10 * 11 * See the file "license.terms" for information on usage and redistribution 12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id: tkMacOSXScale.c,v 1.2.2.9 2007/06/29 03:22:02 das Exp $ 15 */ 16 17#include "tkMacOSXPrivate.h" 18#include "tkScale.h" 19 20/* 21#ifdef TK_MAC_DEBUG 22#define TK_MAC_DEBUG_SCALE 23#endif 24*/ 25 26/* 27 * Defines used in this file. 28 */ 29 30#define slider 1110 31#define inSlider 1 32#define inInc 2 33#define inDecr 3 34 35/* 36 * Declaration of Macintosh specific scale structure. 37 */ 38 39typedef struct MacScale { 40 TkScale info; /* Generic scale info. */ 41 int flags; /* Flags. */ 42 ControlRef scaleHandle; /* Handle to the Scale control struct. */ 43} MacScale; 44 45/* 46 * Globals uses locally in this file. 47 */ 48static ControlActionUPP scaleActionProc = NULL; /* Pointer to func. */ 49 50/* 51 * Forward declarations for procedures defined later in this file: 52 */ 53 54static void MacScaleEventProc(ClientData clientData, XEvent *eventPtr); 55static pascal void ScaleActionProc(ControlRef theControl, 56 ControlPartCode partCode); 57 58 59/* 60 *---------------------------------------------------------------------- 61 * 62 * TkpCreateScale -- 63 * 64 * Allocate a new TkScale structure. 65 * 66 * Results: 67 * Returns a newly allocated TkScale structure. 68 * 69 * Side effects: 70 * None. 71 * 72 *---------------------------------------------------------------------- 73 */ 74 75TkScale * 76TkpCreateScale( 77 Tk_Window tkwin) 78{ 79 MacScale *macScalePtr = (MacScale *) ckalloc(sizeof(MacScale)); 80 81 macScalePtr->scaleHandle = NULL; 82 if (scaleActionProc == NULL) { 83 scaleActionProc = NewControlActionUPP(ScaleActionProc); 84 } 85 86 Tk_CreateEventHandler(tkwin, ButtonPressMask, 87 MacScaleEventProc, (ClientData) macScalePtr); 88 89 return (TkScale *) macScalePtr; 90} 91 92/* 93 *---------------------------------------------------------------------- 94 * 95 * TkpDestroyScale -- 96 * 97 * Free Macintosh specific resources. 98 * 99 * Results: 100 * None 101 * 102 * Side effects: 103 * The slider control is destroyed. 104 * 105 *---------------------------------------------------------------------- 106 */ 107 108void 109TkpDestroyScale( 110 TkScale *scalePtr) 111{ 112 MacScale *macScalePtr = (MacScale *) scalePtr; 113 114 /* 115 * Free Macintosh control. 116 */ 117 118 if (macScalePtr->scaleHandle != NULL) { 119 DisposeControl(macScalePtr->scaleHandle); 120 } 121} 122 123/* 124 *---------------------------------------------------------------------- 125 * 126 * TkpDisplayScale -- 127 * 128 * This procedure is invoked as an idle handler to redisplay 129 * the contents of a scale widget. 130 * 131 * Results: 132 * None. 133 * 134 * Side effects: 135 * The scale gets redisplayed. 136 * 137 *---------------------------------------------------------------------- 138 */ 139 140void 141TkpDisplayScale( 142 ClientData clientData) /* Widget record for scale. */ 143{ 144 TkScale *scalePtr = (TkScale *) clientData; 145 Tk_Window tkwin = scalePtr->tkwin; 146 Tcl_Interp *interp = scalePtr->interp; 147 int result; 148 char string[PRINT_CHARS]; 149 MacScale *macScalePtr = (MacScale *) clientData; 150 Rect r; 151 WindowRef windowRef; 152 CGrafPtr destPort, savePort; 153 Boolean portChanged; 154 MacDrawable *macDraw; 155 SInt32 initialValue, minValue, maxValue; 156 UInt16 numTicks; 157 158#ifdef TK_MAC_DEBUG_SCALE 159 TkMacOSXDbgMsg("TkpDisplayScale"); 160#endif 161 scalePtr->flags &= ~REDRAW_PENDING; 162 if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(scalePtr->tkwin)) { 163 goto done; 164 } 165 166 /* 167 * Invoke the scale's command if needed. 168 */ 169 170 Tcl_Preserve((ClientData) scalePtr); 171 if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) { 172 Tcl_Preserve((ClientData) interp); 173 sprintf(string, scalePtr->format, scalePtr->value); 174 result = Tcl_VarEval(interp, scalePtr->command, " ", string, NULL); 175 if (result != TCL_OK) { 176 Tcl_AddErrorInfo(interp, "\n (command executed by scale)"); 177 Tcl_BackgroundError(interp); 178 } 179 Tcl_Release((ClientData) interp); 180 } 181 scalePtr->flags &= ~INVOKE_COMMAND; 182 if (scalePtr->flags & SCALE_DELETED) { 183 Tcl_Release((ClientData) scalePtr); 184 return; 185 } 186 Tcl_Release((ClientData) scalePtr); 187 188 /* 189 * Now handle the part of redisplay that is the same for 190 * horizontal and vertical scales: border and traversal 191 * highlight. 192 */ 193 194 if (scalePtr->highlightWidth != 0) { 195 GC gc = Tk_GCForColor(scalePtr->highlightColorPtr, Tk_WindowId(tkwin)); 196 197 Tk_DrawFocusHighlight(tkwin, gc, scalePtr->highlightWidth, 198 Tk_WindowId(tkwin)); 199 } 200 Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), scalePtr->bgBorder, 201 scalePtr->highlightWidth, scalePtr->highlightWidth, 202 Tk_Width(tkwin) - 2*scalePtr->highlightWidth, 203 Tk_Height(tkwin) - 2*scalePtr->highlightWidth, 204 scalePtr->borderWidth, scalePtr->relief); 205 206 /* 207 * Set up port for drawing Macintosh control. 208 */ 209 210 macDraw = (MacDrawable *) Tk_WindowId(tkwin); 211 destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin)); 212 windowRef = TkMacOSXDrawableWindow(Tk_WindowId(tkwin)); 213 portChanged = QDSwapPort(destPort, &savePort); 214 TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin)); 215 216 /* 217 * Create Macintosh control. 218 */ 219 220#define MAC_OSX_SCROLL_WIDTH 10 221 222 if (scalePtr->orient == ORIENT_HORIZONTAL) { 223 int offset = (Tk_Height(tkwin) - MAC_OSX_SCROLL_WIDTH)/2; 224 225 if (offset < 0) { 226 offset = 0; 227 } 228 229 r.left = macDraw->xOff + scalePtr->inset; 230 r.top = macDraw->yOff + offset; 231 r.right = macDraw->xOff+Tk_Width(tkwin) - scalePtr->inset; 232 r.bottom = macDraw->yOff + offset + MAC_OSX_SCROLL_WIDTH/2; 233 } else { 234 int offset = (Tk_Width(tkwin) - MAC_OSX_SCROLL_WIDTH)/2; 235 236 if (offset < 0) { 237 offset = 0; 238 } 239 240 r.left = macDraw->xOff + offset; 241 r.top = macDraw->yOff + scalePtr->inset; 242 r.right = macDraw->xOff + offset + MAC_OSX_SCROLL_WIDTH/2; 243 r.bottom = macDraw->yOff+Tk_Height(tkwin) - scalePtr->inset; 244 } 245 246 if (macScalePtr->scaleHandle == NULL) { 247#ifdef TK_MAC_DEBUG_SCALE 248 TkMacOSXDbgMsg("Initialising scale"); 249#endif 250 initialValue = scalePtr->value; 251 if (scalePtr->orient == ORIENT_HORIZONTAL) { 252 minValue = scalePtr->fromValue; 253 maxValue = scalePtr->toValue; 254 } else { 255 minValue = scalePtr->fromValue; 256 maxValue = scalePtr->toValue; 257 } 258 259 if (scalePtr->tickInterval == 0) { 260 numTicks = 0; 261 } else { 262 numTicks = (maxValue - minValue)/scalePtr->tickInterval; 263 } 264 265 CreateSliderControl(windowRef, &r, initialValue, minValue, maxValue, 266 kControlSliderPointsDownOrRight, numTicks, 1, scaleActionProc, 267 &(macScalePtr->scaleHandle)); 268 SetControlReference(macScalePtr->scaleHandle, (UInt32) scalePtr); 269 270 if (IsWindowActive(windowRef)) { 271 macScalePtr->flags |= ACTIVE; 272 } 273 } else { 274 SetControlBounds(macScalePtr->scaleHandle, &r); 275 SetControl32BitValue(macScalePtr->scaleHandle, scalePtr->value); 276 SetControl32BitMinimum(macScalePtr->scaleHandle, scalePtr->fromValue); 277 SetControl32BitMaximum(macScalePtr->scaleHandle, scalePtr->toValue); 278 } 279 280 /* 281 * Finally draw the control. 282 */ 283 284 SetControlVisibility(macScalePtr->scaleHandle,true,true); 285 HiliteControl(macScalePtr->scaleHandle,0); 286 Draw1Control(macScalePtr->scaleHandle); 287 288 if (portChanged) { 289 QDSwapPort(savePort, NULL); 290 } 291done: 292 scalePtr->flags &= ~REDRAW_ALL; 293} 294 295/* 296 *---------------------------------------------------------------------- 297 * 298 * TkpScaleElement -- 299 * 300 * Determine which part of a scale widget lies under a given 301 * point. 302 * 303 * Results: 304 * The return value is either TROUGH1, SLIDER, TROUGH2, or 305 * OTHER, depending on which of the scale's active elements 306 * (if any) is under the point at (x,y). 307 * 308 * Side effects: 309 * None. 310 * 311 *---------------------------------------------------------------------- 312 */ 313 314int 315TkpScaleElement( 316 TkScale *scalePtr, /* Widget record for scale. */ 317 int x, int y) /* Coordinates within scalePtr's window. */ 318{ 319 MacScale *macScalePtr = (MacScale *) scalePtr; 320 ControlPartCode part; 321 Point where; 322 Rect bounds; 323 CGrafPtr destPort, savePort; 324 Boolean portChanged; 325 326#ifdef TK_MAC_DEBUG_SCALE 327 TkMacOSXDbgMsg("TkpScaleElement"); 328#endif 329 destPort = TkMacOSXGetDrawablePort(Tk_WindowId(scalePtr->tkwin)); 330 portChanged = QDSwapPort(destPort, &savePort); 331 332 /* 333 * All of the calculations in this procedure mirror those in 334 * DisplayScrollbar. Be sure to keep the two consistent. 335 */ 336 337 TkMacOSXWinBounds((TkWindow *) scalePtr->tkwin, &bounds); 338 where.h = x + bounds.left; 339 where.v = y + bounds.top; 340 part = TestControl(macScalePtr->scaleHandle, where); 341 342 if (portChanged) { 343 QDSwapPort(savePort, NULL); 344 } 345 346#ifdef TK_MAC_DEBUG_SCALE 347 fprintf (stderr,"ScalePart %d, pos ( %d %d )\n", part, where.h, where.v ); 348#endif 349 350 switch (part) { 351 case inSlider: 352 return SLIDER; 353 case inInc: 354 if (scalePtr->orient == ORIENT_VERTICAL) { 355 return TROUGH1; 356 } else { 357 return TROUGH2; 358 } 359 case inDecr: 360 if (scalePtr->orient == ORIENT_VERTICAL) { 361 return TROUGH2; 362 } else { 363 return TROUGH1; 364 } 365 default: 366 return OTHER; 367 } 368} 369 370/* 371 *-------------------------------------------------------------- 372 * 373 * MacScaleEventProc -- 374 * 375 * This procedure is invoked by the Tk dispatcher for 376 * ButtonPress events on scales. 377 * 378 * Results: 379 * None. 380 * 381 * Side effects: 382 * When the window gets deleted, internal structures get 383 * cleaned up. When it gets exposed, it is redisplayed. 384 * 385 *-------------------------------------------------------------- 386 */ 387 388static void 389MacScaleEventProc( 390 ClientData clientData, /* Information about window. */ 391 XEvent *eventPtr) /* Information about event. */ 392{ 393 MacScale *macScalePtr = (MacScale *) clientData; 394 Point where; 395 Rect bounds; 396 int part; 397 CGrafPtr destPort, savePort; 398 Boolean portChanged; 399 400#ifdef TK_MAC_DEBUG_SCALE 401 fprintf(stderr,"MacScaleEventProc\n" ); 402#endif 403 404 /* 405 * To call Macintosh control routines we must have the port 406 * set to the window containing the control. We will then test 407 * which part of the control was hit and act accordingly. 408 */ 409 410 destPort = TkMacOSXGetDrawablePort(Tk_WindowId(macScalePtr->info.tkwin)); 411 portChanged = QDSwapPort(destPort, &savePort); 412 TkMacOSXSetUpClippingRgn(Tk_WindowId(macScalePtr->info.tkwin)); 413 414 TkMacOSXWinBounds((TkWindow *) macScalePtr->info.tkwin, &bounds); 415 where.h = eventPtr->xbutton.x + bounds.left; 416 where.v = eventPtr->xbutton.y + bounds.top; 417#ifdef TK_MAC_DEBUG_SCALE 418 TkMacOSXDbgMsg("calling TestControl"); 419#endif 420 part = TestControl(macScalePtr->scaleHandle, where); 421 if (part == 0) { 422 return; 423 } 424 425 TkMacOSXTrackingLoop(1); 426 part = HandleControlClick(macScalePtr->scaleHandle, where, 427 TkMacOSXModifierState(), scaleActionProc); 428 TkMacOSXTrackingLoop(0); 429 430 /* 431 * Update the value for the widget. 432 */ 433 434 macScalePtr->info.value = GetControlValue(macScalePtr->scaleHandle); 435 /* TkScaleSetValue(&macScalePtr->info, macScalePtr->info.value, 1, 0); */ 436 437 /* 438 * The HandleControlClick call will "eat" the ButtonUp event. We now 439 * generate a ButtonUp event so Tk will unset implicit grabs etc. 440 */ 441 442 TkGenerateButtonEventForXPointer(Tk_WindowId(macScalePtr->info.tkwin)); 443 444 if (portChanged) { 445 QDSwapPort(savePort, NULL); 446 } 447} 448 449/* 450 *-------------------------------------------------------------- 451 * 452 * ScaleActionProc -- 453 * 454 * Callback procedure used by the Macintosh toolbox call 455 * HandleControlClick. This call will update the display 456 * while the scrollbar is being manipulated by the user. 457 * 458 * Results: 459 * None. 460 * 461 * Side effects: 462 * May change the display. 463 * 464 *-------------------------------------------------------------- 465 */ 466 467static pascal void 468ScaleActionProc( 469 ControlRef theControl, /* Handle to scrollbat control */ 470 ControlPartCode partCode) /* Part of scrollbar that was "hit" */ 471{ 472 int value; 473 TkScale *scalePtr = (TkScale *) GetControlReference(theControl); 474 475#ifdef TK_MAC_DEBUG_SCALE 476 TkMacOSXDbgMsg("ScaleActionProc"); 477#endif 478 value = GetControlValue(theControl); 479 TkScaleSetValue(scalePtr, value, 1, 1); 480 Tcl_Preserve((ClientData) scalePtr); 481 TkMacOSXRunTclEventLoop(); 482 Tcl_Release((ClientData) scalePtr); 483} 484 485