Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
Hey all, I'm back again with a few cocoa questions I've yet to find the answers to on Google. To the experienced programmer these should be a piece of cake. :)

1) In a Document-based app, how do you connect menu items to the methods? I have an IPController class, instantiated in both MyDocument.nib and MainMenu.nib. I want to connect a menu item to the class so it can show a help panel existing in MyDocument.nib. Is this possible, or do I have to have an duplicate instance to the same panel in both NIBs?

(The "Help..." menu should open it from MainMenu, and there's a button on a MyDocument window that opens it too.)

2) Toolbar items. Lets say I have a method in IPController.m that opens a sheet. There's an NSButton on the window itself whose action is connected to the method in said IPController class. Now, for the toolbar to work, all the code for it is in MyDocument.m. Problem is connecting the action of the toolbar item to the method in IPController. Any ideas?

Right now I have the action set to "-(void)blahHelper()" which tells an instance of IPController to performSelector(). This doesn't work though. The original method in IPController opens a new sheet, but when I call the method through the toolbar item, I get a "Model session requires modal window" problem.

Thanks for the read and the help, I hope I made this easy enough to understand. :)
 

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
Ah, and I almost forgot a third problem I ran into:

3) There's a simple NSSearchField in my toolbar, and the only search predicate it is registered for is "Task Name." Problem is, this isn't activated by default. So when you type something into the bar initially, nothing happens. You have to pull the menu down from the magnifying class and select "Task Name" before it will search correctly. Any way to programmatically set this? I couldn't find anything in IB, but my previous search field I used that wasn't in a toolbar did it automatically. :eek:
 

Eraserhead

macrumors G4
Nov 3, 2005
10,434
12,250
UK
Hey all, I'm back again with a few cocoa questions I've yet to find the answers to on Google. To the experienced programmer these should be a piece of cake. :)

1) In a Document-based app, how do you connect menu items to the methods? I have an IPController class, instantiated in both MyDocument.nib and MainMenu.nib. I want to connect a menu item to the class so it can show a help panel existing in MyDocument.nib. Is this possible, or do I have to have an duplicate instance to the same panel in both NIBs?

I do it like this:
Code:
-(IBAction)actionName:(id)sender{
	MyDocument *currentDocument=(MyDocument *)[[NSDocumentController sharedDocumentController] currentDocument];
	if(currentDocument==nil){
		currentDocument=(MyDocument *)[[NSApp orderedDocuments] objectAtIndex:0];
		if(currentDocument==nil){
			NSLog(@"currentDocument playing up.");
			NSBeep();
			return;
		}
	}
	[currentDocument theActionIWantToRun];
}

The other two, I have no idea.
 

Nutter

macrumors 6502
Mar 31, 2005
432
0
London, England
1) In a Document-based app, how do you connect menu items to the methods? I have an IPController class, instantiated in both MyDocument.nib and MainMenu.nib. I want to connect a menu item to the class so it can show a help panel existing in MyDocument.nib. Is this possible, or do I have to have an duplicate instance to the same panel in both NIBs?

First of all, why do you have two instances of this IPController class? If the only reason is so that you can make connections in your NIBs, it's almost certainly better to set up connections between objects in different NIBs programmatically, using the File's Owner object as the entry point to the graph of objects in your NIB.

To answer your question, I would implement the action method for opening the panel in your NSDocument subclass. Then, just connect the menu item to the "First Responder" proxy in MainMenu.nib, adding an action name that matches the name of your NSDocument subclass' action method.

The great thing about doing it this way is that Cocoa will automatically enable and disable the menu depending on whether or not a document is the main/key window.

The responder chain is a wonderful thing. You can read more about it here:
http://developer.apple.com/document.../CoreAppArchitecture/chapter_7_section_6.html
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
First of all, why do you have two instances of this IPController class? If the only reason is so that you can make connections in your NIBs, it's almost certainly better to set up connections between objects in different NIBs programmatically, using the File's Owner object as the entry point to the graph of objects in your NIB.

