Amendment to prev commit: We can distinguish these two scenarios by the return
[nedit.git] / util / managedList.c
blobe0d126d156b41502d34f77ff1691caba57c9c404
1 static const char CVSID[] = "$Id: managedList.c,v 1.15 2006/08/08 10:59:32 edg Exp $";
2 /*******************************************************************************
3 * *
4 * managedList.c -- User interface for reorderable list of records *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * November, 1995 *
25 * *
26 * Written by Mark Edel *
27 * *
28 *******************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
34 #include "managedList.h"
35 #include "misc.h"
36 #include "DialogF.h"
38 #include <stdio.h>
39 #include <string.h>
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>
47 #ifdef HAVE_DEBUG_H
48 #include "../debug.h"
49 #endif
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;
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);
83 ** Create a user interface to help manage a list of arbitrary data records
84 ** which users can edit, add, delete, and reorder.
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.
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.
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.
99 ** See ManageListAndButtons for a description of the remaining arguments.
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 *))
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;
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);
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 = XtMalloc(nColumns+1);
138 memset(placeholderStr, 'm', nColumns);
139 placeholderStr[nColumns] = '\0';
140 placeholderTable = StringTable(1, placeholderStr);
141 XtFree(placeholderStr);
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);
158 return ManageListAndButtons(listW, deleteBtn, copyBtn, moveUpBtn,
159 moveDownBtn, itemList, nItems, maxItems, getDialogDataCB,
160 getDialogDataArg, setDialogDataCB, setDialogDataArg, freeItemCB);
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.
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.
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:
184 ** void setDialogDataCB(void *item, void *cbArg)
186 ** item: a pointer to the record to be displayed
188 ** cbArg: an arbitrary argument passed on to the callback routine
190 ** The callback "setDialogDataCB" must allocate (with XtMalloc) 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:
202 ** void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
203 ** void *cbArg)
205 ** oldItem: a pointer to the existing record being modified in the
206 ** dialog, or NULL, if the user is modifying the "New" item.
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).
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.
221 ** cbArg: arbitrary data, passed on from where the callback was
222 ** established in the list creation routines
224 ** The return value should be an allocated
226 ** The callback "freeItemCB" should free the item passed to it:
228 ** void freeItemCB(void *item, void *cbArg)
230 ** item: a pointer to the record to be freed
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.
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 *))
245 managedListData *ml;
247 /* Create a managedList data structure to hold information about the
248 widgets, callbacks, and current state of the list */
249 ml = (managedListData *)XtMalloc(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;
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);
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);
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;
285 return ml->listW;
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).
295 int UpdateManagedList(Widget listW, int explicitRequest)
297 managedListData *ml;
299 /* Recover the pointer to the managed list structure from the widget's
300 userData pointer */
301 XtVaGetValues(listW, XmNuserData, &ml, NULL);
303 /* Make the update */
304 return incorporateDialogData(ml, selectedListPosition(ml), explicitRequest);
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).
311 void ChangeManagedListData(Widget listW)
313 managedListData *ml;
315 /* Recover the pointer to the managed list structure from the widget's
316 userData pointer */
317 XtVaGetValues(listW, XmNuserData, &ml, NULL);
319 updateDialogFromList(ml, -1);
323 ** Change the selected item in the managed list given the index into the
324 ** list being managed.
326 void SelectManagedListItem(Widget listW, int itemIndex)
328 selectItem(listW, itemIndex, True);
332 ** Return the index of the item currently selected in the list
334 int ManagedListSelectedIndex(Widget listW)
336 managedListData *ml;
338 XtVaGetValues(listW, XmNuserData, &ml, NULL);
339 return selectedListPosition(ml)-2;
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.
347 void AddDeleteConfirmCB(Widget listW, int (*deleteConfirmCB)(int, void *),
348 void *deleteConfirmArg)
350 managedListData *ml;
352 XtVaGetValues(listW, XmNuserData, &ml, NULL);
353 ml->deleteConfirmCB = deleteConfirmCB;
354 ml->deleteConfirmArg = deleteConfirmArg;
358 ** Called on destruction of the list widget
360 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
362 /* Free the managed list data structure */
363 XtFree((char *)clientData);
367 ** Button callbacks: deleteCB, copyCB, moveUpCB, moveDownCB
369 static void deleteCB(Widget w, XtPointer clientData, XtPointer callData)
371 managedListData *ml = (managedListData *)clientData;
372 int i, ind, listPos;
374 /* get the selected list position and the item to be deleted */
375 listPos = selectedListPosition(ml);
376 ind = listPos-2;
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;
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)--;
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);
395 static void copyCB(Widget w, XtPointer clientData, XtPointer callData)
397 managedListData *ml = (managedListData *)clientData;
398 int i, listPos, abort = False;
399 void *item;
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" */
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;
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;
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;
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)++;
440 /* redisplay the list widget and select the new item */
441 updateDialogFromList(ml, listPos-1);
444 static void moveUpCB(Widget w, XtPointer clientData, XtPointer callData)
446 managedListData *ml = (managedListData *)clientData;
447 int ind, listPos;
448 void *temp;
450 /* get the item index currently selected in the menu item list */
451 listPos = selectedListPosition(ml);
452 ind = listPos-2;
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;
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;
465 /* update the list widget and keep the selection on moved item */
466 updateDialogFromList(ml, ind-1);
469 static void moveDownCB(Widget w, XtPointer clientData, XtPointer callData)
471 managedListData *ml = (managedListData *)clientData;
472 int ind, listPos;
473 void *temp;
475 /* get the item index currently selected in the menu item list */
476 listPos = selectedListPosition(ml);
477 ind = listPos-2;
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;
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;
489 /* update the list widget and keep the selection on moved item */
490 updateDialogFromList(ml, ind+1);
494 ** Called when the user clicks on an item in the list widget
496 static void listSelectionCB(Widget w, XtPointer clientData, XtPointer callData)
498 managedListData *ml = (managedListData *)clientData;
499 int ind, listPos = ((XmListCallbackStruct *)callData)->item_position;
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;
510 /* reselect item because incorporateDialogData can alter selection */
511 selectItem(ml->listW, listPos-2, False);
513 ml->lastSelection = listPos;
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);
529 /* get the index of the item currently selected in the item list */
530 ind = listPos - 2;
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);
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.
545 static int incorporateDialogData(managedListData *ml, int listPos, int explicit)
547 int abort = False;
548 void *item;
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;
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;
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);
575 return True;
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.
583 static void updateDialogFromList(managedListData *ml, int selection)
585 int i;
586 XmString *stringTable;
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);
592 /* Fill in the list widget with the names from the item list */
593 stringTable = (XmString *)XtMalloc(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 XtFree((char *)stringTable);
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);
611 ** Update one item of the managed list widget to reflect the current contents
612 ** of the managed item list.
614 static void updateListWidgetItem(managedListData *ml, int listPos)
616 int savedPos;
617 XmString newString[1];
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);
625 /* update the list */
626 newString[0] = XmStringCreateSimple(*(char **)ml->itemList[listPos-2]);
627 XmListReplaceItemsPos(ml->listW, newString, 1, listPos);
628 XmStringFree(newString[0]);
630 /* restore the selected position */
631 XmListSelectPos(ml->listW, savedPos, False);
635 ** Get the position of the selection in the menu item list widget
637 static int selectedListPosition(managedListData *ml)
639 int listPos;
640 int *posList = NULL, posCount = 0;
642 if (!XmListGetSelectedPos(ml->listW, &posList, &posCount)) {
643 fprintf(stderr, "Internal error (nothing selected)");
644 return 1;
646 listPos = *posList;
647 XtFree((char *)posList);
648 if (listPos < 1 || listPos > *ml->nItems+1) {
649 fprintf(stderr, "Internal error (XmList bad value)");
650 return 1;
652 return listPos;
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.
660 static void selectItem(Widget listW, int itemIndex, int updateDialog)
662 int topPos, nVisible, selection = itemIndex+2;
664 /* Select the item */
665 XmListDeselectAllItems(listW);
666 XmListSelectPos(listW, selection, updateDialog);
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);
677 static Widget shellOfWidget(Widget w)
679 while(1) {
680 if (!w) return 0;
681 if (XtIsSubclass(w, shellWidgetClass)) return w;
682 w = XtParent(w);