| 1 | /* |
| 2 | * tkSquare.c -- |
| 3 | * |
| 4 | * This module implements "square" widgets. A "square" is |
| 5 | * a widget that displays a single square that can be moved |
| 6 | * around and resized. This file is intended as an example |
| 7 | * of how to build a widget. |
| 8 | * |
| 9 | * Copyright 1991-1992 Regents of the University of California. |
| 10 | * Permission to use, copy, modify, and distribute this |
| 11 | * software and its documentation for any purpose and without |
| 12 | * fee is hereby granted, provided that the above copyright |
| 13 | * notice appear in all copies. The University of California |
| 14 | * makes no representations about the suitability of this |
| 15 | * software for any purpose. It is provided "as is" without |
| 16 | * express or implied warranty. |
| 17 | */ |
| 18 | |
| 19 | #ifndef lint |
| 20 | static char rcsid[] = "$Header: /user6/ouster/wish/scripts/demos/RCS/tkSquare.c,v 1.2 92/04/29 11:45:17 ouster Exp $ SPRITE (Berkeley)"; |
| 21 | #endif |
| 22 | |
| 23 | #include "tkConfig.h" |
| 24 | #include "tk.h" |
| 25 | |
| 26 | /* |
| 27 | * A data structure of the following type is kept for each square |
| 28 | * widget managed by this file: |
| 29 | */ |
| 30 | |
| 31 | typedef struct { |
| 32 | Tk_Window tkwin; /* Window that embodies the square. NULL |
| 33 | * means that the window has been destroyed |
| 34 | * but the data structures haven't yet been |
| 35 | * cleaned up.*/ |
| 36 | Tcl_Interp *interp; /* Interpreter associated with widget. */ |
| 37 | int x, y; /* Position of square's upper-left corner |
| 38 | * within widget. */ |
| 39 | int size; /* Width and height of square. */ |
| 40 | int flags; /* Various flags; see below for |
| 41 | * definitions. */ |
| 42 | |
| 43 | /* |
| 44 | * Information used when displaying widget: |
| 45 | */ |
| 46 | |
| 47 | int borderWidth; /* Width of 3-D border around whole widget. */ |
| 48 | Tk_3DBorder bgBorder; /* Used for drawing background. */ |
| 49 | Tk_3DBorder fgBorder; /* For drawing square. */ |
| 50 | int relief; /* Indicates whether window as a whole is |
| 51 | * raised, sunken, or flat. */ |
| 52 | int doubleBuffer; /* Non-zero means double-buffer redisplay |
| 53 | * with pixmap; zero means draw straight |
| 54 | * onto the display. */ |
| 55 | } Square; |
| 56 | |
| 57 | /* |
| 58 | * Flag bits for squares: |
| 59 | * |
| 60 | * REDRAW_PENDING - 1 means redraw has already been scheduled. |
| 61 | */ |
| 62 | |
| 63 | #define REDRAW_PENDING 1 |
| 64 | |
| 65 | /* |
| 66 | * Information used for argv parsing. |
| 67 | */ |
| 68 | |
| 69 | static Tk_ConfigSpec configSpecs[] = { |
| 70 | {TK_CONFIG_BORDER, "-background", "background", "Background", |
| 71 | "#cdb79e", Tk_Offset(Square, bgBorder), TK_CONFIG_COLOR_ONLY}, |
| 72 | {TK_CONFIG_BORDER, "-background", "background", "Background", |
| 73 | "white", Tk_Offset(Square, bgBorder), TK_CONFIG_MONO_ONLY}, |
| 74 | {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, |
| 75 | (char *) NULL, 0, 0}, |
| 76 | {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, |
| 77 | (char *) NULL, 0, 0}, |
| 78 | {TK_CONFIG_INT, "-borderwidth", "borderWidth", "BorderWidth", |
| 79 | "2", Tk_Offset(Square, borderWidth), 0}, |
| 80 | {TK_CONFIG_INT, "-dbl", "doubleBuffer", "DoubleBuffer", |
| 81 | "1", Tk_Offset(Square, doubleBuffer), 0}, |
| 82 | {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, |
| 83 | (char *) NULL, 0, 0}, |
| 84 | {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", |
| 85 | "#b03060", Tk_Offset(Square, fgBorder), TK_CONFIG_COLOR_ONLY}, |
| 86 | {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", |
| 87 | "black", Tk_Offset(Square, fgBorder), TK_CONFIG_MONO_ONLY}, |
| 88 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", |
| 89 | "raised", Tk_Offset(Square, relief), 0}, |
| 90 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, |
| 91 | (char *) NULL, 0, 0} |
| 92 | }; |
| 93 | |
| 94 | /* |
| 95 | * Forward declarations for procedures defined later in this file: |
| 96 | */ |
| 97 | |
| 98 | static int ConfigureSquare _ANSI_ARGS_((Tcl_Interp *interp, |
| 99 | Square *squarePtr, int argc, char **argv, |
| 100 | int flags)); |
| 101 | static void DestroySquare _ANSI_ARGS_((ClientData clientData)); |
| 102 | static void DisplaySquare _ANSI_ARGS_((ClientData clientData)); |
| 103 | static void KeepInWindow _ANSI_ARGS_((Square *squarePtr)); |
| 104 | static void SquareEventProc _ANSI_ARGS_((ClientData clientData, |
| 105 | XEvent *eventPtr)); |
| 106 | static int SquareWidgetCmd _ANSI_ARGS_((ClientData clientData, |
| 107 | Tcl_Interp *, int argc, char **argv)); |
| 108 | \f |
| 109 | /* |
| 110 | *-------------------------------------------------------------- |
| 111 | * |
| 112 | * Tk_SquareCmd -- |
| 113 | * |
| 114 | * This procedure is invoked to process the "square" Tcl |
| 115 | * command. It creates a new "square" widget. |
| 116 | * |
| 117 | * Results: |
| 118 | * A standard Tcl result. |
| 119 | * |
| 120 | * Side effects: |
| 121 | * A new widget is created and configured. |
| 122 | * |
| 123 | *-------------------------------------------------------------- |
| 124 | */ |
| 125 | |
| 126 | int |
| 127 | Tk_SquareCmd(clientData, interp, argc, argv) |
| 128 | ClientData clientData; /* Main window associated with |
| 129 | * interpreter. */ |
| 130 | Tcl_Interp *interp; /* Current interpreter. */ |
| 131 | int argc; /* Number of arguments. */ |
| 132 | char **argv; /* Argument strings. */ |
| 133 | { |
| 134 | Tk_Window main = (Tk_Window) clientData; |
| 135 | register Square *squarePtr; |
| 136 | Tk_Window tkwin; |
| 137 | |
| 138 | if (argc < 2) { |
| 139 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 140 | argv[0], " pathName ?options?\"", (char *) NULL); |
| 141 | return TCL_ERROR; |
| 142 | } |
| 143 | |
| 144 | tkwin = Tk_CreateWindowFromPath(interp, main, argv[1], (char *) NULL); |
| 145 | if (tkwin == NULL) { |
| 146 | return TCL_ERROR; |
| 147 | } |
| 148 | |
| 149 | /* |
| 150 | * Initialize fields that won't be initialized by ConfigureSquare, |
| 151 | * or which ConfigureSquare expects to have reasonable values |
| 152 | * (e.g. resource pointers). |
| 153 | */ |
| 154 | |
| 155 | squarePtr = (Square *) ckalloc(sizeof(Square)); |
| 156 | squarePtr->tkwin = tkwin; |
| 157 | squarePtr->interp = interp; |
| 158 | squarePtr->x = 0; |
| 159 | squarePtr->y = 0; |
| 160 | squarePtr->size = 20; |
| 161 | squarePtr->bgBorder = NULL; |
| 162 | squarePtr->fgBorder = NULL; |
| 163 | squarePtr->flags = 0; |
| 164 | |
| 165 | Tk_SetClass(squarePtr->tkwin, "Square"); |
| 166 | Tk_CreateEventHandler(squarePtr->tkwin, ExposureMask|StructureNotifyMask, |
| 167 | SquareEventProc, (ClientData) squarePtr); |
| 168 | Tcl_CreateCommand(interp, Tk_PathName(squarePtr->tkwin), SquareWidgetCmd, |
| 169 | (ClientData) squarePtr, (void (*)()) NULL); |
| 170 | if (ConfigureSquare(interp, squarePtr, argc-2, argv+2, 0) != TCL_OK) { |
| 171 | Tk_DestroyWindow(squarePtr->tkwin); |
| 172 | return TCL_ERROR; |
| 173 | } |
| 174 | |
| 175 | interp->result = Tk_PathName(squarePtr->tkwin); |
| 176 | return TCL_OK; |
| 177 | } |
| 178 | \f |
| 179 | /* |
| 180 | *---------------------------------------------------------------------- |
| 181 | * |
| 182 | * ConfigureSquare -- |
| 183 | * |
| 184 | * This procedure is called to process an argv/argc list in |
| 185 | * conjunction with the Tk option database to configure (or |
| 186 | * reconfigure) a square widget. |
| 187 | * |
| 188 | * Results: |
| 189 | * The return value is a standard Tcl result. If TCL_ERROR is |
| 190 | * returned, then interp->result contains an error message. |
| 191 | * |
| 192 | * Side effects: |
| 193 | * Configuration information, such as colors, border width, |
| 194 | * etc. get set for squarePtr; old resources get freed, |
| 195 | * if there were any. |
| 196 | * |
| 197 | *---------------------------------------------------------------------- |
| 198 | */ |
| 199 | |
| 200 | static int |
| 201 | ConfigureSquare(interp, squarePtr, argc, argv, flags) |
| 202 | Tcl_Interp *interp; /* Used for error reporting. */ |
| 203 | register Square *squarePtr; /* Information about widget. */ |
| 204 | int argc; /* Number of valid entries in argv. */ |
| 205 | char **argv; /* Arguments. */ |
| 206 | int flags; /* Flags to pass to |
| 207 | * Tk_ConfigureWidget. */ |
| 208 | { |
| 209 | if (Tk_ConfigureWidget(interp, squarePtr->tkwin, configSpecs, |
| 210 | argc, argv, (char *) squarePtr, flags) != TCL_OK) { |
| 211 | return TCL_ERROR; |
| 212 | } |
| 213 | |
| 214 | /* |
| 215 | * A few options need special processing, such as setting the |
| 216 | * background from a 3-D border. |
| 217 | */ |
| 218 | |
| 219 | Tk_SetBackgroundFromBorder(squarePtr->tkwin, squarePtr->bgBorder); |
| 220 | |
| 221 | /* |
| 222 | * Register the desired geometry for the window. Then arrange for |
| 223 | * the window to be redisplayed. |
| 224 | */ |
| 225 | |
| 226 | Tk_GeometryRequest(squarePtr->tkwin, 200, 150); |
| 227 | Tk_SetInternalBorder(squarePtr->tkwin, squarePtr->borderWidth); |
| 228 | if (!(squarePtr->flags & REDRAW_PENDING)) { |
| 229 | Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr); |
| 230 | squarePtr->flags |= REDRAW_PENDING; |
| 231 | } |
| 232 | return TCL_OK; |
| 233 | } |
| 234 | \f |
| 235 | /* |
| 236 | *-------------------------------------------------------------- |
| 237 | * |
| 238 | * DisplaySquare -- |
| 239 | * |
| 240 | * This procedure redraws the contents of a square window. |
| 241 | * It is invoked as a do-when-idle handler, so it only runs |
| 242 | * when there's nothing else for the application to do. |
| 243 | * |
| 244 | * Results: |
| 245 | * None. |
| 246 | * |
| 247 | * Side effects: |
| 248 | * Information appears on the screen. |
| 249 | * |
| 250 | *-------------------------------------------------------------- |
| 251 | */ |
| 252 | |
| 253 | static void |
| 254 | DisplaySquare(clientData) |
| 255 | ClientData clientData; /* Information about window. */ |
| 256 | { |
| 257 | register Square *squarePtr = (Square *) clientData; |
| 258 | register Tk_Window tkwin = squarePtr->tkwin; |
| 259 | Pixmap pm = None; |
| 260 | Drawable d; |
| 261 | |
| 262 | squarePtr->flags &= ~REDRAW_PENDING; |
| 263 | if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) { |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | /* |
| 268 | * Create a pixmap for double-buffering, if necessary. |
| 269 | */ |
| 270 | |
| 271 | if (squarePtr->doubleBuffer) { |
| 272 | pm = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), |
| 273 | Tk_Width(tkwin), Tk_Height(tkwin), |
| 274 | DefaultDepthOfScreen(Tk_Screen(tkwin))); |
| 275 | d = pm; |
| 276 | } else { |
| 277 | d = Tk_WindowId(tkwin); |
| 278 | } |
| 279 | |
| 280 | /* |
| 281 | * Redraw the widget's background and border. |
| 282 | */ |
| 283 | |
| 284 | Tk_Fill3DRectangle(Tk_Display(tkwin), d, squarePtr->bgBorder, |
| 285 | 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), |
| 286 | squarePtr->borderWidth, squarePtr->relief); |
| 287 | |
| 288 | /* |
| 289 | * Display the square. |
| 290 | */ |
| 291 | |
| 292 | Tk_Fill3DRectangle(Tk_Display(tkwin), d, squarePtr->fgBorder, |
| 293 | squarePtr->x, squarePtr->y, squarePtr->size, squarePtr->size, |
| 294 | squarePtr->borderWidth, TK_RELIEF_RAISED); |
| 295 | |
| 296 | /* |
| 297 | * If double-buffered, copy to the screen and release the pixmap. |
| 298 | */ |
| 299 | |
| 300 | if (squarePtr->doubleBuffer) { |
| 301 | XCopyArea(Tk_Display(tkwin), pm, Tk_WindowId(tkwin), |
| 302 | DefaultGCOfScreen(Tk_Screen(tkwin)), 0, 0, |
| 303 | Tk_Width(tkwin), Tk_Height(tkwin), 0, 0); |
| 304 | XFreePixmap(Tk_Display(tkwin), pm); |
| 305 | } |
| 306 | } |
| 307 | \f |
| 308 | /* |
| 309 | *-------------------------------------------------------------- |
| 310 | * |
| 311 | * SquareWidgetCmd -- |
| 312 | * |
| 313 | * This procedure is invoked to process the Tcl command |
| 314 | * that corresponds to a widget managed by this module. |
| 315 | * See the user documentation for details on what it does. |
| 316 | * |
| 317 | * Results: |
| 318 | * A standard Tcl result. |
| 319 | * |
| 320 | * Side effects: |
| 321 | * See the user documentation. |
| 322 | * |
| 323 | *-------------------------------------------------------------- |
| 324 | */ |
| 325 | |
| 326 | static int |
| 327 | SquareWidgetCmd(clientData, interp, argc, argv) |
| 328 | ClientData clientData; /* Information about square widget. */ |
| 329 | Tcl_Interp *interp; /* Current interpreter. */ |
| 330 | int argc; /* Number of arguments. */ |
| 331 | char **argv; /* Argument strings. */ |
| 332 | { |
| 333 | register Square *squarePtr = (Square *) clientData; |
| 334 | int result = TCL_OK; |
| 335 | int length; |
| 336 | char c; |
| 337 | |
| 338 | if (argc < 2) { |
| 339 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 340 | argv[0], " option ?arg arg ...?\"", (char *) NULL); |
| 341 | return TCL_ERROR; |
| 342 | } |
| 343 | Tk_Preserve((ClientData) squarePtr); |
| 344 | c = argv[1][0]; |
| 345 | length = strlen(argv[1]); |
| 346 | if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) { |
| 347 | if (argc == 2) { |
| 348 | result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs, |
| 349 | (char *) squarePtr, (char *) NULL, 0); |
| 350 | } else if (argc == 3) { |
| 351 | result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs, |
| 352 | (char *) squarePtr, argv[2], 0); |
| 353 | } else { |
| 354 | result = ConfigureSquare(interp, squarePtr, argc-2, argv+2, |
| 355 | TK_CONFIG_ARGV_ONLY); |
| 356 | } |
| 357 | } else if ((c == 'p') && (strncmp(argv[1], "position", length) == 0)) { |
| 358 | if ((argc != 2) && (argc != 4)) { |
| 359 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 360 | argv[0], " position ?x y?\"", (char *) NULL); |
| 361 | goto error; |
| 362 | } |
| 363 | if (argc == 4) { |
| 364 | if ((Tcl_GetInt(interp, argv[2], &squarePtr->x) != TCL_OK) |
| 365 | || (Tcl_GetInt(interp, argv[3], &squarePtr->y) != TCL_OK)) { |
| 366 | goto error; |
| 367 | } |
| 368 | KeepInWindow(squarePtr); |
| 369 | } |
| 370 | sprintf(interp->result, "%d %d", squarePtr->x, squarePtr->y); |
| 371 | } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) { |
| 372 | if ((argc != 2) && (argc != 3)) { |
| 373 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 374 | argv[0], " size ?amount?\"", (char *) NULL); |
| 375 | goto error; |
| 376 | } |
| 377 | if (argc == 3) { |
| 378 | int i; |
| 379 | |
| 380 | if (Tcl_GetInt(interp, argv[2], &i) != TCL_OK) { |
| 381 | goto error; |
| 382 | } |
| 383 | if ((i <= 0) || (i > 100)) { |
| 384 | Tcl_AppendResult(interp, "bad size \"", argv[2], |
| 385 | "\"", (char *) NULL); |
| 386 | goto error; |
| 387 | } |
| 388 | squarePtr->size = i; |
| 389 | KeepInWindow(squarePtr); |
| 390 | } |
| 391 | sprintf(interp->result, "%d", squarePtr->size); |
| 392 | } else { |
| 393 | Tcl_AppendResult(interp, "bad option \"", argv[1], |
| 394 | "\": must be configure, position, or size", (char *) NULL); |
| 395 | goto error; |
| 396 | } |
| 397 | if (!(squarePtr->flags & REDRAW_PENDING)) { |
| 398 | Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr); |
| 399 | squarePtr->flags |= REDRAW_PENDING; |
| 400 | } |
| 401 | Tk_Release((ClientData) squarePtr); |
| 402 | return result; |
| 403 | |
| 404 | error: |
| 405 | Tk_Release((ClientData) squarePtr); |
| 406 | return TCL_ERROR; |
| 407 | } |
| 408 | \f |
| 409 | /* |
| 410 | *-------------------------------------------------------------- |
| 411 | * |
| 412 | * SquareEventProc -- |
| 413 | * |
| 414 | * This procedure is invoked by the Tk dispatcher for various |
| 415 | * events on squares. |
| 416 | * |
| 417 | * Results: |
| 418 | * None. |
| 419 | * |
| 420 | * Side effects: |
| 421 | * When the window gets deleted, internal structures get |
| 422 | * cleaned up. When it gets exposed, it is redisplayed. |
| 423 | * |
| 424 | *-------------------------------------------------------------- |
| 425 | */ |
| 426 | |
| 427 | static void |
| 428 | SquareEventProc(clientData, eventPtr) |
| 429 | ClientData clientData; /* Information about window. */ |
| 430 | XEvent *eventPtr; /* Information about event. */ |
| 431 | { |
| 432 | Square *squarePtr = (Square *) clientData; |
| 433 | |
| 434 | if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { |
| 435 | if (!(squarePtr->flags & REDRAW_PENDING)) { |
| 436 | Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr); |
| 437 | squarePtr->flags |= REDRAW_PENDING; |
| 438 | } |
| 439 | } else if (eventPtr->type == ConfigureNotify) { |
| 440 | KeepInWindow(squarePtr); |
| 441 | if (!(squarePtr->flags & REDRAW_PENDING)) { |
| 442 | Tk_DoWhenIdle(DisplaySquare, (ClientData) squarePtr); |
| 443 | squarePtr->flags |= REDRAW_PENDING; |
| 444 | } |
| 445 | } else if (eventPtr->type == DestroyNotify) { |
| 446 | Tcl_DeleteCommand(squarePtr->interp, Tk_PathName(squarePtr->tkwin)); |
| 447 | squarePtr->tkwin = NULL; |
| 448 | if (squarePtr->flags & REDRAW_PENDING) { |
| 449 | Tk_CancelIdleCall(DisplaySquare, (ClientData) squarePtr); |
| 450 | } |
| 451 | Tk_EventuallyFree((ClientData) squarePtr, DestroySquare); |
| 452 | } |
| 453 | } |
| 454 | \f |
| 455 | /* |
| 456 | *---------------------------------------------------------------------- |
| 457 | * |
| 458 | * DestroySquare -- |
| 459 | * |
| 460 | * This procedure is invoked by Tk_EventuallyFree or Tk_Release |
| 461 | * to clean up the internal structure of a square at a safe time |
| 462 | * (when no-one is using it anymore). |
| 463 | * |
| 464 | * Results: |
| 465 | * None. |
| 466 | * |
| 467 | * Side effects: |
| 468 | * Everything associated with the square is freed up. |
| 469 | * |
| 470 | *---------------------------------------------------------------------- |
| 471 | */ |
| 472 | |
| 473 | static void |
| 474 | DestroySquare(clientData) |
| 475 | ClientData clientData; /* Info about square widget. */ |
| 476 | { |
| 477 | register Square *squarePtr = (Square *) clientData; |
| 478 | |
| 479 | if (squarePtr->bgBorder != NULL) { |
| 480 | Tk_Free3DBorder(squarePtr->bgBorder); |
| 481 | } |
| 482 | if (squarePtr->fgBorder != NULL) { |
| 483 | Tk_Free3DBorder(squarePtr->fgBorder); |
| 484 | } |
| 485 | ckfree((char *) squarePtr); |
| 486 | } |
| 487 | \f |
| 488 | /* |
| 489 | *---------------------------------------------------------------------- |
| 490 | * |
| 491 | * KeepInWindow -- |
| 492 | * |
| 493 | * Adjust the position of the square if necessary to keep it in |
| 494 | * the widget's window. |
| 495 | * |
| 496 | * Results: |
| 497 | * None. |
| 498 | * |
| 499 | * Side effects: |
| 500 | * The x and y position of the square are adjusted if necessary |
| 501 | * to keep the square in the window. |
| 502 | * |
| 503 | *---------------------------------------------------------------------- |
| 504 | */ |
| 505 | |
| 506 | static void |
| 507 | KeepInWindow(squarePtr) |
| 508 | register Square *squarePtr; /* Pointer to widget record. */ |
| 509 | { |
| 510 | int i, bd; |
| 511 | |
| 512 | if (squarePtr->relief == TK_RELIEF_FLAT) { |
| 513 | bd = 0; |
| 514 | } else { |
| 515 | bd = squarePtr->borderWidth; |
| 516 | } |
| 517 | i = (Tk_Width(squarePtr->tkwin) - bd) - (squarePtr->x + squarePtr->size); |
| 518 | if (i < 0) { |
| 519 | squarePtr->x += i; |
| 520 | } |
| 521 | i = (Tk_Height(squarePtr->tkwin) - bd) - (squarePtr->y + squarePtr->size); |
| 522 | if (i < 0) { |
| 523 | squarePtr->y += i; |
| 524 | } |
| 525 | if (squarePtr->x < bd) { |
| 526 | squarePtr->x = bd; |
| 527 | } |
| 528 | if (squarePtr->y < bd) { |
| 529 | squarePtr->y = bd; |
| 530 | } |
| 531 | } |