To answer your question, I would implement the action method for opening the panel in your NSDocument subclass. Then, just connect the menu item to the "First Responder" proxy in MainMenu.nib, adding an action name that matches the name of your NSDocument subclass' action method.

The great thing about doing it this way is that Cocoa will automatically enable and disable the menu depending on whether or not a document is the main/key window.

The responder chain is a wonderful thing. You can read more about it here:
http://developer.apple.com/document.../CoreAppArchitecture/chapter_7_section_6.html

This is the correct solution. I was going to wait till I was home to post something similar but you've saved me the trouble which is ace :D

I did an ultra-simple demo of this that I posted here in the past to show how it worked. I wonder if it's still around...


Edit to add: it's in from ages back that basically asked the same thing in a different way...
 

Eraserhead

macrumors G4
Nov 3, 2005
10,434
12,250
UK
This is the correct solution. I was going to wait till I was home to post something similar but you've saved me the trouble which is ace :D

So my way is wrong/worse :eek:, oh well it's better that way in my application as my enable/disable requirements are a little more complex as I want to disable most actions when a sheet is displayed.
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
So my way is wrong/worse :eek:, oh well it's better that way in my application as my enable/disable requirements are a little more complex as I want to disable most actions when a sheet is displayed.

If it works the it's not necessarily wrong as such. This just saves having extra actions to simply forward the message to another object, saves working out the document object as the system can do it and: more importantly would automatically disable the actions when a sheet was showing :p
 

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
Ah, thanks so much! That's much easier than all the ways I was trying to go about it.

One problem though, after the connections, the menu items are permanently disabled. So while it would probably work, I can't click on the little buggers! :rolleyes:
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
Ah, thanks so much! That's much easier than all the ways I was trying to go about it.

One problem though, after the connections, the menu items are permanently disabled. So while it would probably work, I can't click on the little buggers! :rolleyes:

If they are disabled then Cocoa cannot find a method with the correct signature anywhere in the responder chain. Can you post your code (zip the project without any generated files)?
 

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
If they are disabled then Cocoa cannot find a method with the correct signature anywhere in the responder chain. Can you post your code (zip the project without any generated files)?
Gah, would if I could, the project directory zipped is a little over 3 megs :eek:

Final build project zipped is only 300k, so I'm not too scared yet! Just to make sure I've got this right though:

1) I have a method in MyDocument.m called openHelpWindow:, and is an IBAction.
2) In MainMenu.nib, I added an action to First Responder called "openHelpWindow:" and connected the menu item to that action.

Wah-lah? :confused:

Edit: The connections work right, I just connected a button on MyDocument.nib to its First Responder with the same method, openHelpWindow:, and it worked fine. So the problem is just that the menu item's aren't correctly being enabled.
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
Just the code (not all the intermediate build files and so on) is 3Mb? :eek:

Do you have anything else that might be setting the enabled status of the menus? A validateMenuItem method for example?
 

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
Just the code (not all the intermediate build files and so on) is 3Mb? :eek:

Do you have anything else that might be setting the enabled status of the menus? A validateMenuItem method for example?
Here we go, turns out I accidentally copied a few frameworks in there when linking.
 

Attachments

  • iProcrastinate copy.zip
    209.9 KB · Views: 102

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
OK I'm confused.

In MyDocument.m there is a method called - (IBAction)openHelpWindow:(id)sender. In MainMenu.nib the FirstResponder has an action called openHelpPanel: but not openHelpWindow: So the action has not been connected.

I also cannot work out which menu item you think is sending an openHelpMenu: action?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
Also I can't build as it appears that your project expects the iLifeControls framework to be in /System/Library/Frameworks. That's exceptionally bad. Nothing should be adding stuff to there. The Framework should be self contained within the app.
 

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
Ah, got the menu items working! You were right, I had changed the method names around in a fit of frustration, and forgot to change First Responder. :D

