[comp.windows.news] scrolling lists in XView - a file dialog

kneller@dufy.mmwb.ucsf.edu (Don Kneller) (06/27/90)

Here seems like an easy project:

	Create a file open dialog that shows the contents of the current
	directory and allows selection of a file or directory. If a
	directory is chosen, show the contents of that directory. If a
	file is chosen, make it the selected file.


I'm trying to use the XView Scrolling List (aka PANEL_LIST panel item)
as documented in section 7.9 of the XView programming manual.

I'm having the following problems with setting the elements in the list:

	1) using xv_set, it is possible to either set a fixed number of
	   strings for the list, or set an indexed string for the list.
	   What I need is a variable number of strings. Something like
	   **argv would have done the trick.

	   To get a variable number of strings, it is necessary to insert
	   one entry for each string. This is painfully slow and causes
	   unnecessary flashing on the screen. Moreover, it is not possible
	   to start with an empty list of entries --- there must be at least
	   one or PANEL_LIST_INSERT dumps core.

		xv_set(thing, PANEL_LIST_STRINGS, "", (char *) NULL, 0);
		xv_set(thing, PANEL_LIST_INSERT, i
			      PANEL_LIST_STRING, i, newvalue,
			      0);


	2) to change the entries, it is not possible to change all of them
	   at once. I would have hoped something like:

		xv_set(thing, PANEL_LIST_STRINGS, (char *) NULL, 0);

	   would have done the trick. Instead it is necessary to delete
	   them one at a time:

		xv_set(thing, PANEL_DELETE_STRING, i, 0);

	   Again, painfully slow and causes unnecessary screen updates.


Surely (you don't mind if I call you "Shirley?") this has been done before.
Any help would be appreciated.

- don

byennaco@suneast.East.Sun.COM (Robert Yennaco - Sun BOS Software) (06/28/90)

I ran across a similar problem in SunView, so I'll describe it in those
terms and leave it to you to convert it to XView.

>	1) using xv_set, it is possible to either set a fixed number of
>	   strings for the list, or set an indexed string for the list.
>	   What I need is a variable number of strings. Something like
>	   **argv would have done the trick.

>	   To get a variable number of strings, it is necessary to insert
>	   one entry for each string. This is painfully slow and causes
>	   unnecessary flashing on the screen. Moreover, it is not possible
>	   to start with an empty list of entries --- there must be at least
>	   one or PANEL_LIST_INSERT dumps core.

>		xv_set(thing, PANEL_LIST_STRINGS, "", (char *) NULL, 0);
>		xv_set(thing, PANEL_LIST_INSERT, i
>			      PANEL_LIST_STRING, i, newvalue,
>			      0);

You can set up an attribute list, and make one call to panel_set().
The disadvantage here is that (at least in SunView) you're limited to
ATTR_STANDARD_SIZE=256 attributes in the list.  Needing 3 per value,
means you can bundle only 80 or so values into one panel_set() call,
keeping in mind that you still need the list NULL-terminator.  To get
rid of screen flickering, keep the item's PANEL_SHOW_ITEM attribute to
FALSE until you've completed list construction, then set the attribute
to TRUE.

This scheme might not suffice (performance-wise) if you're dealing with
a very large list, but it's better than doing each entry one at a time,
and the list can be as long as you want.

	char	*attr[ATTR_STANDARD_SIZE];
	int	num_attr = 0;
	for (i=0; i<n_values; i++)
		attrs[i] = (char *)PANEL_CHOICE_STRING;
		attrs[i+1] = (char *)(i-1)
		attrs[i+2] = newvalue;
		if ((num_attr+6) >= ATTR_STANDARD_SIZE) {
			attrs[num_attr+3] = NULL;
			panel_set(thing, ATTR_LIST, attrs, 0);
			num_attr = 0;
		}
		else
			num_attr += 3;
	}

	/*
         * If any attribute settings still on attribute buffer,
         * then send them now.
         */
        if (num_attr) {
                attrs[num_attr] = NULL;
                panel_set(thing, ATTR_LIST, attrs, 0);
        }


As you state, a better way is to use a string vector argument (**argv)
for PANEL_CHOICE_STRINGS.   I tried this long ago, and I recall getting
it to work (it was lightning-fast!), but now I can't for the life of me
figure out how I did it.  The disadvantage here (at least in SunView)
is that your list cannot exceed ATTR_STANDARD_SIZE=256 string values.


