1 /* 2 Copyright 1989, 1994, 1998 The Open Group 3 4 Permission to use, copy, modify, distribute, and sell this software and its 5 documentation for any purpose is hereby granted without fee, provided that 6 the above copyright notice appear in all copies and that both that 7 copyright notice and this permission notice appear in supporting 8 documentation. 9 10 The above copyright notice and this permission notice shall be included in 11 all copies or substantial portions of the Software. 12 13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 17 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 20 Except as contained in this notice, the name of The Open Group shall not be 21 used in advertising or otherwise to promote the sale, use or other dealings 22 in this Software without prior written authorization from The Open Group. 23 */ 24 25 /* 26 * List.c - List widget 27 * 28 * This is a List widget. It allows the user to select an item in a list and 29 * notifies the application through a callback function. 30 * 31 * Created: 8/13/88 32 * By: Chris D. Peterson 33 * MIT X Consortium 34 */ 35 36 #ifdef HAVE_CONFIG_H 37 #include <config.h> 38 #endif 39 #include <stdio.h> 40 #include <ctype.h> 41 #include <X11/IntrinsicP.h> 42 #include <X11/StringDefs.h> 43 #include <X11/Xmu/Drawing.h> 44 #include <X11/Xaw/ListP.h> 45 #include <X11/Xaw/XawInit.h> 46 #include "Private.h" 47 48 #define HeightLock 1 49 #define WidthLock 2 50 #define LongestLock 4 51 52 #define HeightFree(w) !(((ListWidget)(w))->list.freedoms & HeightLock) 53 #define WidthFree(w) !(((ListWidget)(w))->list.freedoms & WidthLock) 54 #define LongestFree(w) !(((ListWidget)(w))->list.freedoms & LongestLock) 55 56 #define MaxSize 32767 57 58 /* 59 * Class Methods 60 */ 61 static void XawListDestroy(Widget); 62 static void XawListInitialize(Widget, Widget, ArgList, Cardinal*); 63 static XtGeometryResult XawListQueryGeometry(Widget, XtWidgetGeometry*, 64 XtWidgetGeometry*); 65 static void XawListRedisplay(Widget, XEvent*, Region); 66 static void XawListResize(Widget); 67 static Boolean XawListSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 68 69 /* 70 * Prototypes 71 */ 72 static void CalculatedValues(Widget); 73 static void ChangeSize(Widget, unsigned int, unsigned int); 74 static void ClipToShadowInteriorAndLongest(ListWidget, GC*, unsigned int); 75 static int CvtToItem(Widget, int, int, int*); 76 static void FindCornerItems(Widget, XEvent*, int*, int*); 77 static void GetGCs(Widget); 78 static void HighlightBackground(Widget, int, int, GC); 79 static Bool ItemInRectangle(Widget, int, int, int); 80 static Bool Layout(Widget, Bool, Bool, Dimension*, Dimension*); 81 static void PaintItemName(Widget, int); 82 static void ResetList(Widget, Bool, Bool); 83 84 /* 85 * Actions 86 */ 87 static void Notify(Widget, XEvent*, String*, Cardinal*); 88 static void Set(Widget, XEvent*, String*, Cardinal*); 89 static void Unset(Widget, XEvent*, String*, Cardinal*); 90 91 /* 92 * Initialization 93 */ 94 static char defaultTranslations[] = 95 "<Btn1Down>:" "Set()\n" 96 "<Btn1Up>:" "Notify()\n" 97 ; 98 99 #define offset(field) XtOffsetOf(ListRec, field) 100 static XtResource resources[] = { 101 { 102 XtNforeground, 103 XtCForeground, 104 XtRPixel, 105 sizeof(Pixel), 106 offset(list.foreground), 107 XtRString, 108 (XtPointer)XtDefaultForeground 109 }, 110 { 111 XtNcursor, 112 XtCCursor, 113 XtRCursor, 114 sizeof(Cursor), 115 offset(simple.cursor), 116 XtRString, 117 (XtPointer)"left_ptr" 118 }, 119 { 120 XtNfont, 121 XtCFont, 122 XtRFontStruct, 123 sizeof(XFontStruct*), 124 offset(list.font), 125 XtRString, 126 (XtPointer)XtDefaultFont 127 }, 128 { 129 XtNfontSet, 130 XtCFontSet, 131 XtRFontSet, 132 sizeof(XFontSet), 133 offset(list.fontset), 134 XtRString, 135 (XtPointer)XtDefaultFontSet 136 }, 137 { 138 XtNlist, 139 XtCList, 140 XtRPointer, 141 sizeof(char**), 142 offset(list.list), 143 #ifdef notyet 144 XtRStringArray, 145 #else 146 XtRString, 147 #endif 148 NULL 149 }, 150 { 151 XtNdefaultColumns, 152 XtCColumns, 153 XtRInt, 154 sizeof(int), 155 offset(list.default_cols), 156 XtRImmediate, 157 (XtPointer)2 158 }, 159 { 160 XtNlongest, 161 XtCLongest, 162 XtRInt, 163 sizeof(int), 164 offset(list.longest), 165 XtRImmediate, 166 (XtPointer)0 167 }, 168 { 169 XtNnumberStrings, 170 XtCNumberStrings, 171 XtRInt, 172 sizeof(int), 173 offset(list.nitems), 174 XtRImmediate, 175 (XtPointer)0 176 }, 177 { 178 XtNpasteBuffer, 179 XtCBoolean, 180 XtRBoolean, 181 sizeof(Boolean), 182 offset(list.paste), 183 XtRImmediate, 184 (XtPointer)False 185 }, 186 { 187 XtNforceColumns, 188 XtCColumns, 189 XtRBoolean, 190 sizeof(Boolean), 191 offset(list.force_cols), 192 XtRImmediate, 193 (XtPointer)False 194 }, 195 { 196 XtNverticalList, 197 XtCBoolean, 198 XtRBoolean, 199 sizeof(Boolean), 200 offset(list.vertical_cols), 201 XtRImmediate, 202 (XtPointer)False 203 }, 204 { 205 XtNinternalWidth, 206 XtCWidth, 207 XtRDimension, 208 sizeof(Dimension), 209 offset(list.internal_width), 210 XtRImmediate, 211 (XtPointer)2 212 }, 213 { 214 XtNinternalHeight, 215 XtCHeight, 216 XtRDimension, 217 sizeof(Dimension), 218 offset(list.internal_height), 219 XtRImmediate, 220 (XtPointer)2 221 }, 222 { 223 XtNcolumnSpacing, 224 XtCSpacing, 225 XtRDimension, 226 sizeof(Dimension), 227 offset(list.column_space), 228 XtRImmediate, 229 (XtPointer)6 230 }, 231 { 232 XtNrowSpacing, 233 XtCSpacing, 234 XtRDimension, 235 sizeof(Dimension), 236 offset(list.row_space), 237 XtRImmediate, 238 (XtPointer)2 239 }, 240 { 241 XtNcallback, 242 XtCCallback, 243 XtRCallback, 244 sizeof(XtPointer), 245 offset(list.callback), 246 XtRCallback, 247 NULL 248 }, 249 #ifndef OLDXAW 250 { 251 XtNshowCurrent, 252 XtCBoolean, 253 XtRBoolean, 254 sizeof(Boolean), 255 offset(list.show_current), 256 XtRImmediate, 257 (XtPointer)False 258 }, 259 #endif 260 }; 261 #undef offset 262 263 static XtActionsRec actions[] = { 264 {"Notify", Notify}, 265 {"Set", Set}, 266 {"Unset", Unset}, 267 }; 268 269 #define Superclass (&simpleClassRec) 270 ListClassRec listClassRec = { 271 /* core */ 272 { 273 (WidgetClass)Superclass, /* superclass */ 274 "List", /* class_name */ 275 sizeof(ListRec), /* widget_size */ 276 XawInitializeWidgetSet, /* class_initialize */ 277 NULL, /* class_part_initialize */ 278 False, /* class_inited */ 279 XawListInitialize, /* initialize */ 280 NULL, /* initialize_hook */ 281 XtInheritRealize, /* realize */ 282 actions, /* actions */ 283 XtNumber(actions), /* num_actions */ 284 resources, /* resources */ 285 XtNumber(resources), /* num_resources */ 286 NULLQUARK, /* xrm_class */ 287 True, /* compress_motion */ 288 False, /* compress_exposure */ 289 True, /* compress_enterleave */ 290 False, /* visible_interest */ 291 XawListDestroy, /* destroy */ 292 XawListResize, /* resize */ 293 XawListRedisplay, /* expose */ 294 XawListSetValues, /* set_values */ 295 NULL, /* set_values_hook */ 296 XtInheritSetValuesAlmost, /* set_values_almost */ 297 NULL, /* get_values_hook */ 298 NULL, /* accept_focus */ 299 XtVersion, /* version */ 300 NULL, /* callback_private */ 301 defaultTranslations, /* tm_table */ 302 XawListQueryGeometry, /* query_geometry */ 303 NULL, /* display_accelerator */ 304 NULL, /* extension */ 305 }, 306 /* simple */ 307 { 308 XtInheritChangeSensitive, /* change_sensitive */ 309 #ifndef OLDXAW 310 NULL, 311 #endif 312 }, 313 /* list */ 314 { 315 NULL, /* extension */ 316 }, 317 }; 318 319 WidgetClass listWidgetClass = (WidgetClass)&listClassRec; 320 321 /* 322 * Implementation 323 */ 324 static void 325 GetGCs(Widget w) 326 { 327 ListWidget lw = (ListWidget)w; 328 XGCValues values = { 329 .foreground = lw->list.foreground, 330 .font = lw->list.font->fid 331 }; 332 333 if (lw->simple.international == True) 334 lw->list.normgc = XtAllocateGC(w, 0, GCForeground, &values, GCFont, 0); 335 else 336 lw->list.normgc = XtGetGC(w, GCForeground | GCFont, &values); 337 338 values.foreground = lw->core.background_pixel; 339 340 if (lw->simple.international == True) 341 lw->list.revgc = XtAllocateGC(w, 0, GCForeground, &values, GCFont, 0); 342 else 343 lw->list.revgc = XtGetGC(w, GCForeground | GCFont, &values); 344 345 values.tile = XmuCreateStippledPixmap(XtScreen(w), 346 lw->list.foreground, 347 lw->core.background_pixel, 348 lw->core.depth); 349 values.fill_style = FillTiled; 350 351 if (lw->simple.international == True) 352 lw->list.graygc = XtAllocateGC(w, 0, GCTile | GCFillStyle, 353 &values, GCFont, 0); 354 else 355 lw->list.graygc = XtGetGC(w, GCFont | GCTile | GCFillStyle, &values); 356 } 357 358 static void 359 CalculatedValues(Widget w) 360 { 361 ListWidget lw = (ListWidget)w; 362 363 /* If list is NULL then the list will just be the name of the widget */ 364 if (lw->list.list == NULL) { 365 lw->list.list = &lw->core.name; 366 lw->list.nitems = 1; 367 } 368 369 /* Get number of items */ 370 if (lw->list.nitems == 0) 371 for (; lw->list.list[lw->list.nitems] != NULL ; lw->list.nitems++) 372 ; 373 374 /* Get column width */ 375 if (LongestFree(lw)) { 376 int i; 377 378 lw->list.longest = 0; /* so it will accumulate real longest below */ 379 380 for (i = 0 ; i < lw->list.nitems; i++) { 381 int len; 382 383 if (lw->simple.international == True) 384 len = XmbTextEscapement(lw->list.fontset, lw->list.list[i], 385 (int)strlen(lw->list.list[i])); 386 else 387 len = XTextWidth(lw->list.font, lw->list.list[i], 388 (int)strlen(lw->list.list[i])); 389 if (len > lw->list.longest) 390 lw->list.longest = len; 391 } 392 } 393 394 lw->list.col_width = lw->list.longest + lw->list.column_space; 395 } 396 397 /* 398 * Function: 399 * ResetList 400 * 401 * Parameters: 402 * w - list widget 403 * changex - allow the height or width to change? 404 * changey - "" 405 * 406 * Description: 407 * Resets the new list when important things change. 408 * 409 * Returns: 410 * True if width or height have been changed 411 */ 412 static void 413 ResetList(Widget w, Bool changex, Bool changey) 414 { 415 Dimension width = XtWidth(w); 416 Dimension height = XtHeight(w); 417 418 CalculatedValues(w); 419 420 if (Layout(w, changex, changey, &width, &height)) { 421 if (XtIsComposite(XtParent(w))) 422 ChangeSize(w, width, height); 423 else { 424 XtWidth(w) = width; 425 XtHeight(w) = height; 426 } 427 } 428 } 429 430 /* 431 * Function: 432 * ChangeSize 433 * 434 * Parameters: 435 * w - widget to try change the size of 436 * 437 * Description: 438 * Laysout the widget. 439 */ 440 static void 441 ChangeSize(Widget w, unsigned int width, unsigned int height) 442 { 443 XtWidgetGeometry request = { 444 .request_mode = CWWidth | CWHeight, 445 .width = (Dimension)width, 446 .height = (Dimension)height 447 }; 448 XtWidgetGeometry reply; 449 450 switch (XtMakeGeometryRequest(w, &request, &reply)) { 451 case XtGeometryYes: 452 case XtGeometryNo: 453 break; 454 case XtGeometryAlmost: 455 Layout(w, request.height != reply.height, 456 request.width != reply.width, &reply.width, &reply.height); 457 request = reply; 458 switch (XtMakeGeometryRequest(w, &request, &reply)) { 459 case XtGeometryYes: 460 case XtGeometryNo: 461 break; 462 case XtGeometryAlmost: 463 request = reply; 464 Layout(w, False, False, &request.width, &request.height); 465 request.request_mode = CWWidth | CWHeight; 466 XtMakeGeometryRequest(w, &request, &reply); 467 /*FALLTHROUGH*/ 468 default: 469 break; 470 } 471 /*FALLTHROUGH*/ 472 default: 473 break; 474 } 475 } 476 477 /*ARGSUSED*/ 478 static void 479 XawListInitialize(Widget temp1 _X_UNUSED, Widget cnew, ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 480 { 481 ListWidget lw = (ListWidget)cnew; 482 483 if (!lw->list.font) XtError("Aborting: no font found\n"); 484 if (lw->simple.international && !lw->list.fontset) 485 XtError("Aborting: no fontset found\n"); 486 487 /* 488 * Initialize all private resources 489 */ 490 /* record for posterity if we are free */ 491 lw->list.freedoms = ((XtWidth(lw) != 0) * WidthLock + 492 (XtHeight(lw) != 0) * HeightLock + 493 (lw->list.longest != 0) * LongestLock); 494 495 GetGCs(cnew); 496 497 /* Set row height, based on font or fontset */ 498 if (lw->simple.international == True) 499 lw->list.row_height = 500 XExtentsOfFontSet(lw->list.fontset)->max_ink_extent.height + 501 lw->list.row_space; 502 else 503 lw->list.row_height = lw->list.font->max_bounds.ascent + 504 lw->list.font->max_bounds.descent + 505 lw->list.row_space; 506 507 ResetList(cnew, WidthFree(lw), HeightFree(lw)); 508 509 lw->list.highlight = lw->list.is_highlighted = NO_HIGHLIGHT; 510 } 511 512 /* 513 * Function: 514 * CvtToItem 515 * 516 * Parameters: 517 * w - list widget 518 * xloc - x location 519 * yloc - y location 520 * 521 * Description: 522 * Converts Xcoord to item number of item containing that point. 523 * 524 * Returns: 525 * Item number 526 */ 527 static int 528 CvtToItem(Widget w, int xloc, int yloc, int *item) 529 { 530 int one, another; 531 ListWidget lw = (ListWidget)w; 532 int ret_val = OKAY; 533 534 if (lw->list.vertical_cols) { 535 one = lw->list.nrows * ((xloc - (int)lw->list.internal_width) 536 / lw->list.col_width); 537 another = (yloc - (int)lw->list.internal_height) / lw->list.row_height; 538 /* If out of range, return minimum possible value */ 539 if (another >= lw->list.nrows) { 540 another = lw->list.nrows - 1; 541 ret_val = OUT_OF_RANGE; 542 } 543 } 544 else { 545 one = (lw->list.ncols * ((yloc - (int)lw->list.internal_height) 546 / lw->list.row_height)); 547 /* If in right margin handle things right */ 548 another = (xloc - (int)lw->list.internal_width) / lw->list.col_width; 549 if (another >= lw->list.ncols) { 550 another = lw->list.ncols - 1; 551 ret_val = OUT_OF_RANGE; 552 } 553 } 554 if (xloc < 0 || yloc < 0) 555 ret_val = OUT_OF_RANGE; 556 if (one < 0) 557 one = 0; 558 if (another < 0) 559 another = 0; 560 *item = one + another; 561 if (*item >= lw->list.nitems) 562 return (OUT_OF_RANGE); 563 564 return (ret_val); 565 } 566 567 /* 568 * Function: 569 * FindCornerItems 570 * 571 * Arguments: 572 * w - list widget 573 * event - event structure that has the rectangle it it 574 * ul_ret - the corners (return) 575 * lr_ret - "" 576 * 577 * Description: 578 * Find the corners of the rectangle in item space. 579 */ 580 static void 581 FindCornerItems(Widget w, XEvent *event, int *ul_ret, int *lr_ret) 582 { 583 int xloc, yloc; 584 585 xloc = event->xexpose.x; 586 yloc = event->xexpose.y; 587 CvtToItem(w, xloc, yloc, ul_ret); 588 xloc += event->xexpose.width; 589 yloc += event->xexpose.height; 590 CvtToItem(w, xloc, yloc, lr_ret); 591 } 592 593 /* 594 * Function: 595 * ItemInRectangle 596 * 597 * Parameters: 598 * w - list widget 599 * ul - corners of the rectangle in item space 600 * lr - "" 601 * item - item to check 602 * 603 * Returns: 604 * True if the item passed is in the given rectangle 605 */ 606 static Bool 607 ItemInRectangle(Widget w, int ul, int lr, int item) 608 { 609 ListWidget lw = (ListWidget)w; 610 int mod_item; 611 int things; 612 613 if (item < ul || item > lr) 614 return (False); 615 if (lw->list.vertical_cols) 616 things = lw->list.nrows; 617 else 618 things = lw->list.ncols; 619 620 mod_item = item % things; 621 if ((mod_item >= ul % things) && (mod_item <= lr % things)) 622 return (True); 623 624 return (False); 625 } 626 627 /* HighlightBackground() 628 * 629 * Paints the color of the background for the given item. It performs 630 * clipping to the interior of internal_width/height by hand, as its a 631 * simple calculation and probably much faster than using Xlib and a clip mask. 632 * 633 * x, y - ul corner of the area item occupies. 634 * gc - the gc to use to paint this rectangle 635 */ 636 static void 637 HighlightBackground(Widget w, int x, int y, GC gc) 638 { 639 ListWidget lw = (ListWidget)w; 640 Dimension width = (Dimension)lw->list.col_width; 641 Dimension height = (Dimension)lw->list.row_height; 642 Dimension frame_limited_width = (Dimension)(XtWidth(w) - lw->list.internal_width - x); 643 Dimension frame_limited_height= (Dimension)(XtHeight(w) - lw->list.internal_height - y); 644 645 /* Clip the rectangle width and height to the edge of the drawable area */ 646 if (width > frame_limited_width) 647 width = frame_limited_width; 648 if (height > frame_limited_height) 649 height = frame_limited_height; 650 651 /* Clip the rectangle x and y to the edge of the drawable area */ 652 if (x < lw->list.internal_width) { 653 width = (Dimension)(width - (lw->list.internal_width - x)); 654 x = lw->list.internal_width; 655 } 656 if (y < lw->list.internal_height) { 657 height = (Dimension)(height - (lw->list.internal_height - y)); 658 y = lw->list.internal_height; 659 } 660 661 if (gc == lw->list.revgc && lw->core.background_pixmap != XtUnspecifiedPixmap) 662 XClearArea(XtDisplay(w), XtWindow(w), x, y, width, height, False); 663 else 664 XFillRectangle(XtDisplay(w), XtWindow(w), gc, x, y, width, height); 665 } 666 667 668 /* ClipToShadowInteriorAndLongest() 669 * 670 * Converts the passed gc so that any drawing done with that GC will not 671 * write in the empty margin (specified by internal_width/height) (which also 672 * prevents erasing the shadow. It also clips against the value longest. 673 * If the user doesn't set longest, this has no effect (as longest is the 674 * maximum of all item lengths). If the user does specify, say, 80 pixel 675 * columns, though, this prevents items from overwriting other items. 676 */ 677 static void 678 ClipToShadowInteriorAndLongest(ListWidget lw, GC *gc_p, unsigned int x) 679 { 680 XRectangle rect = { 681 .x = (short)x, 682 .y = (short)lw->list.internal_height, 683 .height = (unsigned short) 684 (XtHeight(lw) - (lw->list.internal_height << 1)), 685 .width = (unsigned short) 686 (XtWidth(lw) - (unsigned)lw->list.internal_width - x), 687 }; 688 689 if (rect.width > lw->list.longest) 690 rect.width = (unsigned short)lw->list.longest; 691 692 XSetClipRectangles(XtDisplay((Widget)lw), *gc_p, 0, 0, &rect, 1, YXBanded); 693 } 694 695 static void 696 PaintItemName(Widget w, int item) 697 { 698 _Xconst char *str; 699 GC gc; 700 int x, y, str_y; 701 ListWidget lw = (ListWidget)w; 702 XFontSetExtents *ext = XExtentsOfFontSet(lw->list.fontset); 703 704 if (!XtIsRealized(w) || item > lw->list.nitems) 705 return; 706 707 if (lw->list.vertical_cols) { 708 x = lw->list.col_width * (item / lw->list.nrows) 709 + lw->list.internal_width; 710 y = lw->list.row_height * (item % lw->list.nrows) 711 + lw->list.internal_height; 712 } 713 else { 714 x = lw->list.col_width * (item % lw->list.ncols) 715 + lw->list.internal_width; 716 y = lw->list.row_height * (item / lw->list.ncols) 717 + lw->list.internal_height; 718 } 719 720 if ( lw->simple.international == True ) 721 str_y = y + XawAbs(ext->max_ink_extent.y); 722 else 723 str_y = y + lw->list.font->max_bounds.ascent; 724 725 if (item == lw->list.is_highlighted) { 726 if (item == lw->list.highlight) { 727 gc = lw->list.revgc; 728 HighlightBackground(w, x, y, lw->list.normgc); 729 } 730 else { 731 if (XtIsSensitive(w)) 732 gc = lw->list.normgc; 733 else 734 gc = lw->list.graygc; 735 HighlightBackground(w, x, y, lw->list.revgc); 736 lw->list.is_highlighted = NO_HIGHLIGHT; 737 } 738 } 739 else { 740 if (item == lw->list.highlight) { 741 gc = lw->list.revgc; 742 HighlightBackground(w, x, y, lw->list.normgc); 743 lw->list.is_highlighted = item; 744 } 745 else { 746 if (XtIsSensitive(w)) 747 gc = lw->list.normgc; 748 else 749 gc = lw->list.graygc; 750 } 751 } 752 753 /* List's overall width contains the same number of inter-column 754 column_space's as columns. There should thus be a half 755 column_width margin on each side of each column. 756 The row case is symmetric */ 757 758 x += lw->list.column_space >> 1; 759 str_y += lw->list.row_space >> 1; 760 761 str = lw->list.list[item]; /* draw it */ 762 763 ClipToShadowInteriorAndLongest(lw, &gc, (unsigned)x); 764 765 if (lw->simple.international == True) 766 XmbDrawString(XtDisplay(w), XtWindow(w), lw->list.fontset, 767 gc, x, str_y, str, (int)strlen(str)); 768 else 769 XDrawString(XtDisplay(w), XtWindow(w), gc, x, str_y, str, (int)strlen(str)); 770 771 XSetClipMask(XtDisplay(w), gc, None); 772 } 773 774 static void 775 XawListRedisplay(Widget w, XEvent *event, Region region) 776 { 777 int item; /* an item to work with */ 778 int ul_item, lr_item; /* corners of items we need to paint */ 779 ListWidget lw = (ListWidget)w; 780 781 if (event == NULL) { 782 ul_item = 0; 783 lr_item = lw->list.nrows * lw->list.ncols - 1; 784 XClearWindow(XtDisplay(w), XtWindow(w)); 785 } 786 else 787 FindCornerItems(w, event, &ul_item, &lr_item); 788 789 if (Superclass->core_class.expose) 790 (Superclass->core_class.expose)(w, event, region); 791 792 for (item = ul_item; item <= lr_item && item < lw->list.nitems; item++) 793 if (ItemInRectangle(w, ul_item, lr_item, item)) 794 PaintItemName(w, item); 795 } 796 797 /* XawListQueryGeometry() 798 * 799 * This tells the parent what size we would like to be 800 * given certain constraints. 801 * w - the widget. 802 * intended - what the parent intends to do with us. 803 * requested - what we want to happen */ 804 static XtGeometryResult 805 XawListQueryGeometry(Widget w, XtWidgetGeometry *intended, 806 XtWidgetGeometry *requested) 807 { 808 Dimension new_width, new_height; 809 Bool change, width_req, height_req; 810 811 width_req = intended->request_mode & CWWidth; 812 height_req = intended->request_mode & CWHeight; 813 814 if (width_req) 815 new_width = intended->width; 816 else 817 new_width = XtWidth(w); 818 819 if (height_req) 820 new_height = intended->height; 821 else 822 new_height = XtHeight(w); 823 824 requested->request_mode = 0; 825 826 /* 827 * We only care about our height and width 828 */ 829 if (!width_req && !height_req) 830 return (XtGeometryYes); 831 832 change = Layout(w, !width_req, !height_req, &new_width, &new_height); 833 834 requested->request_mode |= CWWidth; 835 requested->width = new_width; 836 requested->request_mode |= CWHeight; 837 requested->height = new_height; 838 839 if (change) 840 return (XtGeometryAlmost); 841 842 return (XtGeometryYes); 843 } 844 845 static void 846 XawListResize(Widget w) 847 { 848 Dimension width, height; 849 850 width = XtWidth(w); 851 height = XtHeight(w); 852 853 if (Layout(w, False, False, &width, &height)) 854 XtAppWarning(XtWidgetToApplicationContext(w), 855 "List Widget: Size changed when it shouldn't " 856 "have when resising."); 857 } 858 859 /* Layout() 860 * 861 * lays out the item in the list. 862 * w - the widget. 863 * xfree, yfree - True if we are free to resize the widget in 864 * this direction. 865 * width, height- the is the current width and height that we are going 866 * we are going to layout the list widget to, 867 * depending on xfree and yfree of course. 868 * 869 * Return: 870 * True if width or height have been changed */ 871 static Bool 872 Layout(Widget w, Bool xfree, Bool yfree, Dimension *width, Dimension *height) 873 { 874 ListWidget lw = (ListWidget)w; 875 Bool change = False; 876 unsigned long width2 = 0, height2 = 0; 877 878 /* 879 * If force columns is set then always use number of columns specified 880 * by default_cols 881 */ 882 if (lw->list.force_cols) { 883 lw->list.ncols = lw->list.default_cols; 884 if (lw->list.ncols <= 0) 885 lw->list.ncols = 1; 886 lw->list.nrows = ((lw->list.nitems - 1) / lw->list.ncols) + 1; 887 if (xfree) { 888 /* this counts the same number 889 of inter-column column_space 's as columns. There should thus 890 be a half column_space margin on each side of each column...*/ 891 width2 = (unsigned long)(lw->list.ncols * lw->list.col_width + 892 (lw->list.internal_width << 1)); 893 change = True; 894 } 895 if (yfree) { 896 height2 = (unsigned long)(lw->list.nrows * lw->list.row_height + 897 (lw->list.internal_height << 1)); 898 change = True; 899 } 900 } 901 902 /* 903 * If both width and height are free to change the use default_cols 904 * to determine the number columns and set new width and height to 905 * just fit the window 906 */ 907 else if (xfree && yfree) { 908 lw->list.ncols = lw->list.default_cols; 909 if (lw->list.ncols <= 0) { 910 int wid = (int)XtWidth(lw) - (int)(lw->list.internal_width << 1) 911 + (int)lw->list.column_space; 912 913 if (wid <= 0 || lw->list.col_width <= 0 914 || (lw->list.ncols = wid / lw->list.col_width) <= 0) 915 lw->list.ncols = 1; 916 } 917 width2 = (unsigned long)((lw->list.ncols * lw->list.col_width) 918 + (lw->list.internal_width << 1)); 919 height2 = (unsigned long)((lw->list.nrows * lw->list.row_height) 920 + (lw->list.internal_height << 1)); 921 change = True; 922 } 923 924 /* 925 * If the width is fixed then use it to determine the number of columns. 926 * If the height is free to move (width still fixed) then resize the height 927 * of the widget to fit the current list exactly 928 */ 929 else if (!xfree) { 930 lw->list.ncols = ((int)(*width - (lw->list.internal_width << 1)) 931 / (int)lw->list.col_width); 932 if (lw->list.ncols <= 0) 933 lw->list.ncols = 1; 934 lw->list.nrows = ((lw->list.nitems - 1) / lw->list.ncols) + 1; 935 if (yfree) { 936 height2 = (unsigned long)((lw->list.nrows * lw->list.row_height) + 937 (lw->list.internal_height << 1)); 938 change = True; 939 } 940 } 941 942 /* 943 * The last case is xfree and !yfree we use the height to determine 944 * the number of rows and then set the width to just fit the resulting 945 * number of columns 946 */ 947 else if (!yfree) { 948 lw->list.nrows = ((int)(*height - (lw->list.internal_height << 1)) 949 / (int)lw->list.row_height); 950 if (lw->list.nrows <= 0) 951 lw->list.nrows = 1; 952 lw->list.ncols = ((lw->list.nitems - 1) / lw->list.nrows) + 1; 953 width2 = (unsigned long)((lw->list.ncols * lw->list.col_width) + 954 (lw->list.internal_width << 1)); 955 change = True; 956 } 957 958 if (!lw->list.force_cols && lw->list.nrows) { 959 /*CONSTCOND*/ 960 while (1) { 961 lw->list.nrows = ((lw->list.nitems - 1) / lw->list.ncols) + 1; 962 width2 = (unsigned long)((lw->list.ncols * lw->list.col_width) + 963 (lw->list.internal_width << 1)); 964 height2 = (unsigned long)((lw->list.nrows * lw->list.row_height) + 965 (lw->list.internal_height << 1)); 966 if (width2 >= MaxSize && height2 >= MaxSize) 967 break; 968 if (height2 > MaxSize) 969 ++lw->list.ncols; 970 else if (width2 > MaxSize && lw->list.ncols > 1) 971 --lw->list.ncols; 972 else 973 break; 974 } 975 } 976 if (width2) 977 *width = (Dimension)width2; 978 if (height2) 979 *height = (Dimension)height2; 980 981 return (change); 982 } 983 984 /* Notify() - Action 985 * 986 * Notifies the user that a button has been pressed, and 987 * calls the callback; if the XtNpasteBuffer resource is true 988 * then the name of the item is also put in CUT_BUFFER0 */ 989 /*ARGSUSED*/ 990 static void 991 Notify(Widget w, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 992 { 993 ListWidget lw = (ListWidget)w; 994 int item, item_len; 995 996 /* 997 * Find item and if out of range then unhighlight and return 998 * 999 * If the current item is unhighlighted then the user has aborted the 1000 * notify, so unhighlight and return 1001 */ 1002 if ((CvtToItem(w, event->xbutton.x, event->xbutton.y, &item) == OUT_OF_RANGE) 1003 || lw->list.highlight != item) { 1004 #ifndef OLDXAW 1005 if (!lw->list.show_current || lw->list.selected == NO_HIGHLIGHT) 1006 XawListUnhighlight(w); 1007 else 1008 XawListHighlight(w, lw->list.selected); 1009 #else 1010 XawListUnhighlight(w); 1011 #endif 1012 return; 1013 } 1014 1015 item_len = (int)strlen(lw->list.list[item]); 1016 1017 if (lw->list.paste) /* if XtNpasteBuffer set then paste it */ 1018 XStoreBytes(XtDisplay(w), lw->list.list[item], item_len); 1019 1020 #ifndef OLDXAW 1021 lw->list.selected = item; 1022 #endif 1023 /* 1024 * Call Callback function 1025 */ 1026 { 1027 XawListReturnStruct ret_value = { 1028 .string = lw->list.list[item], 1029 .list_index = item 1030 }; 1031 1032 XtCallCallbacks(w, XtNcallback, (XtPointer)&ret_value); 1033 } 1034 } 1035 1036 /* Unset() - Action 1037 * 1038 * unhighlights the current element */ 1039 /*ARGSUSED*/ 1040 static void 1041 Unset(Widget w, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 1042 { 1043 XawListUnhighlight(w); 1044 } 1045 1046 /* Set() - Action 1047 * 1048 * Highlights the current element */ 1049 /*ARGSUSED*/ 1050 static void 1051 Set(Widget w, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 1052 { 1053 int item; 1054 ListWidget lw = (ListWidget)w; 1055 1056 #ifndef OLDXAW 1057 lw->list.selected = lw->list.highlight; 1058 #endif 1059 if (CvtToItem(w, event->xbutton.x, event->xbutton.y, &item) == OUT_OF_RANGE) 1060 XawListUnhighlight(w); /* Unhighlight current item */ 1061 else if (lw->list.is_highlighted != item) /* If this item is not */ 1062 XawListHighlight(w, item); /* highlighted then do it */ 1063 } 1064 1065 /* 1066 * Set specified arguments into widget 1067 */ 1068 /*ARGSUSED*/ 1069 static Boolean 1070 XawListSetValues(Widget current, Widget request, Widget cnew, 1071 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 1072 { 1073 ListWidget cl = (ListWidget)current; 1074 ListWidget rl = (ListWidget)request; 1075 ListWidget nl = (ListWidget)cnew; 1076 Bool redraw = False; 1077 XFontSetExtents *ext = XExtentsOfFontSet(nl->list.fontset); 1078 1079 /* If the request height/width is different, lock it. Unless its 0. If 1080 neither new nor 0, leave it as it was. Not in R5 */ 1081 if (XtWidth(nl) != XtWidth(cl)) 1082 nl->list.freedoms |= WidthLock; 1083 if (XtWidth(nl) == 0) 1084 nl->list.freedoms &= ~WidthLock; 1085 1086 if (XtHeight(nl) != XtHeight(cl)) 1087 nl->list.freedoms |= HeightLock; 1088 if (XtHeight(nl) == 0) 1089 nl->list.freedoms &= ~HeightLock; 1090 1091 if (nl->list.longest != cl->list.longest) 1092 nl->list.freedoms |= LongestLock; 1093 if (nl->list.longest == 0) 1094 nl->list.freedoms &= ~LongestLock; 1095 1096 if (cl->list.foreground != nl->list.foreground || 1097 cl->core.background_pixel != nl->core.background_pixel || 1098 cl->list.font != nl->list.font) { 1099 XGCValues values; 1100 1101 XGetGCValues(XtDisplay(current), cl->list.graygc, GCTile, &values); 1102 XmuReleaseStippledPixmap(XtScreen(current), values.tile); 1103 XtReleaseGC(current, cl->list.graygc); 1104 XtReleaseGC(current, cl->list.revgc); 1105 XtReleaseGC(current, cl->list.normgc); 1106 GetGCs(cnew); 1107 redraw = True; 1108 } 1109 1110 if (cl->list.font != nl->list.font && cl->simple.international == False) 1111 nl->list.row_height = nl->list.font->max_bounds.ascent 1112 + nl->list.font->max_bounds.descent 1113 + nl->list.row_space; 1114 else if (cl->list.fontset != nl->list.fontset 1115 && cl->simple.international == True) 1116 nl->list.row_height = ext->max_ink_extent.height + nl->list.row_space; 1117 1118 /* ...If the above two font(set) change checkers above both failed, check 1119 if row_space was altered. If one of the above passed, row_height will 1120 already have been re-calculated */ 1121 else if (cl->list.row_space != nl->list.row_space) { 1122 if (cl->simple.international == True) 1123 nl->list.row_height = ext->max_ink_extent.height + nl->list.row_space; 1124 else 1125 nl->list.row_height = nl->list.font->max_bounds.ascent 1126 + nl->list.font->max_bounds.descent 1127 + nl->list.row_space; 1128 } 1129 1130 if (XtWidth(cl) != XtWidth(nl) || XtHeight(cl) != XtHeight(nl) 1131 || cl->list.internal_width != nl->list.internal_width 1132 || cl->list.internal_height != nl->list.internal_height 1133 || cl->list.column_space != nl->list.column_space 1134 || cl->list.row_space != nl->list.row_space 1135 || cl->list.default_cols != nl->list.default_cols 1136 || (cl->list.force_cols != nl->list.force_cols 1137 && rl->list.force_cols != nl->list.ncols) 1138 || cl->list.vertical_cols != nl->list.vertical_cols 1139 || cl->list.longest != nl->list.longest 1140 || cl->list.nitems != nl->list.nitems 1141 || cl->list.font != nl->list.font 1142 /* Equiv. fontsets might have different values, but the same fonts, 1143 so the next comparison is sloppy but not dangerous */ 1144 || cl->list.fontset != nl->list.fontset 1145 || cl->list.list != nl->list.list) { 1146 CalculatedValues(cnew); 1147 Layout(cnew, WidthFree(nl), HeightFree(nl), 1148 &nl->core.width, &nl->core.height); 1149 redraw = True; 1150 } 1151 1152 if (cl->list.list != nl->list.list || cl->list.nitems != nl->list.nitems) 1153 nl->list.is_highlighted = nl->list.highlight = NO_HIGHLIGHT; 1154 1155 if (cl->core.sensitive != nl->core.sensitive 1156 || cl->core.ancestor_sensitive != nl->core.ancestor_sensitive) { 1157 nl->list.highlight = NO_HIGHLIGHT; 1158 redraw = True; 1159 } 1160 1161 return (Boolean)(redraw); 1162 } 1163 1164 static void 1165 XawListDestroy(Widget w) 1166 { 1167 ListWidget lw = (ListWidget)w; 1168 XGCValues values; 1169 1170 XGetGCValues(XtDisplay(w), lw->list.graygc, GCTile, &values); 1171 XmuReleaseStippledPixmap(XtScreen(w), values.tile); 1172 XtReleaseGC(w, lw->list.graygc); 1173 XtReleaseGC(w, lw->list.revgc); 1174 XtReleaseGC(w, lw->list.normgc); 1175 } 1176 1177 /* 1178 * Function: 1179 * XawListChange 1180 * 1181 * Parameters: 1182 * w - list widget 1183 * list - new list 1184 * nitems - number of items in the list 1185 * longest - length (in Pixels) of the longest element in the list 1186 * resize - if True the the list widget will try to resize itself 1187 * 1188 * Description: 1189 * Changes the list being used and shown. 1190 * 1191 * Note: 1192 * If nitems of longest are <= 0 then they will be calculated 1193 * If nitems is <= 0 then the list needs to be NULL terminated 1194 */ 1195 void 1196 XawListChange(Widget w, String *list, int nitems, int longest, 1197 #if NeedWidePrototypes 1198 int resize_it 1199 #else 1200 Boolean resize_it 1201 #endif 1202 ) 1203 { 1204 ListWidget lw = (ListWidget)w; 1205 Dimension new_width = XtWidth(w); 1206 Dimension new_height = XtHeight(w); 1207 1208 lw->list.list = list; 1209 1210 if (nitems <= 0) 1211 nitems = 0; 1212 lw->list.nitems = nitems; 1213 if (longest <= 0) 1214 longest = 0; 1215 1216 /* If the user passes 0 meaning "calculate it", it must be free */ 1217 if (longest != 0) 1218 lw->list.freedoms |= LongestLock; 1219 else 1220 lw->list.freedoms &= ~LongestLock; 1221 1222 if (resize_it) 1223 lw->list.freedoms &= ~WidthLock & ~HeightLock; 1224 1225 lw->list.longest = longest; 1226 1227 CalculatedValues(w); 1228 1229 if (Layout(w, WidthFree(w), HeightFree(w), &new_width, &new_height)) 1230 ChangeSize(w, new_width, new_height); 1231 1232 lw->list.is_highlighted = lw->list.highlight = NO_HIGHLIGHT; 1233 if (XtIsRealized(w)) 1234 XawListRedisplay(w, NULL, NULL); 1235 } 1236 1237 void 1238 XawListUnhighlight(Widget w) 1239 { 1240 ListWidget lw = (ListWidget)w; 1241 1242 lw->list.highlight = NO_HIGHLIGHT; 1243 if (lw->list.is_highlighted != NO_HIGHLIGHT) 1244 PaintItemName(w, lw->list.is_highlighted); 1245 } 1246 1247 void 1248 XawListHighlight(Widget w, int item) 1249 { 1250 ListWidget lw = (ListWidget)w; 1251 1252 if (XtIsSensitive(w)) { 1253 lw->list.highlight = item; 1254 if (lw->list.is_highlighted != NO_HIGHLIGHT) 1255 PaintItemName(w, lw->list.is_highlighted); 1256 PaintItemName(w, item); 1257 } 1258 } 1259 1260 /* 1261 * Function: 1262 * XawListShowCurrent 1263 * 1264 * Parameters: 1265 * w - list widget 1266 * 1267 * Returns: 1268 * Info about the currently highlighted object 1269 */ 1270 XawListReturnStruct * 1271 XawListShowCurrent(Widget w) 1272 { 1273 ListWidget lw = (ListWidget)w; 1274 XawListReturnStruct *ret_val; 1275 1276 ret_val = (XawListReturnStruct *)XtMalloc(sizeof(XawListReturnStruct)); 1277 1278 ret_val->list_index = lw->list.highlight; 1279 if (ret_val->list_index == XAW_LIST_NONE) 1280 ret_val->string = ""; 1281 else 1282 ret_val->string = lw->list.list[ret_val->list_index]; 1283 1284 return (ret_val); 1285 } 1286