As for the framework issue, I'm still a bit confused on how the build phases work. Lets say I have two frameworks, DiscRecording.framework, stored in /Sys/Lib/Frameworks/ and iLifeControls.framework, on my desktop.

Do I drag each one into the Linked Frameworks folder in my app source list? Do I copy them into the destination group's folder? Do I need to create a new "copy files" or "link binary with library" build phase? There just seem to be way too many ways to configure these little guys. ;)
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
7
As for the framework issue, I'm still a bit confused on how the build phases work. Lets say I have two frameworks, DiscRecording.framework, stored in /Sys/Lib/Frameworks/ and iLifeControls.framework, on my desktop.

Do I drag each one into the Linked Frameworks folder in my app source list? Do I copy them into the destination group's folder? Do I need to create a new "copy files" or "link binary with library" build phase? There just seem to be way too many ways to configure these little guys. ;)

If it's a System framework, you drag it in, but do not copy it over and do not create a copy files build phase. If it's a custom framework, you usually copy it into your project folder and you always create a copy files build phase.
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
DiscRecording.framework is built in so it is part of the System. Therefore it gets to live in /System

iLifeControls is not. It should be built for embedding and the linked via the Linked Frameworks AND copied in a Copy Files phase.

There's a video tutorial
 

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
If it's a System framework, you drag it in, but do not copy it over and do not create a copy files build phase. If it's a custom framework, you usually copy it into your project folder and you always create a copy files build phase.

DiscRecording.framework is built in so it is part of the System. Therefore it gets to live in /System

iLifeControls is not. It should be built for embedding and the linked via the Linked Frameworks AND copied in a Copy Files phase.

There's a video tutorial
Thanks so much guys, I think I've got it figured out now. :D

I guess the last thing I'm having trouble with is connecting two different classes. Simple example: Programmatically (in MyDocument.m) setting the action of a toolbar item to a method in IPController.m. :) I've tried performSelector, but it doesn't quite work the same. (For example, the IPController method opens a sheet, but when called within MyDocument, results in a "modal window session" type error.)
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
7
I guess the last thing I'm having trouble with is connecting two different classes. Simple example: Programmatically (in MyDocument.m) setting the action of a toolbar item to a method in IPController.m. :) I've tried performSelector, but it doesn't quite work the same. (For example, the IPController method opens a sheet, but when called within MyDocument, results in a "modal window session" type error.)

If I have this right, each MyDocument has an instance of IPController, correct? Why not just create in MyDocument an IBOutlet for IPController, and connect that up in the nib, since you instantiate IPController in the nib anyways. Then in MyDocument.m you can set your toolbar items' targets to controller, and use setAction: to set a selector that controller responds to. Makes sense? :)

Also, it is best to have one window/view per nib if you can. Then you're not wasting memory/speed loading all those objects into memory. With a different nib for each window/view, that nib only gets loaded when it's needed. For example, the Preferences isn't called very often, so putting that in a separate nib causes that nib to only load when you open the Preferences the first time.
 

Littleodie914

macrumors 68000
Original poster
Jun 9, 2004
1,813
8
Rochester, NY
If I have this right, each MyDocument has an instance of IPController, correct? Why not just create in MyDocument an IBOutlet for IPController, and connect that up in the nib, since you instantiate IPController in the nib anyways. Then in MyDocument.m you can set your toolbar items' targets to controller, and use setAction: to set a selector that controller responds to. Makes sense? :)

Also, it is best to have one window/view per nib if you can. Then you're not wasting memory/speed loading all those objects into memory. With a different nib for each window/view, that nib only gets loaded when it's needed. For example, the Preferences isn't called very often, so putting that in a separate nib causes that nib to only load when you open the Preferences the first time.
Ah great, thanks for the tip with the NIBs, I had no idea.

And I'll give the IPController outlet a shot, I don't know why I didn't think of that before, it seems like the most logical way to do it! :)
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.