>	2) to change the entries, it is not possible to change all of them
>	   at once. I would have hoped something like:
>
>		xv_set(thing, PANEL_LIST_STRINGS, (char *) NULL, 0);
>
>	   would have done the trick. Instead it is necessary to delete
>	   them one at a time:
>
>		xv_set(thing, PANEL_DELETE_STRING, i, 0);
>
>	   Again, painfully slow and causes unnecessary screen updates.

Try the same scheme as described above.

Good luck!
-Bob

kneller@dufy.mmwb.ucsf.edu (Don Kneller) (06/29/90)

kneller@dufy.mmwb.ucsf.edu (Don Kneller) writes:

>Here seems like an easy project:

>	Create a file open dialog using scrolling lists.

>I'm having the following problems with setting the elements in the list:

>	1) using xv_set, it is possible to either set a fixed number of
>	   strings for the list, or set an indexed string for the list.
>	   What I need is a variable number of strings. Something like
>	   **argv would have done the trick.


Thanks to everyone that responsed to my posting.

It turns out that it is possible to pass argv-style attributes with the
ATTR_LIST attribute and an attribute-value list (avlist) which is
NULL-terminated. If I have strings STRING_1 through STRING_N which
I want to be the strings in the scrolling list, I set up an avlist with
the following:

	argv[0] = PANEL_LIST_STRINGS;
	argv[1] = STRING_1;
	...
	argv[N] = STRING_N;
	argv[N+1] = NULL;		/* NULL-terminate the strings */
	argv[N+2] = NULL;		/* NULL-terminate the avlist */

Setting this scrolling-list's set of strings is done with one call to xv_set:

	xv_set(thing, ATTR_LIST, argv, NULL);

In what I consider an XView bug, if you use this to try to change the
strings to a different set, and the new set has fewer members than the
old set, the extra members from the old set will remain. Thus it is
necessary to delete the extra members. Again, an avlist can be used to
reduce the calls to xv_set().

NOTE: A SunView programmer has told me that there is a limit on the length
of the avlist of about (I think) 250 elements. I don't know if that is the
case with XView.

Here is the routine I use to update a scrolling list:


/*
 * scrolling_list_update:
 *
 *	Given a scrolling list and an argv-style array of strings,
 *	update the contents of the scrolling list.
 */
scrolling_list_update(list, argc, argv)
Xv_opaque	list;
int		argc;
char		**argv;
{
	register int	i, j;
	char		**avlist;
	int		rows = xv_get(list, PANEL_LIST_NROWS);

	/*
	 * Copy the string information to an avlist.
	 */
	if (argc > 0) {

		/*
		 * Allocate memory for the list and fill it in.
		 */
		avlist = (char **) emalloc((argc + 3) * sizeof (char *));
		avlist[0] = (char *) PANEL_LIST_STRINGS;
		for (i = 0; i < argc; i++)
			avlist[i + 1] = argv[i];
		avlist[++i] = (char *) NULL;	/* NULL-terminate strings */
		avlist[++i] = (char *) NULL;	/* NULL-terminate avlist */

		/*
		 * Set the scrolling list and free the avlist.
		 */
		xv_set(list, ATTR_LIST, avlist, NULL);
		free((char *) avlist);
	}


	/*
	 * This should not be necessary, but ... clear any entries
	 * in excess of the new entries.
	 */
	if (rows > argc) {

		/*
		 * Set up an avlist with PANEL_LIST_DELETE,index pairs. The
		 * panel entries are deleted from bottom (high index) to
		 * top (low index) which might reduce copying of entries.
		 */
		avlist = (char **) emalloc(((rows-argc)*2+1) * sizeof (char *));
		for (j = 0, i = rows - 1; i >= argc; i--) {
			avlist[j++] = (char *) PANEL_LIST_DELETE;
			avlist[j++] = (char *) i;
		}
		avlist[j] = (char *) NULL;	/* NULL-terminate avlist */

		/*
		 * Delete the excess entries and free the avlist.
		 */
		xv_set(list, ATTR_LIST, avlist, NULL);
		free((char *) avlist);
	}
}