1/* The lwlib interface to Athena widgets. 2Copyright (C) 1993 Chuck Thompson <cthomp@cs.uiuc.edu> 3Copyright (C) 1994, 2001, 2002, 2003, 2004, 2005, 2006, 4 2007 Free Software Foundation, Inc. 5 6This file is part of the Lucid Widget Library. 7 8The Lucid Widget Library is free software; you can redistribute it and/or 9modify it under the terms of the GNU General Public License as published by 10the Free Software Foundation; either version 1, or (at your option) 11any later version. 12 13The Lucid Widget Library is distributed in the hope that it will be useful, 14but WITHOUT ANY WARRANTY; without even the implied warranty of 15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16GNU General Public License for more details. 17 18You should have received a copy of the GNU General Public License 19along with GNU Emacs; see the file COPYING. If not, write to 20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21Boston, MA 02110-1301, USA. */ 22 23#ifdef HAVE_CONFIG_H 24#include <config.h> 25#endif 26 27#include <stdio.h> 28 29#include "../src/lisp.h" 30 31#include "lwlib-Xaw.h" 32 33#include <X11/StringDefs.h> 34#include <X11/IntrinsicP.h> 35#include <X11/CoreP.h> 36#include <X11/Shell.h> 37 38#include <X11/Xaw/Scrollbar.h> 39#include <X11/Xaw/Paned.h> 40#include <X11/Xaw/Dialog.h> 41#include <X11/Xaw/Form.h> 42#include <X11/Xaw/Command.h> 43#include <X11/Xaw/Label.h> 44 45#include <X11/Xatom.h> 46 47static void xaw_generic_callback (/*Widget, XtPointer, XtPointer*/); 48 49 50Boolean 51lw_xaw_widget_p (widget) 52 Widget widget; 53{ 54 return (XtIsSubclass (widget, scrollbarWidgetClass) || 55 XtIsSubclass (widget, dialogWidgetClass)); 56} 57 58#if 0 59static void 60xaw_update_scrollbar (instance, widget, val) 61 widget_instance *instance; 62 Widget widget; 63 widget_value *val; 64{ 65 if (val->scrollbar_data) 66 { 67 scrollbar_values *data = val->scrollbar_data; 68 Dimension height, width; 69 Dimension pos_x, pos_y; 70 int widget_shown, widget_topOfThumb; 71 float new_shown, new_topOfThumb; 72 73 XtVaGetValues (widget, 74 XtNheight, &height, 75 XtNwidth, &width, 76 XtNx, &pos_x, 77 XtNy, &pos_y, 78 XtNtopOfThumb, &widget_topOfThumb, 79 XtNshown, &widget_shown, 80 NULL); 81 82 /* 83 * First size and position the scrollbar widget. 84 * We need to position it to second-guess the Paned widget's notion 85 * of what should happen when the WMShell gets resized. 86 */ 87 if (height != data->scrollbar_height || pos_y != data->scrollbar_pos) 88 { 89 XtConfigureWidget (widget, pos_x, data->scrollbar_pos, 90 width, data->scrollbar_height, 0); 91 92 XtVaSetValues (widget, 93 XtNlength, data->scrollbar_height, 94 XtNthickness, width, 95 NULL); 96 } 97 98 /* 99 * Now the size the scrollbar's slider. 100 */ 101 new_shown = (float) data->slider_size / 102 (float) (data->maximum - data->minimum); 103 104 new_topOfThumb = (float) (data->slider_position - data->minimum) / 105 (float) (data->maximum - data->minimum); 106 107 if (new_shown > 1.0) 108 new_shown = 1.0; 109 if (new_shown < 0) 110 new_shown = 0; 111 112 if (new_topOfThumb > 1.0) 113 new_topOfThumb = 1.0; 114 if (new_topOfThumb < 0) 115 new_topOfThumb = 0; 116 117 if (new_shown != widget_shown || new_topOfThumb != widget_topOfThumb) 118 XawScrollbarSetThumb (widget, new_topOfThumb, new_shown); 119 } 120} 121#endif 122 123void 124#ifdef PROTOTYPES 125xaw_update_one_widget (widget_instance *instance, Widget widget, 126 widget_value *val, Boolean deep_p) 127#else 128xaw_update_one_widget (instance, widget, val, deep_p) 129 widget_instance *instance; 130 Widget widget; 131 widget_value *val; 132 Boolean deep_p; 133#endif 134{ 135#if 0 136 if (XtIsSubclass (widget, scrollbarWidgetClass)) 137 { 138 xaw_update_scrollbar (instance, widget, val); 139 } 140#endif 141 if (XtIsSubclass (widget, dialogWidgetClass)) 142 { 143 Arg al[1]; 144 int ac = 0; 145 XtSetArg (al[ac], XtNlabel, val->contents->value); ac++; 146 XtSetValues (widget, al, ac); 147 } 148 else if (XtIsSubclass (widget, commandWidgetClass)) 149 { 150 Dimension bw = 0; 151 Arg al[3]; 152 153 XtVaGetValues (widget, XtNborderWidth, &bw, NULL); 154 if (bw == 0) 155 /* Don't let buttons end up with 0 borderwidth, that's ugly... 156 Yeah, all this should really be done through app-defaults files 157 or fallback resources, but that's a whole different can of worms 158 that I don't feel like opening right now. Making Athena widgets 159 not look like shit is just entirely too much work. 160 */ 161 { 162 XtSetArg (al[0], XtNborderWidth, 1); 163 XtSetValues (widget, al, 1); 164 } 165 166 XtSetSensitive (widget, val->enabled); 167 XtSetArg (al[0], XtNlabel, val->value); 168 /* Force centered button text. Se above. */ 169 XtSetArg (al[1], XtNjustify, XtJustifyCenter); 170 XtSetValues (widget, al, 2); 171 XtRemoveAllCallbacks (widget, XtNcallback); 172 XtAddCallback (widget, XtNcallback, xaw_generic_callback, instance); 173 } 174} 175 176void 177xaw_update_one_value (instance, widget, val) 178 widget_instance *instance; 179 Widget widget; 180 widget_value *val; 181{ 182 /* This function is not used by the scrollbars and those are the only 183 Athena widget implemented at the moment so do nothing. */ 184 return; 185} 186 187void 188xaw_destroy_instance (instance) 189 widget_instance *instance; 190{ 191 if (XtIsSubclass (instance->widget, dialogWidgetClass)) 192 /* Need to destroy the Shell too. */ 193 XtDestroyWidget (XtParent (instance->widget)); 194 else 195 XtDestroyWidget (instance->widget); 196} 197 198void 199xaw_popup_menu (widget, event) 200 Widget widget; 201 XEvent *event; 202{ 203 /* An Athena menubar has not been implemented. */ 204 return; 205} 206 207void 208#ifdef PROTOTYPES 209xaw_pop_instance (widget_instance *instance, Boolean up) 210#else 211xaw_pop_instance (instance, up) 212 widget_instance *instance; 213 Boolean up; 214#endif 215{ 216 Widget widget = instance->widget; 217 218 if (up) 219 { 220 if (XtIsSubclass (widget, dialogWidgetClass)) 221 { 222 /* For dialogs, we need to call XtPopup on the parent instead 223 of calling XtManageChild on the widget. 224 Also we need to hack the shell's WM_PROTOCOLS to get it to 225 understand what the close box is supposed to do!! 226 */ 227 Display *dpy = XtDisplay (widget); 228 Widget shell = XtParent (widget); 229 Atom props [2]; 230 int i = 0; 231 props [i++] = XInternAtom (dpy, "WM_DELETE_WINDOW", False); 232 XChangeProperty (dpy, XtWindow (shell), 233 XInternAtom (dpy, "WM_PROTOCOLS", False), 234 XA_ATOM, 32, PropModeAppend, 235 (unsigned char *) props, i); 236 237 /* Center the widget in its parent. Why isn't this kind of crap 238 done automatically? I thought toolkits were supposed to make 239 life easier? 240 */ 241 { 242 unsigned int x, y, w, h; 243 Widget topmost = instance->parent; 244 Arg args[2]; 245 246 w = shell->core.width; 247 h = shell->core.height; 248 while (topmost->core.parent && XtIsRealized (topmost->core.parent)) 249 topmost = topmost->core.parent; 250 if (topmost->core.width < w) x = topmost->core.x; 251 else x = topmost->core.x + ((topmost->core.width - w) / 2); 252 if (topmost->core.height < h) y = topmost->core.y; 253 else y = topmost->core.y + ((topmost->core.height - h) / 2); 254 /* Using XtMoveWidget caused the widget to come 255 out in the wrong place with vtwm. 256 Question of virtual vs real coords, perhaps. */ 257 XtSetArg (args[0], XtNx, x); 258 XtSetArg (args[1], XtNy, y); 259 XtSetValues (shell, args, 2); 260 } 261 262 /* Finally, pop it up. */ 263 XtPopup (shell, XtGrabNonexclusive); 264 } 265 else 266 XtManageChild (widget); 267 } 268 else 269 { 270 if (XtIsSubclass (widget, dialogWidgetClass)) 271 XtUnmanageChild (XtParent (widget)); 272 else 273 XtUnmanageChild (widget); 274 } 275} 276 277 278/* Dialog boxes */ 279 280static char overrideTrans[] = 281 "<Message>WM_PROTOCOLS: lwlib_delete_dialog()"; 282/* Dialogs pop down on any key press */ 283static char dialogOverride[] = 284 "<KeyPress>Escape: lwlib_delete_dialog()"; 285static void wm_delete_window(); 286static XtActionsRec xaw_actions [] = { 287 {"lwlib_delete_dialog", wm_delete_window} 288}; 289static Boolean actions_initted = False; 290 291static Widget 292make_dialog (name, parent, pop_up_p, shell_title, icon_name, text_input_slot, radio_box, list, left_buttons, right_buttons) 293 char* name; 294 Widget parent; 295 Boolean pop_up_p; 296 char* shell_title; 297 char* icon_name; 298 Boolean text_input_slot; 299 Boolean radio_box; 300 Boolean list; 301 int left_buttons; 302 int right_buttons; 303{ 304 Arg av [20]; 305 int ac = 0; 306 int i, bc; 307 char button_name [255]; 308 Widget shell; 309 Widget dialog; 310 Widget button; 311 XtTranslations override; 312 313 if (! pop_up_p) abort (); /* not implemented */ 314 if (text_input_slot) abort (); /* not implemented */ 315 if (radio_box) abort (); /* not implemented */ 316 if (list) abort (); /* not implemented */ 317 318 if (! actions_initted) 319 { 320 XtAppContext app = XtWidgetToApplicationContext (parent); 321 XtAppAddActions (app, xaw_actions, 322 sizeof (xaw_actions) / sizeof (xaw_actions[0])); 323 actions_initted = True; 324 } 325 326 override = XtParseTranslationTable (overrideTrans); 327 328 ac = 0; 329 XtSetArg (av[ac], XtNtitle, shell_title); ac++; 330 XtSetArg (av[ac], XtNallowShellResize, True); ac++; 331 332 /* Don't allow any geometry request from the user. */ 333 XtSetArg (av[ac], XtNgeometry, 0); ac++; 334 335 shell = XtCreatePopupShell ("dialog", transientShellWidgetClass, 336 parent, av, ac); 337 XtOverrideTranslations (shell, override); 338 339 ac = 0; 340 dialog = XtCreateManagedWidget (name, dialogWidgetClass, shell, av, ac); 341 override = XtParseTranslationTable (dialogOverride); 342 XtOverrideTranslations (dialog, override); 343 344 bc = 0; 345 button = 0; 346 for (i = 0; i < left_buttons; i++) 347 { 348 ac = 0; 349 XtSetArg (av [ac], XtNfromHoriz, button); ac++; 350 XtSetArg (av [ac], XtNleft, XtChainLeft); ac++; 351 XtSetArg (av [ac], XtNright, XtChainLeft); ac++; 352 XtSetArg (av [ac], XtNtop, XtChainBottom); ac++; 353 XtSetArg (av [ac], XtNbottom, XtChainBottom); ac++; 354 XtSetArg (av [ac], XtNresizable, True); ac++; 355 sprintf (button_name, "button%d", ++bc); 356 button = XtCreateManagedWidget (button_name, commandWidgetClass, 357 dialog, av, ac); 358 } 359 if (right_buttons) 360 { 361 /* Create a separator 362 363 I want the separator to take up the slack between the buttons on 364 the right and the buttons on the left (that is I want the buttons 365 after the separator to be packed against the right edge of the 366 window) but I can't seem to make it do it. 367 */ 368 ac = 0; 369 XtSetArg (av [ac], XtNfromHoriz, button); ac++; 370/* XtSetArg (av [ac], XtNfromVert, XtNameToWidget (dialog, "label")); ac++; */ 371 XtSetArg (av [ac], XtNleft, XtChainLeft); ac++; 372 XtSetArg (av [ac], XtNright, XtChainRight); ac++; 373 XtSetArg (av [ac], XtNtop, XtChainBottom); ac++; 374 XtSetArg (av [ac], XtNbottom, XtChainBottom); ac++; 375 XtSetArg (av [ac], XtNlabel, ""); ac++; 376 XtSetArg (av [ac], XtNwidth, 30); ac++; /* #### aaack!! */ 377 XtSetArg (av [ac], XtNborderWidth, 0); ac++; 378 XtSetArg (av [ac], XtNshapeStyle, XmuShapeRectangle); ac++; 379 XtSetArg (av [ac], XtNresizable, False); ac++; 380 XtSetArg (av [ac], XtNsensitive, False); ac++; 381 button = XtCreateManagedWidget ("separator", 382 /* labelWidgetClass, */ 383 /* This has to be Command to fake out 384 the Dialog widget... */ 385 commandWidgetClass, 386 dialog, av, ac); 387 } 388 for (i = 0; i < right_buttons; i++) 389 { 390 ac = 0; 391 XtSetArg (av [ac], XtNfromHoriz, button); ac++; 392 XtSetArg (av [ac], XtNleft, XtChainRight); ac++; 393 XtSetArg (av [ac], XtNright, XtChainRight); ac++; 394 XtSetArg (av [ac], XtNtop, XtChainBottom); ac++; 395 XtSetArg (av [ac], XtNbottom, XtChainBottom); ac++; 396 XtSetArg (av [ac], XtNresizable, True); ac++; 397 sprintf (button_name, "button%d", ++bc); 398 button = XtCreateManagedWidget (button_name, commandWidgetClass, 399 dialog, av, ac); 400 } 401 402 return dialog; 403} 404 405Widget 406xaw_create_dialog (instance) 407 widget_instance* instance; 408{ 409 char *name = instance->info->type; 410 Widget parent = instance->parent; 411 Widget widget; 412 Boolean pop_up_p = instance->pop_up_p; 413 char *shell_name = 0; 414 char *icon_name = 0; 415 Boolean text_input_slot = False; 416 Boolean radio_box = False; 417 Boolean list = False; 418 int total_buttons; 419 int left_buttons = 0; 420 int right_buttons = 1; 421 422 switch (name [0]) { 423 case 'E': case 'e': 424 icon_name = "dbox-error"; 425 shell_name = "Error"; 426 break; 427 428 case 'I': case 'i': 429 icon_name = "dbox-info"; 430 shell_name = "Information"; 431 break; 432 433 case 'L': case 'l': 434 list = True; 435 icon_name = "dbox-question"; 436 shell_name = "Prompt"; 437 break; 438 439 case 'P': case 'p': 440 text_input_slot = True; 441 icon_name = "dbox-question"; 442 shell_name = "Prompt"; 443 break; 444 445 case 'Q': case 'q': 446 icon_name = "dbox-question"; 447 shell_name = "Question"; 448 break; 449 } 450 451 total_buttons = name [1] - '0'; 452 453 if (name [3] == 'T' || name [3] == 't') 454 { 455 text_input_slot = False; 456 radio_box = True; 457 } 458 else if (name [3]) 459 right_buttons = name [4] - '0'; 460 461 left_buttons = total_buttons - right_buttons; 462 463 widget = make_dialog (name, parent, pop_up_p, 464 shell_name, icon_name, text_input_slot, radio_box, 465 list, left_buttons, right_buttons); 466 467 return widget; 468} 469 470 471static void 472xaw_generic_callback (widget, closure, call_data) 473 Widget widget; 474 XtPointer closure; 475 XtPointer call_data; 476{ 477 widget_instance *instance = (widget_instance *) closure; 478 Widget instance_widget; 479 LWLIB_ID id; 480 XtPointer user_data; 481 482 lw_internal_update_other_instances (widget, closure, call_data); 483 484 if (! instance) 485 return; 486 if (widget->core.being_destroyed) 487 return; 488 489 instance_widget = instance->widget; 490 if (!instance_widget) 491 return; 492 493 id = instance->info->id; 494 495#if 0 496 user_data = NULL; 497 XtVaGetValues (widget, XtNuserData, &user_data, NULL); 498#else 499 /* Damn! Athena doesn't give us a way to hang our own data on the 500 buttons, so we have to go find it... I guess this assumes that 501 all instances of a button have the same call data. */ 502 { 503 widget_value *val = instance->info->val->contents; 504 char *name = XtName (widget); 505 while (val) 506 { 507 if (val->name && !strcmp (val->name, name)) 508 break; 509 val = val->next; 510 } 511 if (! val) abort (); 512 user_data = val->call_data; 513 } 514#endif 515 516 if (instance->info->selection_cb) 517 instance->info->selection_cb (widget, id, user_data); 518} 519 520static void 521wm_delete_window (w, closure, call_data) 522 Widget w; 523 XtPointer closure; 524 XtPointer call_data; 525{ 526 LWLIB_ID id; 527 Cardinal nkids; 528 int i; 529 Widget *kids = 0; 530 Widget widget, shell; 531 532 if (XtIsSubclass (w, dialogWidgetClass)) 533 shell = XtParent (w); 534 else 535 shell = w; 536 537 if (! XtIsSubclass (shell, shellWidgetClass)) 538 abort (); 539 XtVaGetValues (shell, XtNnumChildren, &nkids, NULL); 540 XtVaGetValues (shell, XtNchildren, &kids, NULL); 541 if (!kids || !*kids) 542 abort (); 543 for (i = 0; i < nkids; i++) 544 { 545 widget = kids[i]; 546 if (XtIsSubclass (widget, dialogWidgetClass)) 547 break; 548 } 549 id = lw_get_widget_id (widget); 550 if (! id) abort (); 551 552 { 553 widget_info *info = lw_get_widget_info (id); 554 if (! info) abort (); 555 if (info->selection_cb) 556 info->selection_cb (widget, id, (XtPointer) -1); 557 } 558 559 lw_destroy_all_widgets (id); 560} 561 562 563/* Scrollbars */ 564 565#if 0 566static void 567xaw_scrollbar_scroll (widget, closure, call_data) 568 Widget widget; 569 XtPointer closure; 570 XtPointer call_data; 571{ 572 widget_instance *instance = (widget_instance *) closure; 573 LWLIB_ID id; 574 scroll_event event_data; 575 576 if (!instance || widget->core.being_destroyed) 577 return; 578 579 id = instance->info->id; 580 event_data.slider_value = 0; 581 event_data.time = 0; 582 583 if ((int) call_data > 0) 584 event_data.action = SCROLLBAR_PAGE_DOWN; 585 else 586 event_data.action = SCROLLBAR_PAGE_UP; 587 588 if (instance->info->pre_activate_cb) 589 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data); 590} 591#endif 592 593#if 0 594static void 595xaw_scrollbar_jump (widget, closure, call_data) 596 Widget widget; 597 XtPointer closure; 598 XtPointer call_data; 599{ 600 widget_instance *instance = (widget_instance *) closure; 601 LWLIB_ID id; 602 scroll_event event_data; 603 scrollbar_values *val = 604 (scrollbar_values *) instance->info->val->scrollbar_data; 605 float percent; 606 607 if (!instance || widget->core.being_destroyed) 608 return; 609 610 id = instance->info->id; 611 612 percent = * (float *) call_data; 613 event_data.slider_value = 614 (int) (percent * (float) (val->maximum - val->minimum)) + val->minimum; 615 616 event_data.time = 0; 617 event_data.action = SCROLLBAR_DRAG; 618 619 if (instance->info->pre_activate_cb) 620 instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data); 621} 622#endif 623 624static Widget 625xaw_create_scrollbar (instance) 626 widget_instance *instance; 627{ 628#if 0 629 Arg av[20]; 630 int ac = 0; 631 Dimension width; 632 Widget scrollbar; 633 634 XtVaGetValues (instance->parent, XtNwidth, &width, NULL); 635 636 XtSetArg (av[ac], XtNshowGrip, 0); ac++; 637 XtSetArg (av[ac], XtNresizeToPreferred, 1); ac++; 638 XtSetArg (av[ac], XtNallowResize, True); ac++; 639 XtSetArg (av[ac], XtNskipAdjust, True); ac++; 640 XtSetArg (av[ac], XtNwidth, width); ac++; 641 XtSetArg (av[ac], XtNmappedWhenManaged, True); ac++; 642 643 scrollbar = 644 XtCreateWidget (instance->info->name, scrollbarWidgetClass, 645 instance->parent, av, ac); 646 647 /* We have to force the border width to be 0 otherwise the 648 geometry manager likes to start looping for awhile... */ 649 XtVaSetValues (scrollbar, XtNborderWidth, 0, NULL); 650 651 XtRemoveAllCallbacks (scrollbar, "jumpProc"); 652 XtRemoveAllCallbacks (scrollbar, "scrollProc"); 653 654 XtAddCallback (scrollbar, "jumpProc", xaw_scrollbar_jump, 655 (XtPointer) instance); 656 XtAddCallback (scrollbar, "scrollProc", xaw_scrollbar_scroll, 657 (XtPointer) instance); 658 659 return scrollbar; 660#else 661 return NULL; 662#endif 663} 664 665static Widget 666xaw_create_main (instance) 667 widget_instance *instance; 668{ 669 Arg al[1]; 670 int ac; 671 672 /* Create a vertical Paned to hold menubar */ 673 ac = 0; 674 XtSetArg (al[ac], XtNborderWidth, 0); ac++; 675 return XtCreateWidget (instance->info->name, panedWidgetClass, 676 instance->parent, al, ac); 677} 678 679widget_creation_entry 680xaw_creation_table [] = 681{ 682 {"scrollbar", xaw_create_scrollbar}, 683 {"main", xaw_create_main}, 684 {NULL, NULL} 685}; 686 687/* arch-tag: fbbd3589-ae1c-41a0-9142-f628cfee6564 688 (do not change this comment) */ 689