1/* $Id$ 2 * Copyright (c) 2003, Joe English 3 * 4 * label, button, checkbutton, radiobutton, and menubutton widgets. 5 */ 6 7#include <string.h> 8#include <tk.h> 9#include "ttkTheme.h" 10#include "ttkWidget.h" 11 12/* Bit fields for OptionSpec mask field: 13 */ 14#define STATE_CHANGED (0x100) /* -state option changed */ 15#define DEFAULTSTATE_CHANGED (0x200) /* -default option changed */ 16 17/*------------------------------------------------------------------------ 18 * +++ Base resources for labels, buttons, checkbuttons, etc: 19 */ 20typedef struct 21{ 22 /* 23 * Text element resources: 24 */ 25 Tcl_Obj *textObj; 26 Tcl_Obj *textVariableObj; 27 Tcl_Obj *underlineObj; 28 Tcl_Obj *widthObj; 29 30 Ttk_TraceHandle *textVariableTrace; 31 Ttk_ImageSpec *imageSpec; 32 33 /* 34 * Image element resources: 35 */ 36 Tcl_Obj *imageObj; 37 38 /* 39 * Compound label/image resources: 40 */ 41 Tcl_Obj *compoundObj; 42 Tcl_Obj *paddingObj; 43 44 /* 45 * Compatibility/legacy options: 46 */ 47 Tcl_Obj *stateObj; 48 49} BasePart; 50 51typedef struct 52{ 53 WidgetCore core; 54 BasePart base; 55} Base; 56 57static Tk_OptionSpec BaseOptionSpecs[] = 58{ 59 {TK_OPTION_STRING, "-text", "text", "Text", "", 60 Tk_Offset(Base,base.textObj), -1, 61 0,0,GEOMETRY_CHANGED }, 62 {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable", "", 63 Tk_Offset(Base,base.textVariableObj), -1, 64 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 65 {TK_OPTION_INT, "-underline", "underline", "Underline", 66 "-1", Tk_Offset(Base,base.underlineObj), -1, 67 0,0,0 }, 68 /* SB: OPTION_INT, see <<NOTE-NULLOPTIONS>> */ 69 {TK_OPTION_STRING, "-width", "width", "Width", 70 NULL, Tk_Offset(Base,base.widthObj), -1, 71 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 72 73 /* 74 * Image options 75 */ 76 {TK_OPTION_STRING, "-image", "image", "Image", NULL/*default*/, 77 Tk_Offset(Base,base.imageObj), -1, 78 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 79 80 /* 81 * Compound base/image options 82 */ 83 {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound", 84 "none", Tk_Offset(Base,base.compoundObj), -1, 85 0,(ClientData)ttkCompoundStrings,GEOMETRY_CHANGED }, 86 {TK_OPTION_STRING, "-padding", "padding", "Pad", 87 NULL, Tk_Offset(Base,base.paddingObj), -1, 88 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED}, 89 90 /* 91 * Compatibility/legacy options 92 */ 93 {TK_OPTION_STRING, "-state", "state", "State", 94 "normal", Tk_Offset(Base,base.stateObj), -1, 95 0,0,STATE_CHANGED }, 96 97 WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs) 98}; 99 100/* 101 * Variable trace procedure for -textvariable option: 102 */ 103static void TextVariableChanged(void *clientData, const char *value) 104{ 105 Base *basePtr = clientData; 106 Tcl_Obj *newText; 107 108 if (WidgetDestroyed(&basePtr->core)) { 109 return; 110 } 111 112 newText = value ? Tcl_NewStringObj(value, -1) : Tcl_NewStringObj("", 0); 113 114 Tcl_IncrRefCount(newText); 115 Tcl_DecrRefCount(basePtr->base.textObj); 116 basePtr->base.textObj = newText; 117 118 TtkResizeWidget(&basePtr->core); 119} 120 121static void 122BaseInitialize(Tcl_Interp *interp, void *recordPtr) 123{ 124 Base *basePtr = recordPtr; 125 basePtr->base.textVariableTrace = 0; 126 basePtr->base.imageSpec = NULL; 127} 128 129static void 130BaseCleanup(void *recordPtr) 131{ 132 Base *basePtr = recordPtr; 133 if (basePtr->base.textVariableTrace) 134 Ttk_UntraceVariable(basePtr->base.textVariableTrace); 135 if (basePtr->base.imageSpec) 136 TtkFreeImageSpec(basePtr->base.imageSpec); 137} 138 139static int BaseConfigure(Tcl_Interp *interp, void *recordPtr, int mask) 140{ 141 Base *basePtr = recordPtr; 142 Tcl_Obj *textVarName = basePtr->base.textVariableObj; 143 Ttk_TraceHandle *vt = 0; 144 Ttk_ImageSpec *imageSpec = NULL; 145 146 if (textVarName != NULL && *Tcl_GetString(textVarName) != '\0') { 147 vt = Ttk_TraceVariable(interp,textVarName,TextVariableChanged,basePtr); 148 if (!vt) return TCL_ERROR; 149 } 150 151 if (basePtr->base.imageObj) { 152 imageSpec = TtkGetImageSpec( 153 interp, basePtr->core.tkwin, basePtr->base.imageObj); 154 if (!imageSpec) { 155 goto error; 156 } 157 } 158 159 if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) { 160error: 161 if (imageSpec) TtkFreeImageSpec(imageSpec); 162 if (vt) Ttk_UntraceVariable(vt); 163 return TCL_ERROR; 164 } 165 166 if (basePtr->base.textVariableTrace) { 167 Ttk_UntraceVariable(basePtr->base.textVariableTrace); 168 } 169 basePtr->base.textVariableTrace = vt; 170 171 if (basePtr->base.imageSpec) { 172 TtkFreeImageSpec(basePtr->base.imageSpec); 173 } 174 basePtr->base.imageSpec = imageSpec; 175 176 if (mask & STATE_CHANGED) { 177 TtkCheckStateOption(&basePtr->core, basePtr->base.stateObj); 178 } 179 180 return TCL_OK; 181} 182 183static int 184BasePostConfigure(Tcl_Interp *interp, void *recordPtr, int mask) 185{ 186 Base *basePtr = recordPtr; 187 int status = TCL_OK; 188 189 if (basePtr->base.textVariableTrace) { 190 status = Ttk_FireTrace(basePtr->base.textVariableTrace); 191 } 192 193 return status; 194} 195 196/*------------------------------------------------------------------------ 197 * +++ Label widget. 198 * Just a base widget that adds a few appearance-related options 199 */ 200 201typedef struct 202{ 203 Tcl_Obj *backgroundObj; 204 Tcl_Obj *foregroundObj; 205 Tcl_Obj *fontObj; 206 Tcl_Obj *borderWidthObj; 207 Tcl_Obj *reliefObj; 208 Tcl_Obj *anchorObj; 209 Tcl_Obj *justifyObj; 210 Tcl_Obj *wrapLengthObj; 211} LabelPart; 212 213typedef struct 214{ 215 WidgetCore core; 216 BasePart base; 217 LabelPart label; 218} Label; 219 220static Tk_OptionSpec LabelOptionSpecs[] = 221{ 222 {TK_OPTION_BORDER, "-background", "frameColor", "FrameColor", 223 NULL, Tk_Offset(Label,label.backgroundObj), -1, 224 TK_OPTION_NULL_OK,0,0 }, 225 {TK_OPTION_COLOR, "-foreground", "textColor", "TextColor", 226 NULL, Tk_Offset(Label,label.foregroundObj), -1, 227 TK_OPTION_NULL_OK,0,0 }, 228 {TK_OPTION_FONT, "-font", "font", "Font", 229 NULL, Tk_Offset(Label,label.fontObj), -1, 230 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 231 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 232 NULL, Tk_Offset(Label,label.borderWidthObj), -1, 233 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 234 {TK_OPTION_RELIEF, "-relief", "relief", "Relief", 235 NULL, Tk_Offset(Label,label.reliefObj), -1, 236 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 237 {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", 238 NULL, Tk_Offset(Label,label.anchorObj), -1, 239 TK_OPTION_NULL_OK, 0, GEOMETRY_CHANGED}, 240 {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", 241 NULL, Tk_Offset(Label, label.justifyObj), -1, 242 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 243 {TK_OPTION_PIXELS, "-wraplength", "wrapLength", "WrapLength", 244 NULL, Tk_Offset(Label, label.wrapLengthObj), -1, 245 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED /*SB: SIZE_CHANGED*/ }, 246 247 WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) 248}; 249 250static const Ttk_Ensemble LabelCommands[] = { 251 { "configure", TtkWidgetConfigureCommand,0 }, 252 { "cget", TtkWidgetCgetCommand,0 }, 253 { "instate", TtkWidgetInstateCommand,0 }, 254 { "state", TtkWidgetStateCommand,0 }, 255 { "identify", TtkWidgetIdentifyCommand,0 }, 256 { 0,0,0 } 257}; 258 259static WidgetSpec LabelWidgetSpec = 260{ 261 "TLabel", /* className */ 262 sizeof(Label), /* recordSize */ 263 LabelOptionSpecs, /* optionSpecs */ 264 LabelCommands, /* subcommands */ 265 BaseInitialize, /* initializeProc */ 266 BaseCleanup, /* cleanupProc */ 267 BaseConfigure, /* configureProc */ 268 BasePostConfigure, /* postConfigureProc */ 269 TtkWidgetGetLayout, /* getLayoutProc */ 270 TtkWidgetSize, /* sizeProc */ 271 TtkWidgetDoLayout, /* layoutProc */ 272 TtkWidgetDisplay /* displayProc */ 273}; 274 275TTK_BEGIN_LAYOUT(LabelLayout) 276 TTK_GROUP("Label.border", TTK_FILL_BOTH|TTK_BORDER, 277 TTK_GROUP("Label.padding", TTK_FILL_BOTH|TTK_BORDER, 278 TTK_NODE("Label.label", TTK_FILL_BOTH))) 279TTK_END_LAYOUT 280 281/*------------------------------------------------------------------------ 282 * +++ Button widget. 283 * Adds a new subcommand "invoke", and options "-command" and "-default" 284 */ 285 286typedef struct 287{ 288 Tcl_Obj *commandObj; 289 Tcl_Obj *defaultStateObj; 290} ButtonPart; 291 292typedef struct 293{ 294 WidgetCore core; 295 BasePart base; 296 ButtonPart button; 297} Button; 298 299/* 300 * Option specifications: 301 */ 302static Tk_OptionSpec ButtonOptionSpecs[] = 303{ 304 WIDGET_TAKES_FOCUS, 305 306 {TK_OPTION_STRING, "-command", "command", "Command", 307 "", Tk_Offset(Button, button.commandObj), -1, 0,0,0}, 308 {TK_OPTION_STRING_TABLE, "-default", "default", "Default", 309 "normal", Tk_Offset(Button, button.defaultStateObj), -1, 310 0, (ClientData) ttkDefaultStrings, DEFAULTSTATE_CHANGED}, 311 312 WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) 313}; 314 315static int ButtonConfigure(Tcl_Interp *interp, void *recordPtr, int mask) 316{ 317 Button *buttonPtr = recordPtr; 318 319 if (BaseConfigure(interp, recordPtr, mask) != TCL_OK) { 320 return TCL_ERROR; 321 } 322 323 /* Handle "-default" option: 324 */ 325 if (mask & DEFAULTSTATE_CHANGED) { 326 int defaultState = TTK_BUTTON_DEFAULT_DISABLED; 327 Ttk_GetButtonDefaultStateFromObj( 328 NULL, buttonPtr->button.defaultStateObj, &defaultState); 329 if (defaultState == TTK_BUTTON_DEFAULT_ACTIVE) { 330 TtkWidgetChangeState(&buttonPtr->core, TTK_STATE_ALTERNATE, 0); 331 } else { 332 TtkWidgetChangeState(&buttonPtr->core, 0, TTK_STATE_ALTERNATE); 333 } 334 } 335 return TCL_OK; 336} 337 338/* $button invoke -- 339 * Evaluate the button's -command. 340 */ 341static int 342ButtonInvokeCommand( 343 void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 344{ 345 Button *buttonPtr = recordPtr; 346 if (objc > 2) { 347 Tcl_WrongNumArgs(interp, 1, objv, "invoke"); 348 return TCL_ERROR; 349 } 350 if (buttonPtr->core.state & TTK_STATE_DISABLED) { 351 return TCL_OK; 352 } 353 return Tcl_EvalObjEx(interp, buttonPtr->button.commandObj, TCL_EVAL_GLOBAL); 354} 355 356static const Ttk_Ensemble ButtonCommands[] = { 357 { "configure", TtkWidgetConfigureCommand,0 }, 358 { "cget", TtkWidgetCgetCommand,0 }, 359 { "invoke", ButtonInvokeCommand,0 }, 360 { "instate", TtkWidgetInstateCommand,0 }, 361 { "state", TtkWidgetStateCommand,0 }, 362 { "identify", TtkWidgetIdentifyCommand,0 }, 363 { 0,0,0 } 364}; 365 366static WidgetSpec ButtonWidgetSpec = 367{ 368 "TButton", /* className */ 369 sizeof(Button), /* recordSize */ 370 ButtonOptionSpecs, /* optionSpecs */ 371 ButtonCommands, /* subcommands */ 372 BaseInitialize, /* initializeProc */ 373 BaseCleanup, /* cleanupProc */ 374 ButtonConfigure, /* configureProc */ 375 BasePostConfigure, /* postConfigureProc */ 376 TtkWidgetGetLayout, /* getLayoutProc */ 377 TtkWidgetSize, /* sizeProc */ 378 TtkWidgetDoLayout, /* layoutProc */ 379 TtkWidgetDisplay /* displayProc */ 380}; 381 382TTK_BEGIN_LAYOUT(ButtonLayout) 383 TTK_GROUP("Button.border", TTK_FILL_BOTH|TTK_BORDER, 384 TTK_GROUP("Button.focus", TTK_FILL_BOTH, 385 TTK_GROUP("Button.padding", TTK_FILL_BOTH, 386 TTK_NODE("Button.label", TTK_FILL_BOTH)))) 387TTK_END_LAYOUT 388 389/*------------------------------------------------------------------------ 390 * +++ Checkbutton widget. 391 */ 392typedef struct 393{ 394 Tcl_Obj *variableObj; 395 Tcl_Obj *onValueObj; 396 Tcl_Obj *offValueObj; 397 Tcl_Obj *commandObj; 398 399 Ttk_TraceHandle *variableTrace; 400 401} CheckbuttonPart; 402 403typedef struct 404{ 405 WidgetCore core; 406 BasePart base; 407 CheckbuttonPart checkbutton; 408} Checkbutton; 409 410/* 411 * Option specifications: 412 */ 413static Tk_OptionSpec CheckbuttonOptionSpecs[] = 414{ 415 WIDGET_TAKES_FOCUS, 416 417 {TK_OPTION_STRING, "-variable", "variable", "Variable", 418 "", Tk_Offset(Checkbutton, checkbutton.variableObj), -1, 419 TK_OPTION_DONT_SET_DEFAULT,0,0}, 420 {TK_OPTION_STRING, "-onvalue", "onValue", "OnValue", 421 "1", Tk_Offset(Checkbutton, checkbutton.onValueObj), -1, 422 0,0,0}, 423 {TK_OPTION_STRING, "-offvalue", "offValue", "OffValue", 424 "0", Tk_Offset(Checkbutton, checkbutton.offValueObj), -1, 425 0,0,0}, 426 {TK_OPTION_STRING, "-command", "command", "Command", 427 "", Tk_Offset(Checkbutton, checkbutton.commandObj), -1, 428 0,0,0}, 429 430 WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) 431}; 432 433/* 434 * Variable trace procedure for checkbutton -variable option 435 */ 436static void CheckbuttonVariableChanged(void *clientData, const char *value) 437{ 438 Checkbutton *checkPtr = clientData; 439 440 if (WidgetDestroyed(&checkPtr->core)) { 441 return; 442 } 443 444 if (!value) { 445 TtkWidgetChangeState(&checkPtr->core, TTK_STATE_ALTERNATE, 0); 446 return; 447 } 448 /* else */ 449 TtkWidgetChangeState(&checkPtr->core, 0, TTK_STATE_ALTERNATE); 450 if (!strcmp(value, Tcl_GetString(checkPtr->checkbutton.onValueObj))) { 451 TtkWidgetChangeState(&checkPtr->core, TTK_STATE_SELECTED, 0); 452 } else { 453 TtkWidgetChangeState(&checkPtr->core, 0, TTK_STATE_SELECTED); 454 } 455} 456 457static void 458CheckbuttonInitialize(Tcl_Interp *interp, void *recordPtr) 459{ 460 Checkbutton *checkPtr = recordPtr; 461 Tcl_Obj *variableObj; 462 463 /* default -variable is the widget name: 464 */ 465 variableObj = Tcl_NewStringObj(Tk_PathName(checkPtr->core.tkwin), -1); 466 Tcl_IncrRefCount(variableObj); 467 checkPtr->checkbutton.variableObj = variableObj; 468 BaseInitialize(interp, recordPtr); 469} 470 471static void 472CheckbuttonCleanup(void *recordPtr) 473{ 474 Checkbutton *checkPtr = recordPtr; 475 Ttk_UntraceVariable(checkPtr->checkbutton.variableTrace); 476 checkPtr->checkbutton.variableTrace = 0; 477 BaseCleanup(recordPtr); 478} 479 480static int 481CheckbuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask) 482{ 483 Checkbutton *checkPtr = recordPtr; 484 Ttk_TraceHandle *vt = Ttk_TraceVariable( 485 interp, checkPtr->checkbutton.variableObj, 486 CheckbuttonVariableChanged, checkPtr); 487 488 if (!vt) { 489 return TCL_ERROR; 490 } 491 492 if (BaseConfigure(interp, recordPtr, mask) != TCL_OK){ 493 Ttk_UntraceVariable(vt); 494 return TCL_ERROR; 495 } 496 497 Ttk_UntraceVariable(checkPtr->checkbutton.variableTrace); 498 checkPtr->checkbutton.variableTrace = vt; 499 500 return TCL_OK; 501} 502 503static int 504CheckbuttonPostConfigure(Tcl_Interp *interp, void *recordPtr, int mask) 505{ 506 Checkbutton *checkPtr = recordPtr; 507 int status = TCL_OK; 508 509 if (checkPtr->checkbutton.variableTrace) 510 status = Ttk_FireTrace(checkPtr->checkbutton.variableTrace); 511 if (status == TCL_OK && !WidgetDestroyed(&checkPtr->core)) 512 status = BasePostConfigure(interp, recordPtr, mask); 513 return status; 514} 515 516/* 517 * Checkbutton 'invoke' subcommand: 518 * Toggles the checkbutton state. 519 */ 520static int 521CheckbuttonInvokeCommand( 522 void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 523{ 524 Checkbutton *checkPtr = recordPtr; 525 WidgetCore *corePtr = &checkPtr->core; 526 Tcl_Obj *newValue; 527 528 if (objc > 2) { 529 Tcl_WrongNumArgs(interp, 1, objv, "invoke"); 530 return TCL_ERROR; 531 } 532 if (corePtr->state & TTK_STATE_DISABLED) 533 return TCL_OK; 534 535 /* 536 * Toggle the selected state. 537 */ 538 if (corePtr->state & TTK_STATE_SELECTED) 539 newValue = checkPtr->checkbutton.offValueObj; 540 else 541 newValue = checkPtr->checkbutton.onValueObj; 542 543 if (Tcl_ObjSetVar2(interp, 544 checkPtr->checkbutton.variableObj, NULL, newValue, 545 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) 546 == NULL) 547 return TCL_ERROR; 548 549 if (WidgetDestroyed(corePtr)) 550 return TCL_ERROR; 551 552 return Tcl_EvalObjEx(interp, 553 checkPtr->checkbutton.commandObj, TCL_EVAL_GLOBAL); 554} 555 556static const Ttk_Ensemble CheckbuttonCommands[] = { 557 { "configure", TtkWidgetConfigureCommand,0 }, 558 { "cget", TtkWidgetCgetCommand,0 }, 559 { "invoke", CheckbuttonInvokeCommand,0 }, 560 { "instate", TtkWidgetInstateCommand,0 }, 561 { "state", TtkWidgetStateCommand,0 }, 562 { "identify", TtkWidgetIdentifyCommand,0 }, 563 /* MISSING: select, deselect, toggle */ 564 { 0,0,0 } 565}; 566 567static WidgetSpec CheckbuttonWidgetSpec = 568{ 569 "TCheckbutton", /* className */ 570 sizeof(Checkbutton), /* recordSize */ 571 CheckbuttonOptionSpecs, /* optionSpecs */ 572 CheckbuttonCommands, /* subcommands */ 573 CheckbuttonInitialize, /* initializeProc */ 574 CheckbuttonCleanup, /* cleanupProc */ 575 CheckbuttonConfigure, /* configureProc */ 576 CheckbuttonPostConfigure, /* postConfigureProc */ 577 TtkWidgetGetLayout, /* getLayoutProc */ 578 TtkWidgetSize, /* sizeProc */ 579 TtkWidgetDoLayout, /* layoutProc */ 580 TtkWidgetDisplay /* displayProc */ 581}; 582 583TTK_BEGIN_LAYOUT(CheckbuttonLayout) 584 TTK_GROUP("Checkbutton.padding", TTK_FILL_BOTH, 585 TTK_NODE("Checkbutton.indicator", TTK_PACK_LEFT) 586 TTK_GROUP("Checkbutton.focus", TTK_PACK_LEFT | TTK_STICK_W, 587 TTK_NODE("Checkbutton.label", TTK_FILL_BOTH))) 588TTK_END_LAYOUT 589 590/*------------------------------------------------------------------------ 591 * +++ Radiobutton widget. 592 */ 593 594typedef struct 595{ 596 Tcl_Obj *variableObj; 597 Tcl_Obj *valueObj; 598 Tcl_Obj *commandObj; 599 600 Ttk_TraceHandle *variableTrace; 601 602} RadiobuttonPart; 603 604typedef struct 605{ 606 WidgetCore core; 607 BasePart base; 608 RadiobuttonPart radiobutton; 609} Radiobutton; 610 611/* 612 * Option specifications: 613 */ 614static Tk_OptionSpec RadiobuttonOptionSpecs[] = 615{ 616 WIDGET_TAKES_FOCUS, 617 618 {TK_OPTION_STRING, "-variable", "variable", "Variable", 619 "::selectedButton", Tk_Offset(Radiobutton, radiobutton.variableObj),-1, 620 0,0,0}, 621 {TK_OPTION_STRING, "-value", "Value", "Value", 622 "1", Tk_Offset(Radiobutton, radiobutton.valueObj), -1, 623 0,0,0}, 624 {TK_OPTION_STRING, "-command", "command", "Command", 625 "", Tk_Offset(Radiobutton, radiobutton.commandObj), -1, 626 0,0,0}, 627 628 WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) 629}; 630 631/* 632 * Variable trace procedure for radiobuttons. 633 */ 634static void 635RadiobuttonVariableChanged(void *clientData, const char *value) 636{ 637 Radiobutton *radioPtr = clientData; 638 639 if (WidgetDestroyed(&radioPtr->core)) { 640 return; 641 } 642 643 if (!value) { 644 TtkWidgetChangeState(&radioPtr->core, TTK_STATE_ALTERNATE, 0); 645 return; 646 } 647 /* else */ 648 TtkWidgetChangeState(&radioPtr->core, 0, TTK_STATE_ALTERNATE); 649 if (!strcmp(value, Tcl_GetString(radioPtr->radiobutton.valueObj))) { 650 TtkWidgetChangeState(&radioPtr->core, TTK_STATE_SELECTED, 0); 651 } else { 652 TtkWidgetChangeState(&radioPtr->core, 0, TTK_STATE_SELECTED); 653 } 654} 655 656static void 657RadiobuttonCleanup(void *recordPtr) 658{ 659 Radiobutton *radioPtr = recordPtr; 660 Ttk_UntraceVariable(radioPtr->radiobutton.variableTrace); 661 radioPtr->radiobutton.variableTrace = 0; 662 BaseCleanup(recordPtr); 663} 664 665static int 666RadiobuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask) 667{ 668 Radiobutton *radioPtr = recordPtr; 669 Ttk_TraceHandle *vt = Ttk_TraceVariable( 670 interp, radioPtr->radiobutton.variableObj, 671 RadiobuttonVariableChanged, radioPtr); 672 673 if (!vt) { 674 return TCL_ERROR; 675 } 676 677 if (BaseConfigure(interp, recordPtr, mask) != TCL_OK) { 678 Ttk_UntraceVariable(vt); 679 return TCL_ERROR; 680 } 681 682 Ttk_UntraceVariable(radioPtr->radiobutton.variableTrace); 683 radioPtr->radiobutton.variableTrace = vt; 684 685 return TCL_OK; 686} 687 688static int 689RadiobuttonPostConfigure(Tcl_Interp *interp, void *recordPtr, int mask) 690{ 691 Radiobutton *radioPtr = recordPtr; 692 int status = TCL_OK; 693 694 if (radioPtr->radiobutton.variableTrace) 695 status = Ttk_FireTrace(radioPtr->radiobutton.variableTrace); 696 if (status == TCL_OK && !WidgetDestroyed(&radioPtr->core)) 697 status = BasePostConfigure(interp, recordPtr, mask); 698 return status; 699} 700 701/* 702 * Radiobutton 'invoke' subcommand: 703 * Sets the radiobutton -variable to the -value, evaluates the -command. 704 */ 705static int 706RadiobuttonInvokeCommand( 707 void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 708{ 709 Radiobutton *radioPtr = recordPtr; 710 WidgetCore *corePtr = &radioPtr->core; 711 712 if (objc > 2) { 713 Tcl_WrongNumArgs(interp, 1, objv, "invoke"); 714 return TCL_ERROR; 715 } 716 if (corePtr->state & TTK_STATE_DISABLED) 717 return TCL_OK; 718 719 if (Tcl_ObjSetVar2(interp, 720 radioPtr->radiobutton.variableObj, NULL, 721 radioPtr->radiobutton.valueObj, 722 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) 723 == NULL) 724 return TCL_ERROR; 725 726 if (WidgetDestroyed(corePtr)) 727 return TCL_ERROR; 728 729 return Tcl_EvalObjEx(interp, 730 radioPtr->radiobutton.commandObj, TCL_EVAL_GLOBAL); 731} 732 733static const Ttk_Ensemble RadiobuttonCommands[] = { 734 { "configure", TtkWidgetConfigureCommand,0 }, 735 { "cget", TtkWidgetCgetCommand,0 }, 736 { "invoke", RadiobuttonInvokeCommand,0 }, 737 { "instate", TtkWidgetInstateCommand,0 }, 738 { "state", TtkWidgetStateCommand,0 }, 739 { "identify", TtkWidgetIdentifyCommand,0 }, 740 /* MISSING: select, deselect */ 741 { 0,0,0 } 742}; 743 744static WidgetSpec RadiobuttonWidgetSpec = 745{ 746 "TRadiobutton", /* className */ 747 sizeof(Radiobutton), /* recordSize */ 748 RadiobuttonOptionSpecs, /* optionSpecs */ 749 RadiobuttonCommands, /* subcommands */ 750 BaseInitialize, /* initializeProc */ 751 RadiobuttonCleanup, /* cleanupProc */ 752 RadiobuttonConfigure, /* configureProc */ 753 RadiobuttonPostConfigure, /* postConfigureProc */ 754 TtkWidgetGetLayout, /* getLayoutProc */ 755 TtkWidgetSize, /* sizeProc */ 756 TtkWidgetDoLayout, /* layoutProc */ 757 TtkWidgetDisplay /* displayProc */ 758}; 759 760TTK_BEGIN_LAYOUT(RadiobuttonLayout) 761 TTK_GROUP("Radiobutton.padding", TTK_FILL_BOTH, 762 TTK_NODE("Radiobutton.indicator", TTK_PACK_LEFT) 763 TTK_GROUP("Radiobutton.focus", TTK_PACK_LEFT, 764 TTK_NODE("Radiobutton.label", TTK_FILL_BOTH))) 765TTK_END_LAYOUT 766 767/*------------------------------------------------------------------------ 768 * +++ Menubutton widget. 769 */ 770 771typedef struct 772{ 773 Tcl_Obj *menuObj; 774 Tcl_Obj *directionObj; 775} MenubuttonPart; 776 777typedef struct 778{ 779 WidgetCore core; 780 BasePart base; 781 MenubuttonPart menubutton; 782} Menubutton; 783 784/* 785 * Option specifications: 786 */ 787static const char *const directionStrings[] = { 788 "above", "below", "left", "right", "flush", NULL 789}; 790static Tk_OptionSpec MenubuttonOptionSpecs[] = 791{ 792 WIDGET_TAKES_FOCUS, 793 794 {TK_OPTION_STRING, "-menu", "menu", "Menu", 795 "", Tk_Offset(Menubutton, menubutton.menuObj), -1, 0,0,0}, 796 {TK_OPTION_STRING_TABLE, "-direction", "direction", "Direction", 797 "below", Tk_Offset(Menubutton, menubutton.directionObj), -1, 798 0,(ClientData)directionStrings,GEOMETRY_CHANGED}, 799 800 WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) 801}; 802 803static const Ttk_Ensemble MenubuttonCommands[] = { 804 { "configure", TtkWidgetConfigureCommand,0 }, 805 { "cget", TtkWidgetCgetCommand,0 }, 806 { "instate", TtkWidgetInstateCommand,0 }, 807 { "state", TtkWidgetStateCommand,0 }, 808 { "identify", TtkWidgetIdentifyCommand,0 }, 809 { 0,0,0 } 810}; 811 812static WidgetSpec MenubuttonWidgetSpec = 813{ 814 "TMenubutton", /* className */ 815 sizeof(Menubutton), /* recordSize */ 816 MenubuttonOptionSpecs, /* optionSpecs */ 817 MenubuttonCommands, /* subcommands */ 818 BaseInitialize, /* initializeProc */ 819 BaseCleanup, /* cleanupProc */ 820 BaseConfigure, /* configureProc */ 821 BasePostConfigure, /* postConfigureProc */ 822 TtkWidgetGetLayout, /* getLayoutProc */ 823 TtkWidgetSize, /* sizeProc */ 824 TtkWidgetDoLayout, /* layoutProc */ 825 TtkWidgetDisplay /* displayProc */ 826}; 827 828TTK_BEGIN_LAYOUT(MenubuttonLayout) 829 TTK_GROUP("Menubutton.border", TTK_FILL_BOTH, 830 TTK_GROUP("Menubutton.focus", TTK_FILL_BOTH, 831 TTK_NODE("Menubutton.indicator", TTK_PACK_RIGHT) 832 TTK_GROUP("Menubutton.padding", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_X, 833 TTK_NODE("Menubutton.label", TTK_PACK_LEFT)))) 834TTK_END_LAYOUT 835 836/*------------------------------------------------------------------------ 837 * +++ Initialization. 838 */ 839 840MODULE_SCOPE 841void TtkButton_Init(Tcl_Interp *interp) 842{ 843 Ttk_Theme theme = Ttk_GetDefaultTheme(interp); 844 845 Ttk_RegisterLayout(theme, "TLabel", LabelLayout); 846 Ttk_RegisterLayout(theme, "TButton", ButtonLayout); 847 Ttk_RegisterLayout(theme, "TCheckbutton", CheckbuttonLayout); 848 Ttk_RegisterLayout(theme, "TRadiobutton", RadiobuttonLayout); 849 Ttk_RegisterLayout(theme, "TMenubutton", MenubuttonLayout); 850 851 RegisterWidget(interp, "ttk::label", &LabelWidgetSpec); 852 RegisterWidget(interp, "ttk::button", &ButtonWidgetSpec); 853 RegisterWidget(interp, "ttk::checkbutton", &CheckbuttonWidgetSpec); 854 RegisterWidget(interp, "ttk::radiobutton", &RadiobuttonWidgetSpec); 855 RegisterWidget(interp, "ttk::menubutton", &MenubuttonWidgetSpec); 856} 857