UNIXworkcode

1 /******************************************************************************* 2 * * 3 * managedList.c -- User interface for reorderable list of records * 4 * * 5 * Copyright (C) 1999 Mark Edel * 6 * * 7 * This is free software; you can redistribute it and/or modify it under the * 8 * terms of the GNU General Public License as published by the Free Software * 9 * Foundation; either version 2 of the License, or (at your option) any later * 10 * version. In addition, you may distribute version of this program linked to * 11 * Motif or Open Motif. See README for details. * 12 * * 13 * This software is distributed in the hope that it will be useful, but WITHOUT * 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * 16 * for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License along with * 19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple * 20 * Place, Suite 330, Boston, MA 02111-1307 USA * 21 * * 22 * Nirvana Text Editor * 23 * November, 1995 * 24 * * 25 * Written by Mark Edel * 26 * * 27 *******************************************************************************/ 28 29 #ifdef HAVE_CONFIG_H 30 #include "../config.h" 31 #endif 32 33 #include "managedList.h" 34 #include "misc.h" 35 #include "DialogF.h" 36 #include "nedit_malloc.h" 37 38 #include <stdio.h> 39 #include <string.h> 40 41 #include <X11/Intrinsic.h> 42 #include <Xm/Form.h> 43 #include <Xm/List.h> 44 #include <Xm/PushB.h> 45 #include <Xm/RowColumn.h> 46 47 #ifdef HAVE_DEBUG_H 48 #include "../debug.h" 49 #endif 50 51 /* Common data between the managed list callback functions */ 52 typedef struct { 53 Widget listW, deleteBtn, copyBtn, moveUpBtn, moveDownBtn; 54 void *(*getDialogDataCB)(void *, int, int *, void *); 55 void *getDialogDataArg; 56 void (*setDialogDataCB)(void *, void *); 57 void *setDialogDataArg; 58 void *(*copyItemCB)(void *); 59 void (*freeItemCB)(void *); 60 int (*deleteConfirmCB)(int, void *); 61 void *deleteConfirmArg; 62 int maxItems; 63 int *nItems; 64 void **itemList; 65 int lastSelection; 66 } managedListData; 67 68 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData); 69 static void deleteCB(Widget w, XtPointer clientData, XtPointer callData); 70 static void copyCB(Widget w, XtPointer clientData, XtPointer callData); 71 static void moveUpCB(Widget w, XtPointer clientData, XtPointer callData); 72 static void moveDownCB(Widget w, XtPointer clientData, XtPointer callData); 73 static int incorporateDialogData(managedListData *ml, int listPos, 74 int explicit); 75 static void updateDialogFromList(managedListData *ml, int selection); 76 static void updateListWidgetItem(managedListData *ml, int listPos); 77 static void listSelectionCB(Widget w, XtPointer clientData, XtPointer callData); 78 static int selectedListPosition(managedListData *ml); 79 static void selectItem(Widget listW, int itemIndex, int updateDialog); 80 static Widget shellOfWidget(Widget w); 81 82 /* 83 ** Create a user interface to help manage a list of arbitrary data records 84 ** which users can edit, add, delete, and reorder. 85 ** 86 ** The caller creates the overall dialog for presenting the data to the user, 87 ** but embeds the list and button widgets created here (and list management 88 ** code they activate) to handle the organization of the overall list. 89 ** 90 ** This routine creates a form widget containing the buttons and list widget 91 ** with which the user interacts with the list data. ManageListAndButtons 92 ** can be used alternatively to take advantage of the management code with a 93 ** different arrangement of the widgets (this routine puts buttons in a 94 ** column on the left, list on the right) imposed here. 95 ** 96 ** "args" and "argc" are passed to the form widget creation routine, so that 97 ** attachments can be specified for embedding the form in a dialog. 98 ** 99 ** See ManageListAndButtons for a description of the remaining arguments. 100 */ 101 Widget CreateManagedList(Widget parent, char *name, Arg *args, 102 int argC, void **itemList, int *nItems, int maxItems, int nColumns, 103 void *(*getDialogDataCB)(void *, int, int *, void *), 104 void *getDialogDataArg, void (*setDialogDataCB)(void *, void *), 105 void *setDialogDataArg, void (*freeItemCB)(void *)) 106 { 107 int ac; 108 Arg al[20]; 109 XmString s1; 110 Widget form, rowCol, listW; 111 Widget deleteBtn, copyBtn, moveUpBtn, moveDownBtn; 112 XmString *placeholderTable; 113 char *placeholderStr; 114 115 form = XmCreateForm(parent, name, args, argC); 116 XtManageChild(form); 117 rowCol = XtVaCreateManagedWidget("mlRowCol", xmRowColumnWidgetClass, form, 118 XmNpacking, XmPACK_COLUMN, 119 XmNleftAttachment, XmATTACH_FORM, 120 XmNtopAttachment, XmATTACH_FORM, 121 XmNbottomAttachment, XmATTACH_FORM, NULL); 122 deleteBtn = XtVaCreateManagedWidget("delete", xmPushButtonWidgetClass, 123 rowCol, XmNlabelString, s1=XmStringCreateSimple("Delete"), NULL); 124 XmStringFree(s1); 125 copyBtn = XtVaCreateManagedWidget("copy", xmPushButtonWidgetClass, rowCol, 126 XmNlabelString, s1=XmStringCreateSimple("Copy"), NULL); 127 XmStringFree(s1); 128 moveUpBtn = XtVaCreateManagedWidget("moveUp", xmPushButtonWidgetClass, 129 rowCol, XmNlabelString, s1=XmStringCreateSimple("Move ^"), NULL); 130 XmStringFree(s1); 131 moveDownBtn = XtVaCreateManagedWidget("moveDown", xmPushButtonWidgetClass, 132 rowCol, XmNlabelString, s1=XmStringCreateSimple("Move v"), NULL); 133 XmStringFree(s1); 134 135 /* AFAIK the only way to make a list widget n-columns wide is to make up 136 a fake initial string of that width, and create it with that */ 137 placeholderStr = (char*)NEditMalloc(nColumns+1); 138 memset(placeholderStr, 'm', nColumns); 139 placeholderStr[nColumns] = '\0'; 140 placeholderTable = StringTable(1, placeholderStr); 141 NEditFree(placeholderStr); 142 143 ac = 0; 144 XtSetArg(al[ac], XmNscrollBarDisplayPolicy, XmAS_NEEDED); ac++; 145 XtSetArg(al[ac], XmNlistSizePolicy, XmCONSTANT); ac++; 146 XtSetArg(al[ac], XmNitems, placeholderTable); ac++; 147 XtSetArg(al[ac], XmNitemCount, 1); ac++; 148 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; 149 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; 150 XtSetArg(al[ac], XmNleftWidget, rowCol); ac++; 151 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; 152 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; 153 listW = XmCreateScrolledList(form, "list", al, ac); 154 AddMouseWheelSupport(listW); 155 XtManageChild(listW); 156 FreeStringTable(placeholderTable); 157 158 return ManageListAndButtons(listW, deleteBtn, copyBtn, moveUpBtn, 159 moveDownBtn, itemList, nItems, maxItems, getDialogDataCB, 160 getDialogDataArg, setDialogDataCB, setDialogDataArg, freeItemCB); 161 } 162 163 /* 164 ** Manage a list widget and a set of buttons which represent a list of 165 ** records. The caller provides facilities for editing the records 166 ** individually, and this code handles the organization of the overall list, 167 ** such that the user can modify, add, and delete records, and re-order the 168 ** list. 169 ** 170 ** The format for the list of records managed by this code should be an 171 ** array of size "maxItems" of pointers to record structures. The records 172 ** themselves can be of any format, but the first field of the structure 173 ** must be a pointer to a character string which will be displayed as the 174 ** item name in the list. The list "itemList", and the number of items 175 ** "nItems" are automatically updated by the list management routines as the 176 ** user makes changes. 177 ** 178 ** The caller must provide routines for transferring data to and from the 179 ** dialog fields dedicated to displaying and editing records in the list. 180 ** The callback "setDialogDataCB" must take the contents of the item pointer 181 ** passed to it, and display the data it contains, erasing any previously 182 ** displayed data. The format of the setDialogData callback is: 183 ** 184 ** void setDialogDataCB(void *item, void *cbArg) 185 ** 186 ** item: a pointer to the record to be displayed 187 ** 188 ** cbArg: an arbitrary argument passed on to the callback routine 189 ** 190 ** The callback "setDialogDataCB" must allocate (with NEditMalloc) and return a 191 ** new record reflecting the current contents of the dialog fields. It may 192 ** do error checking on the data that the user has entered, and can abort 193 ** whatever operation triggered the request by setting "abort" to True. 194 ** This routine is called in a variety of contexts, such as the user 195 ** clicking on a list item, or requesting that a copy be made of the current 196 ** list item. To aide in communicating errors to the user, the boolean value 197 ** "explicitRequest" distinguishes between the case where the user has 198 ** specifically requested that the fields be read, and the case where he 199 ** may be surprised that errors are being reported, and require further 200 ** explanation. The format of the getDialogData callback is: 201 ** 202 ** void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort, 203 ** void *cbArg) 204 ** 205 ** oldItem: a pointer to the existing record being modified in the 206 ** dialog, or NULL, if the user is modifying the "New" item. 207 ** 208 ** explicitRequest: True if the user directly asked for the records 209 ** to be changed (as with an OK or Apply button). If a less direct 210 ** process resulted in the request, the user might need extra 211 ** explanation and possibly a chance to proceed using the existing 212 ** stored data (to use the data from oldItem, the routine should 213 ** make a new copy). 214 ** 215 ** abort: Can be set to True if the dialog fields contain errors. 216 ** Setting abort to True, stops whetever process made the request 217 ** for updating the data in the list from the displayed data, and 218 ** forces the user to remain focused on the currently displayed 219 ** item until he either gives up or gets it right. 220 ** 221 ** cbArg: arbitrary data, passed on from where the callback was 222 ** established in the list creation routines 223 ** 224 ** The return value should be an allocated 225 ** 226 ** The callback "freeItemCB" should free the item passed to it: 227 ** 228 ** void freeItemCB(void *item, void *cbArg) 229 ** 230 ** item: a pointer to the record to be freed 231 ** 232 ** The difference between ManageListAndButtons and CreateManagedList, is that 233 ** in this routine, the caller creates the list and button widgets and passes 234 ** them here so that they be arranged explicitly, rather than relying on the 235 ** default style imposed by CreateManagedList. ManageListAndButtons simply 236 ** attaches the appropriate callbacks to process mouse and keyboard input from 237 ** the widgets. 238 */ 239 Widget ManageListAndButtons(Widget listW, Widget deleteBtn, Widget copyBtn, 240 Widget moveUpBtn, Widget moveDownBtn, void **itemList, int *nItems, 241 int maxItems, void *(*getDialogDataCB)(void *, int, int *, void *), 242 void *getDialogDataArg, void (*setDialogDataCB)(void *, void *), 243 void *setDialogDataArg, void (*freeItemCB)(void *)) 244 { 245 managedListData *ml; 246 247 /* Create a managedList data structure to hold information about the 248 widgets, callbacks, and current state of the list */ 249 ml = (managedListData *)NEditMalloc(sizeof(managedListData)); 250 ml->listW = listW; 251 ml->deleteBtn = deleteBtn; 252 ml->copyBtn = copyBtn; 253 ml->moveUpBtn = moveUpBtn; 254 ml->moveDownBtn = moveDownBtn; 255 ml->getDialogDataCB = NULL; 256 ml->getDialogDataArg = getDialogDataArg; 257 ml->setDialogDataCB = NULL; 258 ml->setDialogDataArg = setDialogDataArg; 259 ml->freeItemCB = freeItemCB; 260 ml->deleteConfirmCB = NULL; 261 ml->deleteConfirmArg = NULL; 262 ml->nItems = nItems; 263 ml->maxItems = maxItems; 264 ml->itemList = itemList; 265 ml->lastSelection = 1; 266 267 /* Make the managed list data structure accessible from the list widget 268 pointer, and make sure it gets freed when the list is destroyed */ 269 XtVaSetValues(ml->listW, XmNuserData, ml, NULL); 270 XtAddCallback(ml->listW, XmNdestroyCallback, destroyCB, ml); 271 272 /* Add callbacks for button and list actions */ 273 XtAddCallback(ml->deleteBtn, XmNactivateCallback, deleteCB, ml); 274 XtAddCallback(ml->copyBtn, XmNactivateCallback, copyCB, ml); 275 XtAddCallback(ml->moveUpBtn, XmNactivateCallback, moveUpCB, ml); 276 XtAddCallback(ml->moveDownBtn, XmNactivateCallback, moveDownCB, ml); 277 XtAddCallback(ml->listW, XmNbrowseSelectionCallback, listSelectionCB, ml); 278 279 /* Initialize the list and buttons (don't set up the callbacks until 280 this is done, so they won't get called on creation) */ 281 updateDialogFromList(ml, -1); 282 ml->getDialogDataCB = getDialogDataCB; 283 ml->setDialogDataCB = setDialogDataCB; 284 285 return ml->listW; 286 } 287 288 /* 289 ** Update the currently selected list item from the dialog fields, using 290 ** the getDialogDataCB callback. "explicitRequest" is a boolean value 291 ** passed to on to the getDialogDataCB callback to help set the tone for 292 ** how error messages are presented (see ManageListAndButtons for more 293 ** information). 294 */ 295 int UpdateManagedList(Widget listW, int explicitRequest) 296 { 297 managedListData *ml; 298 299 /* Recover the pointer to the managed list structure from the widget's 300 userData pointer */ 301 XtVaGetValues(listW, XmNuserData, &ml, NULL); 302 303 /* Make the update */ 304 return incorporateDialogData(ml, selectedListPosition(ml), explicitRequest); 305 } 306 307 /* 308 ** Update the displayed list and data to agree with a data list which has 309 ** been changed externally (not by the ManagedList list manager). 310 */ 311 void ChangeManagedListData(Widget listW) 312 { 313 managedListData *ml; 314 315 /* Recover the pointer to the managed list structure from the widget's 316 userData pointer */ 317 XtVaGetValues(listW, XmNuserData, &ml, NULL); 318 319 updateDialogFromList(ml, -1); 320 } 321 322 /* 323 ** Change the selected item in the managed list given the index into the 324 ** list being managed. 325 */ 326 void SelectManagedListItem(Widget listW, int itemIndex) 327 { 328 selectItem(listW, itemIndex, True); 329 } 330 331 /* 332 ** Return the index of the item currently selected in the list 333 */ 334 int ManagedListSelectedIndex(Widget listW) 335 { 336 managedListData *ml; 337 338 XtVaGetValues(listW, XmNuserData, &ml, NULL); 339 return selectedListPosition(ml)-2; 340 } 341 342 /* 343 ** Add a delete-confirmation callback to a managed list. This will be called 344 ** when the user presses the Delete button on the managed list. The callback 345 ** can put up a dialog, and optionally abort the operation by returning False. 346 */ 347 void AddDeleteConfirmCB(Widget listW, int (*deleteConfirmCB)(int, void *), 348 void *deleteConfirmArg) 349 { 350 managedListData *ml; 351 352 XtVaGetValues(listW, XmNuserData, &ml, NULL); 353 ml->deleteConfirmCB = deleteConfirmCB; 354 ml->deleteConfirmArg = deleteConfirmArg; 355 } 356 357 /* 358 ** Called on destruction of the list widget 359 */ 360 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData) 361 { 362 /* Free the managed list data structure */ 363 NEditFree(clientData); 364 } 365 366 /* 367 ** Button callbacks: deleteCB, copyCB, moveUpCB, moveDownCB 368 */ 369 static void deleteCB(Widget w, XtPointer clientData, XtPointer callData) 370 { 371 managedListData *ml = (managedListData *)clientData; 372 int i, ind, listPos; 373 374 /* get the selected list position and the item to be deleted */ 375 listPos = selectedListPosition(ml); 376 ind = listPos-2; 377 378 /* if there's a delete confirmation callback, call it first, and allow 379 it to request that the operation be aborted */ 380 if (ml->deleteConfirmCB != NULL) 381 if (!(*ml->deleteConfirmCB)(ind, ml->deleteConfirmArg)) 382 return; 383 384 /* free the item and remove it from the list */ 385 (*ml->freeItemCB)(ml->itemList[ind]); 386 for (i=ind; i<*ml->nItems-1; i++) 387 ml->itemList[i] = ml->itemList[i+1]; 388 (*ml->nItems)--; 389 390 /* update the list widget and move the selection to the previous item 391 in the list and display the fields appropriate for that entry */ 392 updateDialogFromList(ml, ind-1); 393 } 394 395 static void copyCB(Widget w, XtPointer clientData, XtPointer callData) 396 { 397 managedListData *ml = (managedListData *)clientData; 398 int i, listPos, abort = False; 399 void *item; 400 401 /* get the selected list position and the item to be copied */ 402 listPos = selectedListPosition(ml); 403 if (listPos == 1) 404 return; /* can't copy "new" */ 405 406 if ((*ml->nItems) == ml->maxItems) { 407 DialogF(DF_ERR, shellOfWidget(ml->listW), 1, "Limits exceeded", 408 "Cannot copy item.\nToo many items in list.", 409 "OK"); 410 return; 411 } 412 413 /* Bring the entry up to date (could result in operation being canceled) */ 414 item = (*ml->getDialogDataCB)(ml->itemList[listPos-2], False, &abort, 415 ml->getDialogDataArg); 416 if (abort) 417 return; 418 if (item != NULL) { 419 (*ml->freeItemCB)(ml->itemList[listPos-2]); 420 ml->itemList[listPos-2] = item; 421 } 422 423 /* Make a copy by requesting the data again. 424 In case getDialogDataCB() returned a fallback value, the dialog may 425 not be in sync with the internal list. If we _explicitly_ request the 426 data again, we could get an invalid answer. Therefore, we first update 427 the dialog to make sure that we can copy the right data. */ 428 updateDialogFromList(ml, listPos-2); 429 item = (*ml->getDialogDataCB)(ml->itemList[listPos-2], True, &abort, 430 ml->getDialogDataArg); 431 if (abort) 432 return; 433 434 /* add the item to the item list */ 435 for (i= *ml->nItems; i>=listPos; i--) 436 ml->itemList[i] = ml->itemList[i-1]; 437 ml->itemList[listPos-1] = item; 438 (*ml->nItems)++; 439 440 /* redisplay the list widget and select the new item */ 441 updateDialogFromList(ml, listPos-1); 442 } 443 444 static void moveUpCB(Widget w, XtPointer clientData, XtPointer callData) 445 { 446 managedListData *ml = (managedListData *)clientData; 447 int ind, listPos; 448 void *temp; 449 450 /* get the item index currently selected in the menu item list */ 451 listPos = selectedListPosition(ml); 452 ind = listPos-2; 453 454 /* Bring the item up to date with the dialog fields (It would be better 455 if this could be avoided, because user errors will be flagged here, 456 but it's not worth re-writing everything for such a trivial point) */ 457 if (!incorporateDialogData(ml, ml->lastSelection, False)) 458 return; 459 460 /* shuffle the item up in the menu item list */ 461 temp = ml->itemList[ind]; 462 ml->itemList[ind] = ml->itemList[ind-1]; 463 ml->itemList[ind-1] = temp; 464 465 /* update the list widget and keep the selection on moved item */ 466 updateDialogFromList(ml, ind-1); 467 } 468 469 static void moveDownCB(Widget w, XtPointer clientData, XtPointer callData) 470 { 471 managedListData *ml = (managedListData *)clientData; 472 int ind, listPos; 473 void *temp; 474 475 /* get the item index currently selected in the menu item list */ 476 listPos = selectedListPosition(ml); 477 ind = listPos-2; 478 479 /* Bring the item up to date with the dialog fields (I wish this could 480 be avoided) */ 481 if (!incorporateDialogData(ml, ml->lastSelection, False)) 482 return; 483 484 /* shuffle the item down in the menu item list */ 485 temp = ml->itemList[ind]; 486 ml->itemList[ind] = ml->itemList[ind+1]; 487 ml->itemList[ind+1] = temp; 488 489 /* update the list widget and keep the selection on moved item */ 490 updateDialogFromList(ml, ind+1); 491 } 492 493 /* 494 ** Called when the user clicks on an item in the list widget 495 */ 496 static void listSelectionCB(Widget w, XtPointer clientData, XtPointer callData) 497 { 498 managedListData *ml = (managedListData *)clientData; 499 int ind, listPos = ((XmListCallbackStruct *)callData)->item_position; 500 501 /* Save the current dialog fields before overwriting them. If there's an 502 error, force the user to go back to the old selection and fix it 503 before proceeding */ 504 if (ml->getDialogDataCB != NULL && ml->lastSelection != 0) { 505 if (!incorporateDialogData(ml, ml->lastSelection, False)) { 506 XmListDeselectAllItems(ml->listW); 507 XmListSelectPos(ml->listW, ml->lastSelection, False); 508 return; 509 } 510 /* reselect item because incorporateDialogData can alter selection */ 511 selectItem(ml->listW, listPos-2, False); 512 } 513 ml->lastSelection = listPos; 514 515 /* Dim or un-dim buttons at bottom of dialog based on whether the 516 selected item is a menu entry, or "New" */ 517 if (listPos == 1) { 518 XtSetSensitive(ml->copyBtn, False); 519 XtSetSensitive(ml->deleteBtn, False); 520 XtSetSensitive(ml->moveUpBtn, False); 521 XtSetSensitive(ml->moveDownBtn, False); 522 } else { 523 XtSetSensitive(ml->copyBtn, True); 524 XtSetSensitive(ml->deleteBtn, True); 525 XtSetSensitive(ml->moveUpBtn, listPos != 2); 526 XtSetSensitive(ml->moveDownBtn, listPos != *ml->nItems+1); 527 } 528 529 /* get the index of the item currently selected in the item list */ 530 ind = listPos - 2; 531 532 /* tell the caller to show the new item */ 533 if (ml->setDialogDataCB != NULL) 534 (*ml->setDialogDataCB)(listPos==1 ? NULL : ml->itemList[ind], 535 ml->setDialogDataArg); 536 } 537 538 /* 539 ** Incorporate the current contents of the dialog fields into the list 540 ** being managed, and if necessary change the display in the list widget. 541 ** The data is obtained by calling the getDialogDataCB callback, which 542 ** is allowed to reject whatever request triggered the update. If the 543 ** request is rejected, the return value from this function will be False. 544 */ 545 static int incorporateDialogData(managedListData *ml, int listPos, int explicit) 546 { 547 int abort = False; 548 void *item; 549 550 /* Get the current contents of the dialog fields. Callback will set 551 abort to True if canceled */ 552 item = (*ml->getDialogDataCB)(listPos == 1 ? NULL : ml->itemList[ 553 listPos-2], explicit, &abort, ml->getDialogDataArg); 554 if (abort) 555 return False; 556 if (item == NULL) /* don't modify if fields are empty */ 557 return True; 558 559 /* If the item is "new" add a new entry to the list, otherwise, 560 modify the entry with the text fields from the dialog */ 561 if (listPos == 1) { 562 if ((*ml->nItems) == ml->maxItems) { 563 DialogF(DF_ERR, shellOfWidget(ml->listW), 1, "Limits exceeded", 564 "Cannot add new item.\nToo many items in list.", 565 "OK"); 566 return False; 567 } 568 ml->itemList[(*ml->nItems)++] = item; 569 updateDialogFromList(ml, *ml->nItems - 1); 570 } else { 571 (*ml->freeItemCB)(ml->itemList[listPos-2]); 572 ml->itemList[listPos-2] = item; 573 updateListWidgetItem(ml, listPos); 574 } 575 return True; 576 } 577 578 /* 579 ** Update the list widget to reflect the current contents of the managed item 580 ** list, set the item that should now be highlighted, and call getDisplayed 581 ** on the newly selected item to fill in the dialog fields. 582 */ 583 static void updateDialogFromList(managedListData *ml, int selection) 584 { 585 int i; 586 XmString *stringTable; 587 588 /* On many systems under Motif 1.1 the list widget can't handle items 589 being changed while anything is selected! */ 590 XmListDeselectAllItems(ml->listW); 591 592 /* Fill in the list widget with the names from the item list */ 593 stringTable = (XmString *)NEditMalloc(sizeof(XmString) * (*ml->nItems+1)); 594 stringTable[0] = XmStringCreateSimple("New"); 595 for (i=0; i < *ml->nItems; i++) 596 stringTable[i+1] = XmStringCreateSimple(*(char **)ml->itemList[i]); 597 XtVaSetValues(ml->listW, XmNitems, stringTable, 598 XmNitemCount, *ml->nItems+1, NULL); 599 for (i=0; i < *ml->nItems+1; i++) 600 XmStringFree(stringTable[i]); 601 NEditFree(stringTable); 602 603 /* Select the requested item (indirectly filling in the dialog fields), 604 but don't trigger an update of the last selected item from the current 605 dialog fields */ 606 ml->lastSelection = 0; 607 selectItem(ml->listW, selection, True); 608 } 609 610 /* 611 ** Update one item of the managed list widget to reflect the current contents 612 ** of the managed item list. 613 */ 614 static void updateListWidgetItem(managedListData *ml, int listPos) 615 { 616 int savedPos; 617 XmString newString[1]; 618 619 /* save the current selected position (Motif sometimes does stupid things 620 to the selection when a change is made, like selecting the new item 621 if it matches the name of currently selected one) */ 622 savedPos = selectedListPosition(ml); 623 XmListDeselectAllItems(ml->listW); 624 625 /* update the list */ 626 newString[0] = XmStringCreateSimple(*(char **)ml->itemList[listPos-2]); 627 XmListReplaceItemsPos(ml->listW, newString, 1, listPos); 628 XmStringFree(newString[0]); 629 630 /* restore the selected position */ 631 XmListSelectPos(ml->listW, savedPos, False); 632 } 633 634 /* 635 ** Get the position of the selection in the menu item list widget 636 */ 637 static int selectedListPosition(managedListData *ml) 638 { 639 int listPos; 640 int *posList = NULL, posCount = 0; 641 642 if (!XmListGetSelectedPos(ml->listW, &posList, &posCount)) { 643 fprintf(stderr, "Internal error (nothing selected)"); 644 return 1; 645 } 646 listPos = *posList; 647 NEditFree(posList); 648 if (listPos < 1 || listPos > *ml->nItems+1) { 649 fprintf(stderr, "Internal error (XmList bad value)"); 650 return 1; 651 } 652 return listPos; 653 } 654 655 /* 656 ** Select an item in the list given the list (array) index value. 657 ** If updateDialog is True, trigger a complete dialog update, which 658 ** could potentially reject the change. 659 */ 660 static void selectItem(Widget listW, int itemIndex, int updateDialog) 661 { 662 int topPos, nVisible, selection = itemIndex+2; 663 664 /* Select the item */ 665 XmListDeselectAllItems(listW); 666 XmListSelectPos(listW, selection, updateDialog); 667 668 /* If the selected item is not visible, scroll the list */ 669 XtVaGetValues(listW, XmNtopItemPosition, &topPos, XmNvisibleItemCount, 670 &nVisible, NULL); 671 if (selection < topPos) 672 XmListSetPos(listW, selection); 673 else if (selection >= topPos + nVisible) 674 XmListSetPos(listW, selection - nVisible + 1); 675 } 676 677 static Widget shellOfWidget(Widget w) 678 { 679 while(1) { 680 if (!w) return 0; 681 if (XtIsSubclass(w, shellWidgetClass)) return w; 682 w = XtParent(w); 683 } 684 } 685