[comp.windows.x] XView menu-generating procedures

denault@hale.ifa.hawaii.edu (Tony Denault) (03/22/90)

Has anyone used the menu-generating procedures in XView. I want 
to create a menu of filenames when the user selects a menu item. 
I've gotten it to work, but its very unstable.

It work when using the ACTION_MENU to move down the hierarchy of 
menus, but crashes when  the user's selects the pullright menu's
parent with the ACTION_SELECT button. 

I get the following warning messages when trying to destroy the pullright 
menu object:

XView warning: obj 0x3bdc8 invalid object (embedding seal incorrect) 
   xv_destroy_status

Since the menu_dir2.c example in O'Reilly's XView Programming 
Manual behave in a similar fashion, I suspect it a bug in the 
XView programming kit.

I'm using OpenWindows 1.0 on the a Sparc-1.

If your interested, here is a test program using pullright menus:

/*  Example program starts with this line   */
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <xview/xview.h>
#include <xview/frame.h>
#include <xview/panel.h>

Frame frame;

Xv_opaque dummy_menu_nproc( );
Menu gen_execute_menu( );
void command_execute_file_menu_nproc( );
void command_execute_menu_item_nproc( );

main( argc, argv)
int argc;
char * argv[];
{
   Panel panel;
   Menu  command_menu;
   void quit();

   xv_init( XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);

   frame = (Frame) xv_create( NULL, FRAME,
      FRAME_LABEL,      argv[0],
      XV_WIDTH,         200,
      XV_HEIGHT,        100,
      NULL);

   command_menu = (Menu) xv_create( NULL, MENU,
      MENU_ITEM,
         MENU_STRING,         "Execute",
         MENU_NOTIFY_PROC,    command_execute_menu_item_nproc,
         MENU_GEN_PULLRIGHT,  gen_execute_menu,
         NULL,
      MENU_ACTION_ITEM,    "Edit",             dummy_menu_nproc,
      MENU_ACTION_ITEM,    "Command line",     dummy_menu_nproc,
      NULL);

   panel = (Panel) xv_create (frame, PANEL, NULL);

   (void) xv_create( panel, PANEL_BUTTON,
      PANEL_LABEL_STRING,  "Commands",
      PANEL_ITEM_MENU,     command_menu,
      NULL);
 
   (void) xv_create( panel, PANEL_BUTTON,
      PANEL_LABEL_STRING,  "Quit",
      PANEL_NOTIFY_PROC,   quit,
      NULL);

   xv_main_loop( frame );
   exit(0);
}

void quit()
{
   xv_destroy_safe(frame);
}

/*
**  dummy_menu_nproc()
*/
Xv_opaque dummy_menu_nproc( item, event )
   Panel_item   item;
   Event         *event;
{
   printf("dummy_memu_nproc()\n");
}

/******************************************************************************/

