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