fellman@celece.ucsd.edu (Ronald Fellman) (07/13/90)
I am trying to dynamically change a submenu but cannot figure out how to get the id for the submenu object that was created by Interface Builder. MenuCells don't seem to keep a record of what menu they are in. Menu objects don't seem to keep any records of submenus of items. I must be overlooping something. Can anyone help?? Thanks, -ron fellman (e-mail : rfellman@ucsd.edu)
bruce@atncpc.UUCP (Bruce Henderson) (07/15/90)
In article <11858@sdcc6.ucsd.edu>, fellman@celece.ucsd.edu (Ronald Fellman) writes: > I am trying to dynamically change a submenu but cannot figure out how to > get the id for the submenu object that was created by Interface Builder. > MenuCells don't seem to keep a record of what menu they are in. Menu > objects don't seem to keep any records of submenus of items. I must be > overlooping something. Can anyone help?? > > Thanks, > -ron fellman > (e-mail : rfellman@ucsd.edu)
bruce@atncpc.UUCP (Bruce Henderson) (07/15/90)
Sorry about the previous mail-blunder!
here is the naughty code segment
Ron,
Here is what I do:
in my -appDidInit method, I have a line that looks like this:
[self InitMainMenu:[NXApp mainMenu]];
This is a method that walks through the menu structure and allows me to grab
various menu ID's that I find interesting, cache them, and set individual Cell's
updateAction so that I can enable them and disable them when approprate to
the program's context.
It looks alot like this: (much of this code comes from Bruce Blumberg's DrawApp,
a wonderful guide on how to do lots of neat thigs.....)
/* these instance varibles are mentioned in the code fragment, but they are */
/* never defined, they belong to the application subClass that these methods */
/* belong to.... They hold the id's of the 3 submenus we want to do things to */
id submn1;
id submn2;
id submn3;
/* these are cell tags that I have set up in Interface Builder */
/* they correspond to items in the main menu that have sub */
/* menus attached to them */
#define SubMenu1 1000
#define SubMenu2 2000
#define SubMenu3 3000
- initMainMenu: menu
/*
* Sets the updateAction for every menu item which sends to the
* First Responder (i.e. their target is nil). When autoupdate is on,
* every event will be followed by an update of each of the menu items
* which is visible. This keep all unavailable menu items dimmed out
* so that the user knows what options are available at any given time.
*/
{
int count;
int celltag;
const char *s;
id matrix, cell;
id matrixTarget, cellTarget;
id activateMenu = nil;
/* retreives the main menu's Matrix */
matrix = [menu itemList];
matrixTarget = [matrix target];
/* walls therough the menu cells in the main menu */
/* the cells that have sub menus have been modified */
/* in Interface Builder to have cell tags that I have defined */
/* above, These will be used to tell if we have on of the */
/* menus we want to cache */
count = [matrix cellCount];
while (count--)
{
cell = [matrix cellAt:count :0];
cellTarget = [cell target];
celltag = [cell tag];
if (!matrixTarget && !cellTarget)
{/* means that the cell doesn't have a submenu */
[cell setUpdateAction:@selector(mainMenuUpdate:) forMenu:menu];
}
else
if ([cell hasSubmenu])
{
switch(celltag) /* tells us which submenu to deal with */
{
case SubMenu1:
/* dispatch to a method to do */
/* initalization to the 1st sub- */
/* menu, store the menu id in */
/* an instance variable */
submn1 = [self initSubMenu1 :cellTarget];
break;
case SubMenu2:
submn2 = [self initSubMenu2 :cellTarget];
break;
case SubMenu3:
submn3 = [self initSubMenu3 :cellTarget];
break;
}
}
}
return self;
}
/* the next routine is an example of a method that sets up one of the sub menus, note the */
/* the setting of the menu cell's updateAction: to a selector of an application method */
/* the menu cell will call this method, passing in itself (id) whenever it thinks it might */
/* need to be changed (enabled, disabled, change of text, etc) This is MEGA handy! */
- initSubMenu1: menu
{
int count;
int celltag;
const char *s;
id matrix, cell;
matrix = [menu itemList];
matrixTarget = [matrix target];
count = [matrix cellCount];
while (count--)
{
cell = [matrix cellAt:count :0];
[cell setUpdateAction:@selector(subMenu1Update:) forMenu:menu];
}
return menu
}
/* now, still another application method. This is an example of an update method */
/* we are going to interigate the firstResponder to see if it know how to respond to */
/* the method we might want to pass it. We are including the #defs for the menu */
/* cell tags.... */
#define menuItem1 100
#define menuItem2 101
#define menuItem3 102
#define menuItem4 104
- (BOOL)subMenu1Update: menuCell
/*
* Method called by all menu items which send their actions to the
* First Responder. First, if the object which would respond were the
* action sent down the responder chain also responds to the message
* validateCommand:, then it is sent validateCommand: to determine
* whether that command is valid now, otherwise, if there is a responder
* to the message, then it is assumed that the item is valid.
* The method returns YES if the cell has changed its appearance (so that
* the caller (a Menu) knows to redraw it).
*/
{
SEL action;
id responder, target;
BOOL enable;
target = [menuCell target];
enable = [menuCell isEnabled];
if (!target)
{
action = [menuCell action];
responder = [self calcTargetForAction:action];
/* note, when you are unsure about the class of the target of a method */
/* such as we are here, it is wise to ask the possible resonder if it can */
/* take the method you are about to pass it. This is one of the beautiful */
/* things about Objective C, because we have no idea what class this */
/* validateCommand: may end up in.... */
if ([responder respondsTo:@selector(validateCommand:)])
{ /* ask the first responder if this command is approprate at this time */
enable = [responder validateCommand:menuCell];
}
else
{ /* does the responder have a (whatever) method ?: */
enable = responder ? YES : NO;
}
}
if ([menuCell isEnabled] != enable)
{ /* we changed the appearance. Update the display */
[menuCell setEnabled:enable];
return YES;
}
else
{ /* menu cell does not need to change appearance */
return NO;
}
}
Now, enabling and disabling are not the only things you can do in one of these
update methods. Take for instance this one:
- (BOOL)subMenu1Update: menuCell
{
SEL action;
id responder, target;
BOOL enable;
BOOL targState;
BOOL isAResponder;
BOOL changeName;
int celltag;
char* celltitle;
char targName[26];
enable = [menuCell isEnabled];
action = [menuCell action];
responder = [self calcTargetForAction:action];
celltitle = (char*)[menuCell title];
celltag = [menuCell tag];
isAResponder = (responder != NULL);
switch(celltag)
{
case menuItem1:
/* change a cell title... */
if( canDoItem1() )
strcpy( targName, "Bake A Potato" );
else
strcpy( targName, "Mash A Potato" );
targState = strcmp( targName, celltitle );
break;
case menuItem2:
{
/* enable if there is an open window that responds to */
/* this cell's action */
if(enable != isAResponder)
{
[menuCell setEnabled:isASheet];
return YES;
}
else
return NO;
}
break;
case menuItem3:
/* change title and enable/disable... */
if((canDoItem3())&&(enable != isAResponder))
{
if(!enable)
[menuCell setEnabled:TRUE];
if(item3State1())
sprintf(targName,"Idaho Potatoes");
else
if(! item3State2())
sprintf(targName,"Georga Potatoes");
else
sprintf(targName,"Irish Potatoes");
targState = (strcmp(targName, celltitle))||(!enable);
}
else
if(enable)
{
[menuCell setEnabled:FALSE];
return YES;
}
else return NO;
break;
default:
{
if(enable != isAResponder)
{
[menuCell setEnabled:isASheet];
return YES;
}
else
return NO;
}
break;
}
if (targState)
{
[menuCell setTitle:targName]; /* set the title here */
targState = 0;
return YES;
}
else
{
return NO;
}
}
Got it??
Any Questions?
Bruce Henderson
Ashton-Tate NeXTeam
User Interface KGB