Menu gen_execute_menu( menu_item, op )
   Menu_item      menu_item;
   Menu_generate    op;
{
   Menu   menu;
   int    cnt;
   DIR * dirp;
   struct dirent * dp;

   /*  Destroy any previous menu owned by menu item */
   if( menu = (Menu) xv_get( menu_item, MENU_PULLRIGHT) )
      xv_destroy(menu);

   if( op == MENU_DISPLAY )  /* Create a new menu */
   {
      /*  Create Menu */
      menu = (Menu) xv_create(NULL, MENU, NULL);

      /*  Find the menu items and append them to the menu */
      cnt = 0;
      if( NULL != (dirp = opendir(".")) )
         for( dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
            {
            cnt++;
            menu_item = xv_create( NULL, MENUITEM,
               MENU_STRING,      strcpy( malloc(strlen(dp->d_name)+1), dp->d_name),
               MENU_NOTIFY_PROC,   command_execute_file_menu_nproc,
               MENU_RELEASE,
               NULL);
            xv_set( menu, MENU_APPEND_ITEM, menu_item, NULL);
         }
      /*  Make dummy menu item if no files are found */
      if( cnt == 0 )
      {
         menu_item = (Menu_item) xv_create( NULL, MENUITEM,
            MENU_STRING,   "No Command Files Available",
            NULL);
         xv_set( menu, MENU_APPEND_ITEM, menu_item, NULL);
      }
   }
   else if( !(menu == (Menu) xv_get( menu_item, MENU_PULLRIGHT)))
   /* Always insure the menu item has a menu assoicated with it */
   {
      menu = (Menu) xv_create( NULL, MENU,
         MENU_STRINGS, "Couldn't build a menu.", NULL,
         NULL);
   }
   return menu;
}

void command_execute_file_menu_nproc( menu, menu_item )
   Menu          menu;
   Menu_item   menu_item;
{
   char filename[40];

   strncpy( filename, (char *) xv_get( menu_item, MENU_STRING), sizeof(filename) );
   printf("command_execute_file_menu_nproc() - filename is [%s]\n", filename);
}

void command_execute_menu_item_nproc( menu, menu_item )
   Menu          menu;
   Menu_item   menu_item;
{
   xv_set( (Menu) xv_get( menu_item, MENU_PULLRIGHT ), XV_SHOW, TRUE, NULL);
}

/*  Last line of example program */
----------------------------------------------------------------------------
| Tony Denault                            | denault@hale.ifa.hawaii.edu
| Institute for Astronomy, UH             |
| 2680 Woodlawn Drive, Honolulu, HI 96789 |
----------------------------------------------------------------------------
--
----------------------------------------------------------------------------
| Tony Denault                            | denault@hale.ifa.hawaii.edu
| Institute for Astronomy, UH             |
| 2680 Woodlawn Drive, Honolulu, HI 96789 |

argv%turnpike@Sun.COM (Dan Heller) (03/22/90)

In article <7059@uhccux.uhcc.hawaii.edu> denault@hale.ifa.hawaii.edu (Tony Denault) writes:
> Has anyone used the menu-generating procedures in XView. I want 
> to create a menu of filenames when the user selects a menu item. 
> I've gotten it to work, but its very unstable.
... 
> I get the following warning messages when trying to destroy the pullright 
> menu object:

> XView warning: obj 0x3bdc8 invalid object (embedding seal incorrect) 
>    xv_destroy_status
> 
> Since the menu_dir2.c example in O'Reilly's XView Programming 
> Manual behave in a similar fashion, I suspect it a bug in the 
> XView programming kit.

It is a bug in the toolkit.  Unfortunately, I expected it to be
fixed by the time the manual was printed.  In a discussion with
the engineer responsible for the menu package at Sun, he acknowledges
the bug, but is hesitant to change it because it is backwards
compatible with SunView.  He suggested I "query the net" and
see if there are any sunview programs out there who really depend
on this bug which would set precedent for not fixing it in XView.

Let me be more precise on the bug itself and then I'll address
your specific problem.

When you specify a menu generating procedure, it will be called either
2 or 4 times, depending on whether or not the user made a menu
selection.

The first time the generating procedure is called, it is called
with MENU_DISPLAY (indicating that the menu is about to be displayed
so you should generate the menu).  When the user dismisses the menu,
the generating procedure is called with MENU_DISPLAY_DONE.
If the user made a menu selection, then the procedure is called
with MENU_NOTIFY *before* the menu item's callback procedure is
called.  Then after it is called, the menu gen procedure is called
*again* with MENU_NOTIFY_DONE.

So, all in all, the menu notify routine may be called four times.
However, if the user doesn't make a menu selection, then it's only
called twice.  The question is, at which time do you destroy the
menu you created!?  Right?  Well, here is the bug...

The sequence of calls is:
    1) MENU_DISPLAY
    2) MENU_DISPLAY_DONE
    3) MENU_NOTIFY
    4) MENU_NOTIFY_DONE
Here, you would destroy the menu at stage 4 since it is the
last one called.  However, if the user did not make a selection,
then items 3 and 4 are never called, so you would destroy the
menu at step 2.  The problem is, it is impossible to determine if
the user made a selection or not at step 2, so you can't make
the decision to destroy the menu.

The bug *fix* is to arrange the calling sequence like this:
    1) MENU_DISPLAY
    2) MENU_NOTIFY
    3) MENU_NOTIFY_DONE
    4) MENU_DISPLAY_DONE
That way, if the user doesn't make a selection, then steps 2 and 3
are not called, but it doesn't matter.  In this case, you always
destroy the menu in step 4.  This is what I assumed was going to
happen when I wrote the menus chapter.  So, currently, this is
an open ended issue since the word is that the bug won't be fixed
unless we're sure that sunview programs won't break when ported
to XView.

The only way this type of philosophy can change, apparently, is
via feedback from the net --- do you place that much value on
sunview compatibility that even bugs should be compatible?

To answer your question specifically..
Here is your problem:

> Menu gen_execute_menu( menu_item, op )
>    Menu_item      menu_item;
>    Menu_generate    op;
> {
>    Menu   menu;
>    int    cnt;
>    DIR * dirp;
>    struct dirent * dp;
> 
>    /*  Destroy any previous menu owned by menu item */
>    if( menu = (Menu) xv_get( menu_item, MENU_PULLRIGHT) )
>       xv_destroy(menu);
Don't do this -- never destroy the menu until you have to
recreate the *same* menu!  This is how menu_dir2.c avoids
the bug, altho I didn't stress this fact enough (altho I
have for the next edition of the book).

This workaround may seem wasteful (and it is a little), but
your worst case is that you'll have at most 2 copies of the
same cascading menu.

dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.