1 /* Copyright (C) 1993, 1992 Nathan Sidwell */ 2 /* RCS $Id: control.c,v 4.15 1993/12/10 11:52:23 nathan Stable $ */ 3 /*{{{ includes*/ 4 #include "xmred.h" 5 #include <X11/StringDefs.h> 6 #include "Icon.h" 7 #include "Drag.h" 8 #include <X11/Xaw/Scrollbar.h> 9 #include <X11/Xaw/Form.h> 10 #include <X11/Xaw/Label.h> 11 #include <math.h> 12 /*}}}*/ 13 #define XtROption "Option" 14 /*{{{ defines*/ 15 /*{{{ control gizmos*/ 16 #define GIZMO_CONTROL 0 17 #define GIZMO_TOTAL 1 18 #define GIZMO_COMMENT 2 19 #define GIZMO_DISPLAY 3 20 #define GIZMO_DISPLAY_TITLE 4 21 #define GIZMO_DISPLAY_BASE 5 22 #define GIZMO_DISPLAY_COMBINED (5 + MODE_COMBINED) 23 #define GIZMO_DISPLAY_SEPARATE (5 + MODE_SEPARATE) 24 #define GIZMO_DISPLAY_RANDOM (5 + MODE_RANDOM) 25 #define GIZMO_CONTROL_OPTIONS 8 26 #define GIZMO_CONTROL_FILLS 9 27 #define GIZMO_CONTROL_COLORS 10 28 #define GIZMO_CONTROL_BUTTONS 11 29 #define GIZMO_OPTION_LABEL 12 30 #define GIZMO_FILL_LABEL 13 31 #define GIZMO_COLOR_LABEL 14 32 #define GIZMO_BUTTON_LABEL 15 33 #define GIZMO_TOTAL_LABEL 16 34 #define GIZMO_TOTAL_BOXES 17 35 #define GIZMO_TOTAL_ICONS 27 36 #define GIZMO_TOTAL_COUNTS 37 37 #define GIZMO_TOTAL_WARNINGS 47 38 #define GIZMO_OPTION_DRAG 57 39 #define GIZMO_BUTTON_DRAG 58 40 #define GIZMO_OPTIONS 59 41 #define GIZMO_FILLS (GIZMO_OPTIONS + OPTIONS) 42 #define GIZMO_COLORS (GIZMO_FILLS + FILLS) 43 #define GIZMO_BUTTONS (GIZMO_COLORS + BACKGROUNDS) 44 #define GIZMOS (GIZMO_BUTTONS + BUTTONS) 45 /*}}}*/ 46 #define VALUE_LOCK 256 47 /*}}}*/ 48 /*{{{ structs*/ 49 /*{{{ typedef struct Icon*/ 50 typedef struct Icon 51 { 52 int gizmo; /* gizmo number */ 53 COORD size; /* size of pixmap */ 54 unsigned sprite; /* sprite source */ 55 unsigned fill; /* background fill pattern */ 56 unsigned color; /* background color */ 57 Pixmap pixmap; /* displayed pixmap */ 58 } ICON; 59 /*}}}*/ 60 /*}}}*/ 61 /*{{{ tables*/ 62 /*{{{ static Arg arg_apple[] =*/ 63 static Arg arg_apple[] = 64 { 65 {MredNcolumns, (XtArgVal)2}, 66 {MredNrows, (XtArgVal)2}, 67 }; 68 /*}}}*/ 69 /*{{{ static Arg arg_notapple[] =*/ 70 static Arg arg_notapple[] = 71 { 72 {MredNcolumns, (XtArgVal)1}, 73 {MredNrows, (XtArgVal)1}, 74 }; 75 /*}}}*/ 76 /*{{{ static Arg arg_nohighlight[] =*/ 77 static Arg arg_nohighlight[] = 78 { 79 {MredNhighlightThickness, (XtArgVal)0}, 80 }; 81 /*}}}*/ 82 /*{{{ static Arg arg_nodrag[] =*/ 83 static Arg arg_nodrag[] = 84 { 85 {MredNdragSensitivity, (XtArgVal)0}, 86 }; 87 /*}}}*/ 88 /*{{{ static Arg arg_font[] =*/ 89 static Arg arg_count[] = 90 { 91 {XtNfont, (XtArgVal)NULL}, 92 }; 93 /*}}}*/ 94 /*{{{ static Arg arg_warning[] =*/ 95 static Arg arg_warning[] = 96 { 97 {XtNbitmap, (XtArgVal)NULL}, 98 }; 99 /*}}}*/ 100 /*{{{ static GIZMO gizmos[GIZMOS] =*/ 101 static GIZMO gizmos[GIZMOS] = 102 { 103 {"controls", -1, &formWidgetClass}, 104 {"totals", -1, &formWidgetClass}, 105 {"comment", -1, &iconWidgetClass, 106 arg_nodrag, XtNumber(arg_nodrag)}, 107 {"mode", -1, &formWidgetClass}, 108 {"label", GIZMO_DISPLAY, &labelWidgetClass}, 109 {"combined", GIZMO_DISPLAY, &iconWidgetClass, 110 arg_nodrag, XtNumber(arg_nodrag)}, 111 {"separate", GIZMO_DISPLAY, &iconWidgetClass, 112 arg_nodrag, XtNumber(arg_nodrag)}, 113 {"random", GIZMO_DISPLAY, &iconWidgetClass, 114 arg_nodrag, XtNumber(arg_nodrag)}, 115 {"options", GIZMO_CONTROL, &formWidgetClass}, 116 {"fills", GIZMO_CONTROL, &formWidgetClass}, 117 {"colors", GIZMO_CONTROL, &formWidgetClass}, 118 {"buttons", GIZMO_CONTROL, &formWidgetClass}, 119 {"label", GIZMO_CONTROL_OPTIONS, &labelWidgetClass}, 120 {"label", GIZMO_CONTROL_BUTTONS, &labelWidgetClass}, 121 {"label", GIZMO_CONTROL_FILLS, &labelWidgetClass}, 122 {"label", GIZMO_CONTROL_COLORS, &labelWidgetClass}, 123 {"label", GIZMO_TOTAL, &labelWidgetClass}, 124 {"total0", GIZMO_TOTAL, &formWidgetClass}, 125 {"total1", GIZMO_TOTAL, &formWidgetClass}, 126 {"total2", GIZMO_TOTAL, &formWidgetClass}, 127 {"total3", GIZMO_TOTAL, &formWidgetClass}, 128 {"total4", GIZMO_TOTAL, &formWidgetClass}, 129 {"total5", GIZMO_TOTAL, &formWidgetClass}, 130 {"total6", GIZMO_TOTAL, &formWidgetClass}, 131 {"total7", GIZMO_TOTAL, &formWidgetClass}, 132 {"total8", GIZMO_TOTAL, &formWidgetClass}, 133 {"total9", GIZMO_TOTAL, &formWidgetClass}, 134 {"icon", GIZMO_TOTAL_BOXES + 0, &iconWidgetClass, 135 arg_nohighlight, XtNumber(arg_nohighlight)}, 136 {"icon", GIZMO_TOTAL_BOXES + 1, &iconWidgetClass, 137 arg_nohighlight, XtNumber(arg_nohighlight)}, 138 {"icon", GIZMO_TOTAL_BOXES + 2, &iconWidgetClass, 139 arg_nohighlight, XtNumber(arg_nohighlight)}, 140 {"icon", GIZMO_TOTAL_BOXES + 3, &iconWidgetClass, 141 arg_nohighlight, XtNumber(arg_nohighlight)}, 142 {"icon", GIZMO_TOTAL_BOXES + 4, &iconWidgetClass, 143 arg_nohighlight, XtNumber(arg_nohighlight)}, 144 {"icon", GIZMO_TOTAL_BOXES + 5, &iconWidgetClass, 145 arg_nohighlight, XtNumber(arg_nohighlight)}, 146 {"icon", GIZMO_TOTAL_BOXES + 6, &iconWidgetClass, 147 arg_nohighlight, XtNumber(arg_nohighlight)}, 148 {"icon", GIZMO_TOTAL_BOXES + 7, &iconWidgetClass, 149 arg_nohighlight, XtNumber(arg_nohighlight)}, 150 {"icon", GIZMO_TOTAL_BOXES + 8, &iconWidgetClass, 151 arg_nohighlight, XtNumber(arg_nohighlight)}, 152 {"icon", GIZMO_TOTAL_BOXES + 9, &iconWidgetClass, 153 arg_nohighlight, XtNumber(arg_nohighlight)}, 154 {"count", GIZMO_TOTAL_BOXES + 0, &labelWidgetClass, 155 arg_count, XtNumber(arg_count)}, 156 {"count", GIZMO_TOTAL_BOXES + 1, &labelWidgetClass, 157 arg_count, XtNumber(arg_count)}, 158 {"count", GIZMO_TOTAL_BOXES + 2, &labelWidgetClass, 159 arg_count, XtNumber(arg_count)}, 160 {"count", GIZMO_TOTAL_BOXES + 3, &labelWidgetClass, 161 arg_count, XtNumber(arg_count)}, 162 {"count", GIZMO_TOTAL_BOXES + 4, &labelWidgetClass, 163 arg_count, XtNumber(arg_count)}, 164 {"count", GIZMO_TOTAL_BOXES + 5, &labelWidgetClass, 165 arg_count, XtNumber(arg_count)}, 166 {"count", GIZMO_TOTAL_BOXES + 6, &labelWidgetClass, 167 arg_count, XtNumber(arg_count)}, 168 {"count", GIZMO_TOTAL_BOXES + 7, &labelWidgetClass, 169 arg_count, XtNumber(arg_count)}, 170 {"count", GIZMO_TOTAL_BOXES + 8, &labelWidgetClass, 171 arg_count, XtNumber(arg_count)}, 172 {"count", GIZMO_TOTAL_BOXES + 9, &labelWidgetClass, 173 arg_count, XtNumber(arg_count)}, 174 {"warning", GIZMO_TOTAL_BOXES + 0, &labelWidgetClass, 175 arg_warning, XtNumber(arg_warning)}, 176 {"warning", GIZMO_TOTAL_BOXES + 1, &labelWidgetClass, 177 arg_warning, XtNumber(arg_warning)}, 178 {"warning", GIZMO_TOTAL_BOXES + 2, &labelWidgetClass, 179 arg_warning, XtNumber(arg_warning)}, 180 {"warning", GIZMO_TOTAL_BOXES + 3, &labelWidgetClass, 181 arg_warning, XtNumber(arg_warning)}, 182 {"warning", GIZMO_TOTAL_BOXES + 4, &labelWidgetClass, 183 arg_warning, XtNumber(arg_warning)}, 184 {"scrollbar", GIZMO_TOTAL_BOXES + 5, &scrollbarWidgetClass}, 185 {"warning", GIZMO_TOTAL_BOXES + 6, &labelWidgetClass, 186 arg_warning, XtNumber(arg_warning)}, 187 {"warning", GIZMO_TOTAL_BOXES + 7, &labelWidgetClass, 188 arg_warning, XtNumber(arg_warning)}, 189 {"warning", GIZMO_TOTAL_BOXES + 8, &labelWidgetClass, 190 arg_warning, XtNumber(arg_warning)}, 191 {"warning", GIZMO_TOTAL_BOXES + 9, &labelWidgetClass, 192 arg_warning, XtNumber(arg_warning)}, 193 {"drag", GIZMO_CONTROL_OPTIONS, &dragWidgetClass}, 194 {"drag", GIZMO_CONTROL_BUTTONS, &dragWidgetClass}, 195 {"apple", GIZMO_CONTROL_OPTIONS, &iconWidgetClass, 196 arg_apple, XtNumber(arg_apple)}, 197 {"random", GIZMO_CONTROL_OPTIONS, &iconWidgetClass}, 198 {"cherry", GIZMO_CONTROL_OPTIONS, &iconWidgetClass}, 199 {"path", GIZMO_CONTROL_OPTIONS, &iconWidgetClass}, 200 {"player", GIZMO_CONTROL_OPTIONS, &iconWidgetClass}, 201 {"den", GIZMO_CONTROL_OPTIONS, &iconWidgetClass}, 202 {"fill0", GIZMO_CONTROL_FILLS, &iconWidgetClass, 203 arg_nodrag, XtNumber(arg_nodrag)}, 204 {"fill1", GIZMO_CONTROL_FILLS, &iconWidgetClass, 205 arg_nodrag, XtNumber(arg_nodrag)}, 206 {"fill2", GIZMO_CONTROL_FILLS, &iconWidgetClass, 207 arg_nodrag, XtNumber(arg_nodrag)}, 208 {"fill3", GIZMO_CONTROL_FILLS, &iconWidgetClass, 209 arg_nodrag, XtNumber(arg_nodrag)}, 210 {"color0", GIZMO_CONTROL_COLORS, &iconWidgetClass, 211 arg_nodrag, XtNumber(arg_nodrag)}, 212 {"color1", GIZMO_CONTROL_COLORS, &iconWidgetClass, 213 arg_nodrag, XtNumber(arg_nodrag)}, 214 {"color2", GIZMO_CONTROL_COLORS, &iconWidgetClass, 215 arg_nodrag, XtNumber(arg_nodrag)}, 216 {"button1", GIZMO_CONTROL_BUTTONS, &iconWidgetClass}, 217 {"button2", GIZMO_CONTROL_BUTTONS, &iconWidgetClass}, 218 {"button3", GIZMO_CONTROL_BUTTONS, &iconWidgetClass}, 219 {"button4", GIZMO_CONTROL_BUTTONS, &iconWidgetClass}, 220 {"button5", GIZMO_CONTROL_BUTTONS, &iconWidgetClass}, 221 }; 222 /*}}}*/ 223 /*{{{ static ICON icons[] =*/ 224 static ICON icons[] = 225 { 226 {GIZMO_DISPLAY_COMBINED, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLES + 4}, 227 {GIZMO_DISPLAY_SEPARATE, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLES}, 228 {GIZMO_DISPLAY_RANDOM, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_BIG_APPLE + 1}, 229 {GIZMO_COMMENT, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_ICON_I}, 230 {GIZMO_TOTAL_ICONS + COUNT_APPLES + 0, 231 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLES + 0}, 232 {GIZMO_TOTAL_ICONS + COUNT_APPLES + 1, 233 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLES + 1}, 234 {GIZMO_TOTAL_ICONS + COUNT_APPLES + 2, 235 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLES + 2}, 236 {GIZMO_TOTAL_ICONS + COUNT_APPLES + 3, 237 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLES + 3}, 238 {GIZMO_TOTAL_ICONS + COUNT_CHERRY, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_CHERRY}, 239 {GIZMO_TOTAL_ICONS + COUNT_RANDOM, 240 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_BIG_APPLE}, 241 {GIZMO_TOTAL_ICONS + COUNT_SPACES, 242 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_BIG_APPLE + 1}, 243 {GIZMO_TOTAL_ICONS + COUNT_FALL, 244 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLE_DROP}, 245 {GIZMO_TOTAL_ICONS + COUNT_PLAYER, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_PLAYER}, 246 {GIZMO_TOTAL_ICONS + COUNT_DEN, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_DEN}, 247 {GIZMO_OPTIONS + OPTION_APPLES, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_APPLES}, 248 {GIZMO_OPTIONS + OPTION_RANDOM, 249 {CELL_WIDTH, CELL_HEIGHT}, SPRITE_BIG_APPLE + 1}, 250 {GIZMO_OPTIONS + OPTION_CHERRY, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_CHERRY}, 251 {GIZMO_OPTIONS + OPTION_PATH, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_PATH}, 252 {GIZMO_OPTIONS + OPTION_PLAYER, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_PLAYER}, 253 {GIZMO_OPTIONS + OPTION_DEN, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_DEN}, 254 {GIZMO_FILLS + 0, {CELL_WIDTH, CELL_HEIGHT}, 0, VALUE_LOCK | 0}, 255 {GIZMO_FILLS + 1, {CELL_WIDTH, CELL_HEIGHT}, 0, VALUE_LOCK | 1}, 256 {GIZMO_FILLS + 2, {CELL_WIDTH, CELL_HEIGHT}, 0, VALUE_LOCK | 2}, 257 {GIZMO_FILLS + 3, {CELL_WIDTH, CELL_HEIGHT}, 0, VALUE_LOCK | 3}, 258 {GIZMO_COLORS + 0, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_COLORS + 0, 259 0, VALUE_LOCK | 0}, 260 {GIZMO_COLORS + 1, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_COLORS + 1, 261 0, VALUE_LOCK | 1}, 262 {GIZMO_COLORS + 2, {CELL_WIDTH, CELL_HEIGHT}, SPRITE_COLORS + 2, 263 0, VALUE_LOCK | 2}, 264 {GIZMO_BUTTONS + 0, {CELL_WIDTH, CELL_HEIGHT}, 0}, 265 {GIZMO_BUTTONS + 1, {CELL_WIDTH, CELL_HEIGHT}, 0}, 266 {GIZMO_BUTTONS + 2, {CELL_WIDTH, CELL_HEIGHT}, 0}, 267 {GIZMO_BUTTONS + 3, {CELL_WIDTH, CELL_HEIGHT}, 0}, 268 {GIZMO_BUTTONS + 4, {CELL_WIDTH, CELL_HEIGHT}, 0}, 269 }; 270 /*}}}*/ 271 static char count_text[COUNTS][4]; 272 /*}}}*/ 273 /*{{{ prototypes*/ 274 static VOIDFUNC apple_jump PROTOARG((Widget, XtPointer, XtPointer)); 275 static VOIDFUNC apple_scroll PROTOARG((Widget, XtPointer, XtPointer)); 276 static VOIDFUNC apple_set_thumb PROTOARG((VOIDARG)); 277 static VOIDFUNC bind_button PROTOARG((unsigned, int)); 278 static VOIDFUNC command_button PROTOARG((Widget, XtPointer, XtPointer)); 279 static VOIDFUNC command_color PROTOARG((Widget, XtPointer, XtPointer)); 280 static VOIDFUNC command_fill PROTOARG((Widget, XtPointer, XtPointer)); 281 static VOIDFUNC command_mode PROTOARG((Widget, XtPointer, XtPointer)); 282 static VOIDFUNC command_option PROTOARG((Widget, XtPointer, XtPointer)); 283 static Boolean convert_string2option PROTOARG((Display *, XrmValue *, 284 Cardinal *, XrmValue *, XrmValue *, XtPointer *)); 285 static VOIDFUNC drag_button PROTOARG((Widget, XtPointer, XtPointer)); 286 static VOIDFUNC drag_option PROTOARG((Widget, XtPointer, XtPointer)); 287 static VOIDFUNC garden_comment PROTOARG((Widget, XtPointer, XtPointer)); 288 static VOIDFUNC generate_icon PROTOARG((ICON *)); 289 static ICON *gizmo2icon PROTOARG((GIZMO *)); 290 static unsigned select_apple PROTOARG((unsigned)); 291 static VOIDFUNC set_count_flag PROTOARG((unsigned, unsigned)); 292 static VOIDFUNC set_icon_color PROTOARG((unsigned)); 293 static VOIDFUNC set_icon_fill PROTOARG((unsigned)); 294 /*}}}*/ 295 /*{{{ void adjust_count(count, delta)*/ 296 extern VOIDFUNC adjust_count 297 FUNCARG((count, delta), 298 unsigned count /* which count to alter */ 299 ARGSEP int delta /* amount to alter it by */ 300 ) 301 /* alters a count and updates the display 302 * adds or removes the appropriate warning flag(s) 303 */ 304 { 305 size_t ix; 306 307 assert(count < COUNTS && (delta >= 0 || state.counts[count] >= -delta)); 308 state.counts[count] += delta; 309 for(ix = itoa(count_text[count], state.counts[count], 0); ix != 3; ix++) 310 count_text[count][ix] = ' '; 311 count_text[count][4] = 0; 312 XtVaSetValues(gizmos[GIZMO_TOTAL_COUNTS + count].widget, 313 XtNlabel, (XtArgVal)count_text[count], NULL); 314 if(INRANGE(count, COUNT_APPLES, 4)) 315 set_count_flag(count, state.counts[count] != state.counts[COUNT_RANDOM]); 316 else if(count == COUNT_CHERRY) 317 set_count_flag(COUNT_CHERRY, !state.counts[COUNT_CHERRY]); 318 else if(count == COUNT_DEN) 319 set_count_flag(COUNT_DEN, !state.counts[COUNT_DEN]); 320 else if(count == COUNT_PLAYER) 321 set_count_flag(COUNT_PLAYER, state.counts[COUNT_PLAYER] != 1); 322 else if(count == COUNT_RANDOM) 323 { 324 unsigned ix; 325 326 if(state.edit && delta) 327 { 328 state.edit->board->apples = state.counts[COUNT_RANDOM]; 329 changed_flag |= state.change; 330 } 331 for(ix = 4; ix--;) 332 set_count_flag(COUNT_APPLES + ix, 333 state.counts[COUNT_APPLES + ix] != state.counts[COUNT_RANDOM]); 334 set_count_flag(COUNT_SPACES, 335 state.counts[COUNT_SPACES] < state.counts[COUNT_RANDOM]); 336 } 337 else if(count == COUNT_SPACES) 338 set_count_flag(COUNT_SPACES, 339 state.counts[COUNT_SPACES] < state.counts[COUNT_RANDOM]); 340 return; 341 } 342 /*}}}*/ 343 /*{{{ void apple_jump(widget, client, call)*/ 344 static VOIDFUNC apple_jump 345 FUNCARG((widget, client, call), 346 Widget widget 347 ARGSEP XtPointer client 348 ARGSEP XtPointer call 349 ) 350 /* jump scroll callback for setting number of apples to display 351 * Note this slider is upsidedown. 352 */ 353 { 354 int count; 355 356 count = APPLE_LIMIT - (int)floor(*(float *)call * (float)APPLE_LIMIT); 357 if(count != state.counts[5]) 358 adjust_count(5, count - state.counts[5]); 359 return; 360 } 361 /*}}}*/ 362 /*{{{ void apple_scroll(widget, client, call)*/ 363 static VOIDFUNC apple_scroll 364 FUNCARG((widget, client, call), 365 Widget widget 366 ARGSEP XtPointer client 367 ARGSEP XtPointer call 368 ) 369 /* smooth scroll callback on apple set callback 370 * note that this slider is upsidedown to normal, so 371 * we get slider = 1.0 gives 0 apples and slider = 0.0 gives APPLE_LIMIT 372 */ 373 { 374 int delta; 375 376 delta = (int)call ? (int)call < 0 ? -1 : 1 : 0; 377 if((delta >= 0 || state.counts[5]) && (delta <= 0 || 378 state.counts[5] != APPLE_LIMIT)) 379 adjust_count(5, delta); 380 apple_set_thumb(); 381 return; 382 } 383 /*}}}*/ 384 /*{{{ void apple_set_thumb()*/ 385 static VOIDFUNC apple_set_thumb FUNCARGVOID 386 /* sets the apple selectionn scrollbar to the current 387 * count value 388 */ 389 { 390 XawScrollbarSetThumb(gizmos[GIZMO_TOTAL_WARNINGS + 5].widget, 391 (float)(APPLE_LIMIT - state.counts[5]) / (float)APPLE_LIMIT, -1.0); 392 return; 393 } 394 /*}}}*/ 395 /*{{{ void bind_button(button, option)*/ 396 static VOIDFUNC bind_button 397 FUNCARG((button, option), 398 unsigned button /* button to bind */ 399 ARGSEP int option /* option to bind */ 400 ) 401 /* binds an option to a button 402 */ 403 { 404 GIZMO *gptr; 405 ICON *iptr; 406 407 state.button[button] = option; 408 gptr = &gizmos[GIZMO_BUTTONS + button]; 409 XtSetValues(gptr->widget, option == OPTION_APPLES ? 410 arg_apple : arg_notapple, option == OPTION_APPLES ? 411 XtNumber(arg_apple) : XtNumber(arg_notapple)); 412 iptr = gizmo2icon(gptr); 413 iptr->sprite = option < 0 ? 0 : 414 gizmo2icon(&gizmos[GIZMO_OPTIONS + option])->sprite; 415 generate_icon(iptr); 416 IconRepaint(gptr->widget); 417 return; 418 } 419 /*}}}*/ 420 /*{{{ void command_button(widget, client, call)*/ 421 static VOIDFUNC command_button 422 FUNCARG((widget, client, call), 423 Widget widget 424 ARGSEP XtPointer client 425 ARGSEP XtPointer call 426 ) 427 /* button press callback on a command button widget. 428 * Copy the selected widget's option to the pressed button's widget 429 */ 430 { 431 IconCallback *call_data; 432 int option; 433 434 option = state.button[(unsigned)client]; 435 call_data = (IconCallback *)call; 436 if(option == OPTION_APPLES) 437 select_apple(call_data->selection); 438 else if(call_data->button >= Button1 && call_data->button <= Button5) 439 bind_button(call_data->button - Button1, option); 440 return; 441 } 442 /*}}}*/ 443 /*{{{ void command_color(widget, client, call)*/ 444 static VOIDFUNC command_color 445 FUNCARG((widget, client, call), 446 Widget widget 447 ARGSEP XtPointer client 448 ARGSEP XtPointer call 449 ) 450 /* color widget selection 451 * set the garden's color to the selected one. 452 * update all the other icon widgets 453 */ 454 { 455 if(state.edit) 456 { 457 state.edit->board->colors = (unsigned)client; 458 paint_garden_icon(state.edit); 459 paint_garden_image(); 460 repaint_garden_icon(); 461 changed_flag |= state.change; 462 } 463 set_icon_color((unsigned)client); 464 return; 465 } 466 /*}}}*/ 467 /*{{{ void command_fill(widget, client, call)*/ 468 static VOIDFUNC command_fill 469 FUNCARG((widget, client, call), 470 Widget widget 471 ARGSEP XtPointer client 472 ARGSEP XtPointer call 473 ) 474 /* fill widget selection 475 * set garden's fill pattern and update all the other iconn widgets 476 */ 477 { 478 if(state.edit) 479 { 480 state.edit->board->fill = (unsigned)client; 481 paint_garden_icon(state.edit); 482 paint_garden_image(); 483 repaint_garden_icon(); 484 changed_flag |= state.change; 485 } 486 set_icon_fill((unsigned)client); 487 return; 488 } 489 /*}}}*/ 490 /*{{{ void command_mode(widget, client, call)*/ 491 static VOIDFUNC command_mode 492 FUNCARG((widget, client, call), 493 Widget widget 494 ARGSEP XtPointer client 495 ARGSEP XtPointer call 496 ) 497 /* display mode set callback. 498 * change the display mode and update the garden 499 */ 500 { 501 if(state.mode != (unsigned)client) 502 { 503 state.mode = (unsigned)client; 504 paint_garden_icon(state.edit); 505 paint_garden_image(); 506 repaint_garden_icon(); 507 } 508 return; 509 } 510 /*}}}*/ 511 /*{{{ void command_option(widget, client, call)*/ 512 static VOIDFUNC command_option 513 FUNCARG((widget, client, call), 514 Widget widget 515 ARGSEP XtPointer client 516 ARGSEP XtPointer call 517 ) 518 /* option callback. If the selected option is the apples, 519 * set the current apple to the selected one. 520 * Otherwise, or if no current button has any apples 521 * set the pressed button's action to the selected option. 522 * Otherwise update the apple icons 523 */ 524 { 525 IconCallback *call_data; 526 unsigned option; 527 528 option = (unsigned)client; 529 call_data = (IconCallback *)call; 530 if(option == OPTION_APPLES) 531 if(select_apple(call_data->selection)) 532 return; 533 if(call_data->button >= Button1 && call_data->button <= Button5) 534 bind_button(call_data->button - Button1, option); 535 return; 536 } 537 /*}}}*/ 538 /*{{{ Boolean convert_string2option(display, args, num_args, from, to, data)*/ 539 static Boolean convert_string2option 540 /* ARGSUSED */ 541 FUNCARG((display, args, num_args, from, to, data), 542 Display *display 543 ARGSEP XrmValue *args 544 ARGSEP Cardinal *num_args 545 ARGSEP XrmValue *from 546 ARGSEP XrmValue *to 547 ARGSEP XtPointer *data 548 ) 549 /* 550 * converts an option string to option number 551 */ 552 { 553 static char CONST *options[] = 554 {"apple", "random", "cherry", "path", "player", "den", NULL}; 555 char CONST **ptr; 556 static int result; 557 558 if(to->size < sizeof(int)) 559 { 560 to->size = sizeof(int); 561 return False; 562 } 563 for(ptr = options; *ptr; ptr++) 564 if(!strcmp(*ptr, (char CONST *)from->addr)) 565 { 566 to->size = sizeof(int); 567 result = ptr - options; 568 if(to->addr) 569 *(int *)to->addr = result; 570 else 571 to->addr = (XtPointer)&result; 572 return True; 573 } 574 XtDisplayStringConversionWarning(display, from->addr, XtROption); 575 return False; 576 } 577 /*}}}*/ 578 /*{{{ void drag_button(widget, client, call)*/ 579 static VOIDFUNC drag_button 580 FUNCARG((widget, client, call), 581 Widget widget 582 ARGSEP XtPointer client 583 ARGSEP XtPointer call 584 ) 585 /* drag callback for button selection. 586 * Copy the initiating button's option to the selected button. 587 */ 588 { 589 DragCallback *call_data; 590 int option; 591 unsigned ix; 592 GIZMO *gptr; 593 594 call_data = (DragCallback *)call; 595 for(gptr = &gizmos[GIZMO_BUTTONS + BUTTONS - 1], option = BUTTONS; 596 option--; gptr--) 597 if(gptr->widget == call_data->invoker) 598 { 599 option = state.button[option]; 600 break; 601 } 602 for(gptr = &gizmos[GIZMO_BUTTONS + BUTTONS - 1], ix = BUTTONS; 603 ix--; gptr--) 604 if(gptr->widget == call_data->selected) 605 { 606 bind_button(ix, option); 607 break; 608 } 609 return; 610 } 611 /*}}}*/ 612 /*{{{ void drag_option(widget, client, call)*/ 613 static VOIDFUNC drag_option 614 FUNCARG((widget, client, call), 615 Widget widget 616 ARGSEP XtPointer client 617 ARGSEP XtPointer call 618 ) 619 /* drag callback from option. 620 * set the selected button's action from the initiating option 621 */ 622 { 623 DragCallback *call_data; 624 int option; 625 unsigned ix; 626 GIZMO *gptr; 627 628 call_data = (DragCallback *)call; 629 for(gptr = &gizmos[GIZMO_OPTIONS + OPTIONS - 1], option = OPTIONS; 630 option--; gptr--) 631 if(gptr->widget == call_data->invoker) 632 break; 633 for(gptr = &gizmos[GIZMO_BUTTONS + BUTTONS - 1], ix = BUTTONS; 634 ix--; gptr--) 635 if(gptr->widget == call_data->selected) 636 { 637 bind_button(ix, option); 638 break; 639 } 640 return; 641 } 642 /*}}}*/ 643 /*{{{ void generate_icon(iptr)*/ 644 static VOIDFUNC generate_icon 645 FUNCARG((iptr), 646 ICON *iptr 647 ) 648 /* generate an icon pixmap. 649 * Copies its background an blats on its sprite 650 */ 651 { 652 XGCValues gcv; 653 654 gcv.fill_style = FillOpaqueStippled; 655 gcv.background = data.mono != False ? display.white : 656 colors[backgrounds[iptr->color & ~VALUE_LOCK][0]].pixel; 657 gcv.foreground = data.mono != False ? display.black : 658 colors[backgrounds[iptr->color & ~VALUE_LOCK][1]].pixel; 659 gcv.stipple = fills[iptr->fill & ~VALUE_LOCK].mask; 660 XChangeGC(display.display, GCN(GC_BOARD), 661 GCFillStyle | GCForeground | GCBackground | GCStipple, &gcv); 662 XFillRectangle(display.display, iptr->pixmap, 663 GCN(iptr->sprite == SPRITE_PLAYER || iptr->sprite == SPRITE_DEN ? 664 GC_CLEAR : GC_BOARD), 0, 0, iptr->size.x, iptr->size.y); 665 if(iptr->sprite && sprites[iptr->sprite].image) 666 { 667 XCopyArea(display.display, sprites[iptr->sprite].mask, iptr->pixmap, 668 GCN(GC_MASK), 0, 0, iptr->size.x, iptr->size.y, 0, 0); 669 XCopyArea(display.display, sprites[iptr->sprite].image, iptr->pixmap, 670 GCN(GC_OR), 0, 0, iptr->size.x, iptr->size.y, 0, 0); 671 } 672 return; 673 } 674 /*}}}*/ 675 /*{{{ void garden_comment(widget, client, call)*/ 676 static VOIDFUNC garden_comment 677 FUNCARG((widget, client, call), 678 Widget widget 679 ARGSEP XtPointer client 680 ARGSEP XtPointer call 681 ) 682 { 683 if(state.edit) 684 all_garden_comment(state.edit, state.change); 685 return; 686 } 687 /*}}}*/ 688 /*{{{ ICON gizmo2icon(gptr)*/ 689 static ICON *gizmo2icon 690 FUNCARG((gptr), 691 GIZMO *gptr 692 ) 693 /* find the icon associated with a gizmo. 694 * returns NULL if none is 695 */ 696 { 697 unsigned ix; 698 ICON *iptr; 699 700 for(iptr = icons, ix = XtNumber(icons); ix--; iptr++) 701 if(iptr->gizmo == gptr - gizmos) 702 return iptr; 703 return NULL; 704 } 705 /*}}}*/ 706 /*{{{ void install_control(root)*/ 707 extern VOIDFUNC install_control 708 FUNCARG((root), 709 Widget root 710 ) 711 /* create the widgets for the control panel 712 */ 713 { 714 unsigned ix; 715 GIZMO *gptr; 716 717 /*{{{ initialize arg_**/ 718 { 719 XFontStruct *ptr; 720 721 ptr = XQueryFont(display.display, data.font); 722 arg_count[0].value = (XtArgVal)ptr; 723 arg_warning[0].value = (XtArgVal)sprites[SPRITE_WARNING].image; 724 } 725 /*}}}*/ 726 create_gizmos(root, gizmos, XtNumber(gizmos)); 727 /*{{{ initialize pixmaps*/ 728 { 729 ICON *iptr; 730 731 for(iptr = icons, ix = XtNumber(icons); ix--; iptr++) 732 { 733 iptr->pixmap = XCreatePixmap(display.display, display.copy, 734 iptr->size.x, iptr->size.y, display.depth); 735 generate_icon(iptr); 736 XtVaSetValues(gizmos[iptr->gizmo].widget, 737 XtNpixmap, (XtArgVal)iptr->pixmap, NULL); 738 } 739 } 740 /*}}}*/ 741 /*{{{ get additional resources*/ 742 { 743 /*{{{ static XtResource button_resources[] =*/ 744 static XtResource button_resources[] = 745 { 746 {"option", "Option", XtROption, sizeof(int), 747 0, XtRImmediate, (XtPointer)-1}, 748 }; 749 /*}}}*/ 750 /*{{{ static XtResource index_resources[] =*/ 751 static XtResource index_resources[] = 752 { 753 {"index", "Index", XtRInt, sizeof(int), 754 0, XtRImmediate, (XtPointer)0}, 755 }; 756 /*}}}*/ 757 /*{{{ typedef struct _Option*/ 758 typedef struct _Option 759 { 760 unsigned gizmo; 761 unsigned *dest; 762 unsigned range; 763 } OPTION; 764 /*}}}*/ 765 /*{{{ static OPTION options[] =*/ 766 static CONST OPTION options[] = 767 { 768 {GIZMO_OPTIONS + 0, &initial_board[0].apples, 4}, 769 {GIZMO_CONTROL_FILLS, &initial_board[0].fill, FILLS}, 770 {GIZMO_CONTROL_COLORS, &initial_board[0].colors, BACKGROUNDS}, 771 {GIZMO_DISPLAY, &state.mode, MODES}, 772 }; 773 /*}}}*/ 774 OPTION CONST *optr; 775 776 XtAppSetTypeConverter(display.context, XtRString, XtROption, 777 convert_string2option, (XtConvertArgRec *)NULL, 0, XtCacheNone, 778 (void (*)PROTOARG((XtAppContext, XrmValue *, XtPointer, 779 XrmValue *, Cardinal *)))NULL); 780 for(gptr = &gizmos[GIZMO_BUTTONS + BUTTONS - 1], ix = BUTTONS; 781 ix--; gptr--) 782 { 783 unsigned binding; 784 785 XtGetApplicationResources(gptr->widget, (XtPointer)&binding, 786 button_resources, XtNumber(button_resources), NULL, 0); 787 bind_button(ix, binding >= OPTIONS ? -1 : binding); 788 } 789 for(optr = options, ix = XtNumber(options); ix--; optr++) 790 { 791 XtGetApplicationResources(gizmos[optr->gizmo].widget, 792 optr->dest, index_resources, XtNumber(index_resources), NULL, 0); 793 *optr->dest %= optr->range; 794 } 795 gizmo2icon(&gizmos[GIZMO_OPTIONS])->sprite += state.apple; 796 for(ix = BUTTONS; ix--;) 797 if(state.button[ix] >= 0) 798 gizmo2icon(&gizmos[GIZMO_BUTTONS + ix])->sprite = 799 gizmo2icon(&gizmos[GIZMO_OPTIONS + state.button[ix]])->sprite; 800 } 801 /*}}}*/ 802 /*{{{ set option callback*/ 803 for(gptr = &gizmos[GIZMO_OPTIONS + OPTIONS - 1], ix = OPTIONS; 804 ix--; gptr--) 805 XtAddCallback(gptr->widget, XtNcallback, command_option, (XtPointer)ix); 806 /*}}}*/ 807 /*{{{ set fill callback*/ 808 for(gptr = &gizmos[GIZMO_FILLS + FILLS - 1], ix = FILLS; ix--; gptr--) 809 XtAddCallback(gptr->widget, XtNcallback, command_fill, (XtPointer)ix); 810 /*}}}*/ 811 /*{{{ set color callback*/ 812 for(gptr = &gizmos[GIZMO_COLORS + BACKGROUNDS - 1], ix = BACKGROUNDS; 813 ix--; gptr--) 814 XtAddCallback(gptr->widget, XtNcallback, command_color, (XtPointer)ix); 815 /*}}}*/ 816 /*{{{ set mode callback*/ 817 for(gptr = &gizmos[GIZMO_DISPLAY_BASE + MODES - 1], ix = MODES; 818 ix--; gptr--) 819 XtAddCallback(gptr->widget, XtNcallback, command_mode, (XtPointer)ix); 820 /*}}}*/ 821 /*{{{ set button callback*/ 822 for(gptr = &gizmos[GIZMO_BUTTONS + BUTTONS - 1], ix = BUTTONS; 823 ix--; gptr--) 824 XtAddCallback(gptr->widget, XtNcallback, command_button, (XtPointer)ix); 825 /*}}}*/ 826 /*{{{ setup apple scrollbar*/ 827 { 828 float shown; 829 Arg arg[1]; 830 831 gptr = &gizmos[GIZMO_TOTAL_WARNINGS + 5]; 832 shown = (float)1.0; 833 XtSetArg(arg[0], XtNshown, sizeof(float) > sizeof(XtArgVal) ? 834 (XtArgVal)&shown : *(XtArgVal *)&shown); 835 XtSetValues(gptr->widget, arg, 1); 836 XtAddCallback(gptr->widget, XtNjumpProc, apple_jump, (XtPointer)NULL); 837 XtAddCallback(gptr->widget, XtNscrollProc, apple_scroll, (XtPointer)NULL); 838 } 839 /*}}}*/ 840 /*{{{ set drags into buttons*/ 841 { 842 static Widget list[5]; 843 844 for(ix = XtNumber(list); ix--;) 845 list[ix] = gizmos[GIZMO_BUTTONS + ix].widget; 846 XtVaSetValues(gizmos[GIZMO_OPTION_DRAG].widget, 847 MredNwidgetChoices, (XtArgVal)list, 848 MredNnumWidgetChoices, (XtArgVal)XtNumber(list), NULL); 849 XtVaSetValues(gizmos[GIZMO_BUTTON_DRAG].widget, 850 MredNwidgetChoices, (XtArgVal)list, 851 MredNnumWidgetChoices, (XtArgVal)XtNumber(list), NULL); 852 XtAddCallback(gizmos[GIZMO_BUTTON_DRAG].widget, XtNcallback, 853 drag_button, (XtPointer)NULL); 854 XtAddCallback(gizmos[GIZMO_OPTION_DRAG].widget, XtNcallback, 855 drag_option, (XtPointer)NULL); 856 } 857 /*}}}*/ 858 XtAddCallback(gizmos[GIZMO_COMMENT].widget, XtNcallback, 859 garden_comment, (XtPointer)NULL); 860 return; 861 } 862 /*}}}*/ 863 /*{{{ unsigned select_apple(apple)*/ 864 static unsigned select_apple 865 FUNCARG((apple), 866 unsigned apple 867 ) 868 /* sets explicit apple and ipdates buttons bound to it 869 * returns 1 if at least one button bound 870 */ 871 { 872 GIZMO *gptr; 873 ICON *iptr; 874 unsigned ix; 875 unsigned found; 876 unsigned sprite; 877 878 sprite = SPRITE_APPLES + apple; 879 if(state.apple != apple) 880 { 881 state.apple = apple; 882 gptr = &gizmos[GIZMO_OPTIONS]; 883 iptr = gizmo2icon(gptr); 884 iptr->sprite = sprite; 885 generate_icon(iptr); 886 IconRepaint(gptr->widget); 887 gptr = &gizmos[GIZMO_DISPLAY_SEPARATE]; 888 iptr = gizmo2icon(gptr); 889 iptr->sprite = sprite; 890 generate_icon(iptr); 891 IconRepaint(gptr->widget); 892 if(state.mode == MODE_SEPARATE) 893 paint_garden_image(); 894 } 895 found = 0; 896 for(ix = BUTTONS; ix--;) 897 if(!state.button[ix]) 898 { 899 gptr = &gizmos[GIZMO_BUTTONS + ix]; 900 iptr = gizmo2icon(gptr); 901 iptr->sprite = sprite; 902 generate_icon(iptr); 903 IconRepaint(gptr->widget); 904 found = 1; 905 } 906 return found; 907 } 908 /*}}}*/ 909 /*{{{ void set_count_flag(count, flag)*/ 910 static VOIDFUNC set_count_flag 911 FUNCARG((count, flag), 912 unsigned count 913 ARGSEP unsigned flag 914 ) 915 { 916 static unsigned state; 917 918 if(flag != !!(state & (1 << count))) 919 /*{{{ change flag*/ 920 { 921 state ^= 1 << count; 922 XtVaSetValues(gizmos[GIZMO_TOTAL_WARNINGS + count].widget, 923 XtNbitmap, sprites[SPRITE_WARNING + flag].image, NULL); 924 } 925 /*}}}*/ 926 return; 927 } 928 /*}}}*/ 929 /*{{{ void set_icon_color(color)*/ 930 static VOIDFUNC set_icon_color 931 FUNCARG((color), 932 unsigned color 933 ) 934 { 935 ICON *iptr; 936 unsigned ix; 937 938 state.color = color; 939 for(iptr = icons, ix = XtNumber(icons); ix--; iptr++) 940 if(!(iptr->color & VALUE_LOCK)) 941 { 942 iptr->color = color; 943 generate_icon(iptr); 944 IconRepaint(gizmos[iptr->gizmo].widget); 945 } 946 return; 947 } 948 /*}}}*/ 949 /*{{{ void set_icon_fill(fill)*/ 950 static VOIDFUNC set_icon_fill 951 FUNCARG((fill), 952 unsigned fill 953 ) 954 { 955 ICON *iptr; 956 unsigned ix; 957 958 state.fill = fill; 959 for(iptr = icons, ix = XtNumber(icons); ix--; iptr++) 960 if(!(iptr->fill & VALUE_LOCK)) 961 { 962 iptr->fill = fill; 963 generate_icon(iptr); 964 IconRepaint(gizmos[iptr->gizmo].widget); 965 } 966 return; 967 } 968 /*}}}*/ 969 /*{{{ void set_garden(dptr, source, change)*/ 970 extern VOIDFUNC set_garden 971 FUNCARG((dptr, source, change), 972 DESCRIPTOR *dptr /* garden descriptor to edit */ 973 ARGSEP int source /* source value */ 974 ARGSEP unsigned change /* change mask */ 975 ) 976 /* set up for a new garden to edit 977 */ 978 { 979 BOARD *bptr; 980 981 assert(dptr); 982 state.edit = dptr; 983 state.source = source; 984 state.change = change; 985 bptr = dptr->board; 986 assert(bptr); 987 set_icon_fill(bptr->fill); 988 set_icon_color(bptr->colors); 989 update_garden(); 990 reset_garden_stack(); 991 return; 992 } 993 /*}}}*/ 994 /*{{{ void set_garden_source(source, change)*/ 995 extern VOIDFUNC set_garden_source 996 FUNCARG((source, change), 997 int source 998 ARGSEP unsigned change 999 ) 1000 { 1001 state.source = source; 1002 state.change = change; 1003 paint_garden_source(); 1004 return; 1005 } 1006 /*}}}*/ 1007 /*{{{ void update_garden()*/ 1008 extern VOIDFUNC update_garden FUNCARGVOID 1009 /* updates counts and repaints the current garden from scratch 1010 */ 1011 { 1012 char *cptr; 1013 unsigned ix; 1014 1015 memset(state.counts, 0, sizeof(state.counts)); 1016 state.counts[COUNT_RANDOM] = state.edit->board->apples; 1017 /*{{{ set count values*/ 1018 for(ix = CELLS_DOWN; ix--;) 1019 for(cptr = state.edit->board->map[ix]; *cptr; cptr++) 1020 { 1021 if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr)) 1022 state.counts[COUNT_CHERRY]++; 1023 else if(ISPATHDEN(*cptr)) 1024 state.counts[COUNT_DEN]++; 1025 else if(ISPATHPLAYER(*cptr)) 1026 state.counts[COUNT_PLAYER]++; 1027 else if(ISAPPLE(*cptr)) 1028 { 1029 unsigned value; 1030 unsigned ix; 1031 1032 value = GARDENAPPLE(*cptr); 1033 for(ix = 4; ix--;) 1034 if(value & (1 << ix)) 1035 state.counts[COUNT_APPLES + ix]++; 1036 state.counts[COUNT_SPACES]++; 1037 } 1038 else if(*cptr == GARDEN_RANDOM) 1039 state.counts[COUNT_SPACES]++; 1040 if(ix != CELLS_DOWN - 1 && ISPATH(cptr[CELLS_ACROSS + 1]) && 1041 (*cptr == GARDEN_RANDOM || ISAPPLE(*cptr))) 1042 state.counts[COUNT_FALL]++; 1043 } 1044 /*}}}*/ 1045 apple_set_thumb(); 1046 for(ix = COUNTS; ix--;) 1047 adjust_count(ix, 0); 1048 paint_garden_image(); 1049 return; 1050 } 1051 /*}}}*/ 1052