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