Re: Prevent Asynchronous operation of beginSheetModalForWindow
(1) warning: no doSheetSelection:contextInfo method found which, I think, is due to the fact that the external class of mFileSheetDelegate = FileController, yet when referenced in SaveSheetController, it's = id, a general pointer. But, I thought all anonymous pointers were resolved at runtime ... am I just going to force myself to ignore this Build Warning, knowing that it will all go away at runtime??? The reason for this is because the sheet controller is not aware of your File Controller's methods. Nor should it be. So how to resolve it? The answer is to declare the callback method as an informal protocol *within SheetController.h* Thus: I figured this out while I was mowing the lawn yesterday (before reading this reply) (really!) -- do not have a clue what sort of Freudian psychology this demonstrates -- cutting off the heads of delegates and informal protocols, maybe? Regardless of the deviant psychology, I remember thinking (while pushing the mower) so that's what informal protocols are good for .. delaying the presentation of Build Errors until runtime when all unresolved references are - duh - resolved!!! (2)I still get unrecognized selector .. but, That's because you're targeting the wrong object. Here: no psychology here, just plain brain-dead on my part You have got to get these relationships straight in your mind. It's not that hard. I've goaded and prompted you and written your code for you piece by piece - that's great, it solves your problem. But has it increased your understanding? I fear it hasn't, and it still all seems like voodoo. I'm not sure what I can do about that. Amen to that .. no excuses here .. trying to do too many things at once .. get re-acquainted with C, trying to use the same logic as my application's AppleScript Studio Project counterpart .. Cocoa delegates .. who does what to whom behind the scenes. Anyway, it finally sunk in to keep all UI objects(buttons) local to the individual sheet controllers and have the file controller worry only about return codes. I also figured out (quirky psychology as noted above) informal protocols .. my file controller imports WhateverSheetController.h and to do away with informal protocols would necessitate WhateverSheetController.h to import FileController.h, aka a perfect circle, aka failure to compile. BTW, the reason for my originally passing (NSWindow*)sheet to doSheetSelection within FileController.m was so this method could call [sheet closeOut:self] .. not required now that I call the latter within the sheet's didEndSelector. I'll guarantee you one thing .. no one could ask for a more patient teacher that you .. trust me, I mean that, every syllable. You remind me of an old Physics prof I had many, many, many years ago who made physics sound so darn simple (once it sunk in, which it did with minimal effort since I was so young at the time). Anyway, got some more things to understand (Menus and Toolbars) and probably more questions to ask later, so ... John ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
Graham, Yup, Nathan the double indirection of id * 1st, just the key snippet from FileController.m: @implementation FileController id mFileSheetDelegate; = Here's the complete interface and implementation files for just one SheetController, which has 2 buttons in its sheet window: // SaveSheetController.h #import Cocoa/Cocoa.h @interface SaveSheetController:NSWindowController { IBOutlet NSTextField *iboSaveDescription; } - (void) showSheetOnParentWindow:(NSWindow*)parentWindow withDescription:(NSString*)theDescription delegate:(id)theTarget contextInfo:(void*)contextInfo; - (void) sheetDidEnd:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo; - (IBAction) saveIt:(id)sender; - (IBAction) dontSaveIt:(id)sender; - (void) closeSheet:(id)sender withCode:(int)theCode; @end === // SaveSheetController.m #import SaveSheetController.h enum { kJustSave, /* 0, 1 */ kJustNotSave }; @implementation SaveSheetController extern id mFileSheetDelegate; - (void) showSheetOnParentWindow:(NSWindow*)parentWindow withDescription:(NSString*)theDescription delegate:(id)theTarget contextInfo:(void*)contextInfo { mFileSheetDelegate = theTarget; if (theDescription) { // do magic } [NSApp beginSheet:[self window] modalForWindow:parentWindow modalDelegate:theTarget didEndSelector:@selector (sheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } // 2 buttons of saveSheet - (IBAction) saveIt:(id)sender { [self closeSheet:sender withCode:kJustSave]; } - (IBAction) dontSaveIt:(id)sender { [self closeSheet:sender withCode:kJustNotSave]; } - (void) closeSheet:(id)sender withCode:(int)theCode { NSWindow* theSheet; theSheet = [sender window]; [NSApp endSheet:theSheet returnCode:theCode]; // calls didEndSelector } - (void) sheetDidEnd:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo { [mFileSheetDelegate doSheetSelection:returnCode contextInfo:contextInfo]; [sheet orderOut:self]; } @end Finally, a snippet or two from [doSheetSelection:contextInfo]: - (void) doSheetSelection:(int)returnCode contextInfo:(void*)contextInfo { /* If we had different sheets returning the same code, then we would select between the different sheet IDs. But, for our case, all returned IDs are unique. if ([(NSString*)contextInfo isEqualToString:sCalculateSheetID]) { } if ([(NSString*)contextInfo isEqualToString:sSaveSheetID]) { } if ([(NSString*)contextInfo isEqualToString:sErrorSheetID]) { } */ switch (returnCode) { case kJustSave: [self saveSpreadsheet]; gShouldCloseDoc = TRUE; break; case kJustNotSave: [self setStatus:sFileNotSavedMsg]; gShouldCloseDoc = TRUE; break; } Okay ... all the snippets are done ... now, the questions: (1) warning: no doSheetSelection:contextInfo method found which, I think, is due to the fact that the external class of mFileSheetDelegate = FileController, yet when referenced in SaveSheetController, it's = id, a general pointer. But, I thought all anonymous pointers were resolved at runtime ... am I just going to force myself to ignore this Build Warning, knowing that it will all go away at runtime??? (2)I still get unrecognized selector .. but, didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) is within the sheet.m .. good grief! John ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
On 28 Jun 2008, at 12:38 am, John Love wrote: [code snipped] Finally, you nailed it, almost (one small bug, see below). Phew! Okay ... all the snippets are done ... now, the questions: (1) warning: no doSheetSelection:contextInfo method found which, I think, is due to the fact that the external class of mFileSheetDelegate = FileController, yet when referenced in SaveSheetController, it's = id, a general pointer. But, I thought all anonymous pointers were resolved at runtime ... am I just going to force myself to ignore this Build Warning, knowing that it will all go away at runtime??? The reason for this is because the sheet controller is not aware of your File Controller's methods. Nor should it be. So how to resolve it? The answer is to declare the callback method as an informal protocol *within SheetController.h* Thus: @interface NSObject (SheetSelectionDelegate) - (void) doSheetSelection:(int)returnCode contextInfo: (void*)contextInfo; @end Now, your sheet controller does know that there is a method that has this signature, so the warning will go away. But because the method is defined as a category of NSObject, you still have avoided exposing SheetController to the inner workings of your FileController. This is the more precise, and correct meaning of informal protocol, and generally you implement them as categories on NSObject. (2)I still get unrecognized selector .. but, That's because you're targeting the wrong object. Here: [NSApp beginSheet:[self window] modalForWindow:parentWindow modalDelegate:theTarget // -- uh-oh didEndSelector:@selector (sheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; You should be passing 'self', not 'theTarget' as modalDelegate. Why? Because it is the sheet controller that is the modal delegate of the *sheet*, which is what this specifies. The target is the delegate of the sheet *controller*, so the sheet itself doesn't want this. You already dealt with that by recording the target in your own ivar. You have got to get these relationships straight in your mind. It's not that hard. I've goaded and prompted you and written your code for you piece by piece - that's great, it solves your problem. But has it increased your understanding? I fear it hasn't, and it still all seems like voodoo. I'm not sure what I can do about that. cheers, Graham ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
Graham, Still pouring over your thesis .. in process, so let me ask just one thing at a time, rather than chew here on the whole thing. (1) learning about anonymous type = (id) .. I make this call within FC: [iboCalculateSheetCtrl showSheetOnParentWindow:itsWindow withDescription:nil delegate:self contextInfo:sCalculateSheetID]; The delegate here is FileController*, where the delegate received within SC's showSheetOnParentWindow looks like: - (void) showSheetOnParentWindow:(NSWindow*)parentWindow withDescription:(NSString*)theDescription delegate:(id)theTarget contextInfo:(void*)contextInfo { mFileSheetDelegate = theTarget; // etc. } and at the very top: @implementation CalculateSheetController id *mFileSheetDelegate; Within the implementation of showSheetOnParentWindow, I get assignment from incompatible pointer type with: mFileSheetDelegate = theTarget; and later on within this SC's implementation: - (void) sheetDidEnd:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo { [mFileSheetDelegate doSheetSelection:sheet returnCode:returnCode contextInfo:contextInfo]; } I get: warning:invalid receiver type 'id' and no matching doSheetSelection:returnCode:contextInfo. Based on the invalid .. warning, I understand no matching ... By the way my FC has the formal doSheetSelection:returnCode:contextInfo selector or method .. one final thing on this item, I do not really understand informal protocols, so I have not yet tried to implement such. John ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
On Jun 26, 2008, at 8:19 AM, John Love wrote: @implementation CalculateSheetController id *mFileSheetDelegate; id is already typed as a pointer, no need to add another level of indirection here. --Nathan ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
On 27 Jun 2008, at 12:19 am, John Love wrote: Still pouring over your thesis I thought it was getting a bit damp around here... ;-) and at the very top: @implementation CalculateSheetController id *mFileSheetDelegate; Within the implementation of showSheetOnParentWindow, I get assignment from incompatible pointer type with: id already is a pointer, so you don't want the * and later on within this SC's implementation: - (void) sheetDidEnd:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo { [mFileSheetDelegate doSheetSelection:sheet returnCode:returnCode contextInfo:contextInfo]; Looking much better, but one question: why are you passing sheet to your FC? Why on earth does it need it? Again this exposes UI implementation details to an unrelated controller that shouldn't need to care about that. By the way my FC has the formal doSheetSelection:returnCode:contextInfo selector or method .. one final thing on this item, I do not really understand informal protocols, so I have not yet tried to implement such. Well, you could try searching the docs for that phrase... But in simple terms an informal protocol is just a method or methods that different objects agree between themselves will be how they communicate (and by 'agree between themselves' I mean that you, the programmer, made that decision). In fact you are using them all the time whenever you write any method that another object calls. Don't be too misled by the jargon, there's not much more to it than that. A *formal* protocol on the other hand, is much stricter, and uses the @protocol directive, but I won't get into that, you probably don't need to know. The term 'informal' is really there to distinguish it from a formal @protocol situation. cheers, Graham ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
Completed the housekeeping I needed to do .. so I began moving all the sheet code over to SheetController. Within FileController, I have 3 IBOutlets which are the 3 different sheet NSWindow's *and* the same 3 sheet NSWindow outlets for SheetController. I need to pass the specific sheet to my showSheet method within FileController and the showSheet method within SheetController needs to differentiate between the 3 different ones in order to place the withDescription NSString in the appropriate NSTextField of each sheet. All buttons for each sheet are connected to various IBAction's in FileController. The resulting calls from FileController to SheetController look like this: [theSheet showSheet:saveSheet forWindow:itsWindow withDescription:nil onCompletion:nil]; *and* ** please note that for now, I always pass nil for the completion selector because I haven't *yet* figured out what to do within that selector (probably just a long switch statement based on enumerated constants - I did *not* know that Objective C is based on C, not C++ - I got case identifier needs constant integer, so placed constants inside an enum). Anyway, I keep getting unrecognized selector sent to instance ... (see footprint at the very bottom of this message). In the meantime, I call within FileController: shouldClose = ([theSheet getReturnCode] == doCloseDoc); // should I close the mainWindow or not? once I get a handle on that unrecognized selector error, I can get rid of any calls from FileController to SheetController's getReturnCode. ** an example of an IBAction method within FileController (that is connected within IB to one of the sheet buttons): - (IBAction) keeponCalculating:(id)sender { [theSheet closeSheetWithCode:notCloseDoc]; // or, doCloseDoc, integers 0 or 1, depending on method [self doSomethingHere]; } The showSheet, getReturnCode and closeSheetWithCode methods within SheetController looks like: - (void) showSheet:(NSWindow*)whichSheet forWindow:(NSWindow*)theWindow withDescription:(NSString*)theDescription onCompletion:(SEL)doThis { if (theDescription) { if (whichSheet == calculateSheet) { if (calculateDescription) // if connected in IB [calculateDescription setStringValue:theDescription]; } else if (whichSheet == saveSheet) { // etc } else if (whichSheet == errorSheet) { // etc. } [NSApp beginSheet:whichSheet modalForWindow:theWindow modalDelegate:nil // right now, I pass nil as doThis, but if I pass @selector(sheetDidEnd:returnCode:contextInfo:) // I get unrecognized selector sent to instance ... didEndSelector:doThis contextInfo:nil]; itsReturnCode = [NSApp runModalForWindow:whichSheet]; [NSApp endSheet:whichSheet]; [whichSheet orderOut:self]; } /* -- doThis defined in FileController.m - (void) sheetDidEnd:(NSWindow*)whichSheet returnCode:(int)returnCode contextInfo:(void*)contextInfo { } */ - (int) getReturnCode { return itsReturnCode; } - (void) closeSheetWithCode:(int)theCode { [NSApp stopModalWithCode:theCode]; } ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
I have your previous message typed out and I really have read it ... and I will re-read it and re-read until it clicks. You know how I feel about compartmentalization. I really don't want to abandon it because compartmentalization does work for me for the NSProgressIndicator that I have, as well as my Toolbar. The only saving grace is that I've got sheets down to a science when I meld my sheet code into FileController ... and you have no idea how happy that makes me. Right now, I'm cleaning up the code ... renaming outlets and some instance variables so they sound like plain English. When I am finished with this housekeeping, I fully intend to return to sheet compartmentalization ... I have not given up ... I just wanted to get sheets to work. I will give you all the feedback when the elevator reaches the top. With regards, John Love ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
Hello, Graham ... Because I have multiple calls to show a sheet, from various parts of my FileController, and because I wanted to make my sheet calls as general as possible, I really .. really .. wanted to have a distinct SheetController. As a result, I made calls from FileController to look like:[theSheet showSheet:itsWindow], where theSheet was an outlet of FileController. I could get away with that since all my sheets had just one button .. except one, which had 3 buttons .. and for that exception, the asynchronous nature of sheets got in the away. I have not given up on a separate SheetController .. however .. I finally reduced my showSheet routine to something very, very short and placed it in FileController .. - (void) showSheet: (NSWindow*)theSheet forWindow:(NSWindow*)theWindow withDescription:(NSString*)theDescription { if (theDescription) { if (theSheet == calculateSheet) { if (calculateDescription) // NSTextField outlets of FileController, 1 per sheet [calculateDescription setStringValue:theDescription]; } else if (theSheet == saveSheet) { if (saveDescription) [saveDescription setStringValue:theDescription]; } else if (theSheet == errorSheet) { if (saveDescription) [saveDescription setStringValue:theDescription]; } } [NSApp beginSheet:theSheet modalForWindow:theWindow modalDelegate:nil didEndSelector:nil // @selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; itsReturnCode = [NSApp runModalForWindow:theSheet]; // (int) itsReturnCode in FileController.h [NSApp endSheet:theSheet]; [theSheet orderOut:self]; } When I called showSheet, it looked like: [self showSheet:calculateSheet forWindow:itsWindow withDescription:nil]where calculateSheet, showSheet and errorSheet were 3 IBOutlets of FileController, all of class = NSWindow. Every button of each sheet was connected to a specific IBAction of FileController, e.g., - (IBAction) dontSaveIt:(id)sender { [self closeSheetWithCode:doCloseDoc]; } with that last message looking like: - (void) closeSheetWithCode:(int)theCode { [NSApp stopModalWithCode:theCode]; } As the docs explain, when you do that, the message [NSApp runModalForWindow:] returns that identical integer, which can be any integer of your choosing. In my case, the integer choices trigger a decision whether to close the mainWindow after the sheet is closed, or whether to keep mainWindow open after the sheet is closed. I did not even have to mess with sheetDidEnd selectors because closeSheetWithCode returned gave the needed flag I could use to quantify the itsReturnCode instance variable of FileController. Anyway, I have sheets, not the relatively antequated separate dialogs. I have no intention of giving up on a separate SheetController .. but, in the meantime, I have something that works. Best regards, John Love ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
Hi John, Did you even read my last email? I hope so because I really spent a long time writing it. It may not have been clear, but I did my best! A separate SheetController *is* the right way to do it, but the way you are partitioning the responsibilities between your various controllers isn't right. Your FileController should not be caring one iota about how the SheetController works, or gets its information. If it's asking the sheet controller to show window it's already making an assumption about the UI, which is none of its business. Re-read my previous email. I explain at great length how to do this. Whether your sheet controller is called in one place or many different ones makes no difference. Get rid of all UI assumptions from the caller/client code, and make sure that all of the UI-based stuff is encapsulated in the sheet controller only. The callback to the client can be as simple as a single integer value if you want, but make sure that that is all it is, and you're not sneaking implementation details out and expecting the caller to rely on something that is private to the sheet controller. There are also several examples of the architecture I explained in my demo app which I linked in the previous mail. hth, Graham On 17 Jun 2008, at 3:51 am, John Love wrote: Hello, Graham ... Because I have multiple calls to show a sheet, from various parts of my FileController, and because I wanted to make my sheet calls as general as possible, I really .. really .. wanted to have a distinct SheetController. As a result, I made calls from FileController to look like:[theSheet showSheet:itsWindow], where theSheet was an outlet of FileController. I could get away with that since all my sheets had just one button .. except one, which had 3 buttons .. and for that exception, the asynchronous nature of sheets got in the away. I have not given up on a separate SheetController .. however .. I finally reduced my showSheet routine to something very, very short and placed it in FileController .. - (void) showSheet: (NSWindow*)theSheet forWindow:(NSWindow*)theWindow withDescription:(NSString*)theDescription { if (theDescription) { if (theSheet == calculateSheet) { if (calculateDescription) // NSTextField outlets of FileController, 1 per sheet [calculateDescription setStringValue:theDescription]; } else if (theSheet == saveSheet) { if (saveDescription) [saveDescription setStringValue:theDescription]; } else if (theSheet == errorSheet) { if (saveDescription) [saveDescription setStringValue:theDescription]; } } [NSApp beginSheet:theSheet modalForWindow:theWindow modalDelegate:nil didEndSelector:nil // @selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; itsReturnCode = [NSApp runModalForWindow:theSheet]; // (int) itsReturnCode in FileController.h [NSApp endSheet:theSheet]; [theSheet orderOut:self]; } When I called showSheet, it looked like: [self showSheet:calculateSheet forWindow:itsWindow withDescription:nil]where calculateSheet, showSheet and errorSheet were 3 IBOutlets of FileController, all of class = NSWindow. Every button of each sheet was connected to a specific IBAction of FileController, e.g., - (IBAction) dontSaveIt:(id)sender { [self closeSheetWithCode:doCloseDoc]; } with that last message looking like: - (void) closeSheetWithCode:(int)theCode { [NSApp stopModalWithCode:theCode]; } As the docs explain, when you do that, the message [NSApp runModalForWindow:] returns that identical integer, which can be any integer of your choosing. In my case, the integer choices trigger a decision whether to close the mainWindow after the sheet is closed, or whether to keep mainWindow open after the sheet is closed. I did not even have to mess with sheetDidEnd selectors because closeSheetWithCode returned gave the needed flag I could use to quantify the itsReturnCode instance variable of FileController. Anyway, I have sheets, not the relatively antequated separate dialogs. I have no intention of giving up on a separate SheetController .. but, in the meantime, I have something that works. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
Hi John, I hope you don't mind me cc'ing this to the list - I think it might be helpful to others (if not, my apologies for the noise). On 12 Jun 2008, at 9:43 pm, John Love wrote: Hi, Graham ... Graham, this is a condensed summary of my initial communication: Call it my fetish or whatever, but I strongly believe in compartmentalization, i.e., having a separate SheetController to do sheet stuff. I'd say that was entirely reasonable. For this purpose I have made SheetController*theSheet an outlet of a FileController*theFile and theFile is an outlet of my main nib's File's Owner. Another outlet of File's Owner is my mainWindow. In MyDocument.m -awakeFromNib, I pass mainWindow to one of FileController's methods to initialize some stuff pertaining to mainWindow, including the most important setting of FileController's NSWindow *itsWindow, an instance variable. Well, without being aware of the details, I can't be sure this is wrong or right - but I have to say, it smells. One strong principle of OOP design is that objects are responsible as far as possible for themselves and themselves alone. If you need to pass one object to another to put the first object into a useful state, that seems to be violating this principle. However, there are always exceptions and this may be one of them, hence my reluctance to say that it's definitely wrong. Normally to control a window you use NSWindowController (or subclass of it) which already has a 'window' outlet, so there's no reason to have an 'itsWindow' outlet of your own. If another object needs the window (why?) then it should ask the window controller for it rather than keeping its own reference: [controller window]; Sometimes, I kinda have the feeling that the MVC implies one controller controls everything is the ideal. Hmmm, not really. As many controllers as needed to make the design elegant and straightforward, but no more, would be my view. As simple as possible but no simpler. Even though I am far from the end of my program's development, right now I have a total of 7 controllers, including the two already mentioned. I also have a StatusController*theStatus which writes to a NSTextField of my main window. The remaining 4 don't do very much right now. Down the road I could conceivably shrink the 7 to 5 or whatever, but not much beyond that. Before I continue, I would appreciate some feedback on this philosophy. Considering my previous AppleScript Studio experience, I never had this philosophical problem; but now I do. My approach has fallen into a common pattern, which I've grown comfortable with, and which I believe to be correct. Basically, one interface = one controller. This is strongly suggested by NSWindowController, which has an outlet for exactly one window. By interface I mean a single complete window or dialog box, where that is self-contained. If the dialog is complex and has multiple switchable views I might consider a separate controller for each one, though in practice I don't think I've ever done that - I've just put all the code for the entire dialog in one controller. Let's suppose I have a document that owns a single main window controller - this is very typical in the document-based interface - but also has a number of additional dialog boxes that are needed to get certain operations done. The document is the nib's File's Owner, so it has outlets to each controller for each dialog box. Each controller in turn has outlets (and actions) for each individual widget/control in the dialog interface. What is a dialog for? It's to get information from the user in order to complete some task. The document is not interested in how that information is obtained specifically, it only knows it needs it. So the document asks the relevant controller go get me this information I need from the user. The dialog's controller in turn puts up the dialog, handles all the interaction with the user, removes the window when the user is done, packages up the information in a form useful to its client, and returns it. Everyone's happy - the document got its info, the dialog controller didn't have to care about who wanted the info or what was done with it, and the connections between the objects were kept to a minimum, with few dependencies. Now whether the dialog is modal, modeless, a sheet, or whatever, is irrelevant. The controller for that dialog can probably make that decision (it may need to be given supplementary information, such as the parent window, but that can be designed into its interface with its client code - and it is also free to ignore it, so typically my dialog controllers take a parent window argument regardless, even if I end up ignoring it and displaying the dialog modally - the client (document) doesn't care either way). However, because the dialog may be modeless (or asynchronous, if you
Prevent Asynchronous operation of beginSheetModalForWindow
Before I begin, I want to assure you that I have researched entries here on sheets, as well as those in MacTech.com. It's reasonably probable that I have missed some entries and so that is why I am asking for help. I also realize that this description is very long and I *really* did try to shorten it. My app is a Cocoa Document based app. I have chosen the title Prevent Asynchronous operation of beginSheetModalForWindow because I use various calls to beginSheetModalForWindow in many parts of my app code and in one case I need the calls to didEndSelector to be completed *before* the code that follows beginSheetModalForWindow is executed (see MyDocument.m below). With Asynchronous operation, the sheet will show for a very brief moment and continue as the Apple docs stipulate .. I want the sheet to stay down UNTIL I click one of the buttons in the sheet. MacTech names calls to beginSheetModalForWindow DocModalNew. What have I tried to do, but without success: *1)* After the call to [calculateSheet beginSheetModalForWindow:docWindow .. etc]; within showCalculateSheet, I have used: while (itsReturnCode == -1) // the initialized value before the call to beginSheetModalForWindow What happens is a never-ending loop from which I need to force-quit. *2)* Within shouldCloseFile I have called: while ((theReturnCode = [theSheet getReturnCode]) == -1); // same never-ending loop results The app's File's Owner is MyDocument // inside my nib file I have a NSObject named FileController and SheetController // in MyDocument.h #import Cocoa/Cocoa.h #import FileController.h @interface MyDocument:NSDocument { IBOutlet FileController*theFile; IBOutlet NSWindow *documentWindow; // passed to methods in FileController.m } // signatures of various methods here @end // in MyDocument.m if ([theFile shouldCloseFile]) { // stuff that canNOT be executed until shouldCloseFile finishes } == // in FileController.h #import Cocoa/Cocoa.h #import SheetController.h @interface FileController:NSObject { IBOutlet SheetController *theSheet; NSWindow *itsWindow; } - (BOOL) shouldCloseFile; // plus other signatures @end == // in FileController.m // itsWindow is quantified elsewhere in this .m listing by anoter call within MyDocument.m - (BOOL) shouldCloseFile { int theReturnCode; BOOL shouldClose = TRUE; if (!itsFinishedCalculation) { [theSheet showCalculateSheet:itsWindow]; // tried this, but had to force-quit // while ((theReturnCode = [theSheet getReturnCode]) == -1); theReturnCode = [theSheet getReturnCode]; if (theReturnCode == NSAlertFirstButtonReturn) { // Continue shouldClose = FALSE; } else if (theReturnCode == NSAlertSecondButtonReturn) { // Stop and save [self saveFile]; NSLog(@NSAlertSecondButtonReturn); } } return shouldClose; } == // SheetController.h #import Cocoa/Cocoa.h @interface SheetController:NSObject { NSWindow *itsWindow; // passed to show methods int itsReturnCode; } - (int) getReturnCode; - (void) showCalculateSheet:(NSWindow*)docWindow; - (void) endCalculateSheet:(NSAlert*)theSheet returnCode:(int)returnCode contextInfo:(void*)contextInfo; @end == // SheetController.m #import SheetController.h @implementation SheetController - (id) init { if (self = [super init]) { itsReturnCode = -1; } return self; } - (int) getReturnCode { return itsReturnCode; } - (void) showCalculateSheet:(NSWindow*)docWindow { NSButton *cButton, *sButton, *dButton; itsWindow = docWindow; // set instance variable NSAlert *calculateSheet = [[[NSAlert alloc] init] autorelease]; [itsWindow setDelegate:calculateSheet]; cButton = [calculateSheet addButtonWithTitle:@Continue]; // [cButton setKeyEquivalent:@\r]; // automatic for default button sButton = [calculateSheet addButtonWithTitle:@Stop and save]; [sButton setKeyEquivalent:@s]; dButton = [calculateSheet addButtonWithTitle:@Stop and don't save]; [dButton setKeyEquivalent:@d]; [calculateSheet setMessageText:@You have not finished calculating your Spreadsheet.\n Do you wish to continue calculating?]; [calculateSheet setAlertStyle:NSWarningAlertStyle]; itsReturnCode = 1; [calculateSheet beginSheetModalForWindow:docWindow modalDelegate:self didEndSelector:@selector (endCalculateSheet:returnCode:contextInfo:) contextInfo:docWindow]; // while (itsReturnCode == -1); // tried this, but had to force-quit } - (void) endCalculateSheet:(NSAlert*)theSheet returnCode:(int)returnCode contextInfo:(void*)contextInfo { if (returnCode == NSAlertFirstButtonReturn)// Continue
Re: Prevent Asynchronous operation of beginSheetModalForWindow
On Tue, Jun 10, 2008 at 1:10 PM, John Love [EMAIL PROTECTED] wrote: I have chosen the title Prevent Asynchronous operation of beginSheetModalForWindow because I use various calls to beginSheetModalForWindow in many parts of my app code and in one case I need the calls to didEndSelector to be completed *before* the code that follows beginSheetModalForWindow is executed (see MyDocument.m below). I'm sorry to have to tell you this, but you are doomed. Sheets are inherently asynchronous. To understand why, think about what happens when two sheets are displayed on two different windows. The user can dismiss them in any order. What happens if sheet A displays, then sheet B displays, then the user goes back and closes sheet B? If there were a synchronous API the code would have to somehow jump down the stack to where you're waiting for A, while somehow leaving the stuff that's waiting for B live farther up the stack. This is quite simply impossible in a C-based language. Is there some reason you can't just put all of the after code in the endCalculateSheet: method? Mike ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
On 10 Jun '08, at 1:27 PM, Michael Ash wrote: If there were a synchronous API the code would have to somehow jump down the stack to where you're waiting for A, while somehow leaving the stuff that's waiting for B live farther up the stack. This is quite simply impossible in a C-based language. It's not impossible, but it would require either that every window ran in a separate thread (as in the BeOS) or that the Cocoa frameworks supported coroutines. Coroutines are quite feasible in C, using setjmp/ longjmp style trickery; but when I investigated this I saw that using them would probably seriously confuse Objective-C's exception handling, and possibly NSRunLoop too. John, you should look at the Cocoa conceptual docs that describe the use of sheets. There are probably a bunch of examples of sheet usage in the sample code too, if you search for beginSheetModalForWindow:. —Jens smime.p7s Description: S/MIME cryptographic signature ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]
Re: Prevent Asynchronous operation of beginSheetModalForWindow
Hi John, While I understand to some extent why you've done it this way, you've also managed to tie yourself in knots here. Sheets are slightly more complex than running an old-school modal dialog inline but they are not as complicated as you seem to have made them! The basic idea is that you have two pieces of code - one that triggers the sheet, and another that responds to it when the user closes it. You don't normally need anything more, though it may make sense to factor code into a controller object which can ease its re-use (but is not essential). You put all your code that runs after the dialog has completed in the completion routine. One important thing to note is that you do not *ever* run a loop like: while( returnCode == -1 ){ ... } that's just crazy talk ;-) Don't attempt to somehow stall the main event loop until the sheet has finished - that will never work and sheets aren't designed to work that way. Here's what I typically do, this would be code in a window controller dedicated to this sheet (warning - typed into Mail): - (void)startSheetOnParent:(NSWindow*) parentWindow { /* set up the initial state of the sheet dialog here */ [NSApp beginSheet:[self window] modalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:kSomeContextIdentifier]; } - (void) sheetDidEnd:(NSWindow*) sheet returnCode:(int) returnCode contextInfo:(void*) contextInfo { [sheet orderOut:nil]; // close the sheet if ( returnCode == NSOKButton ) { /* process the result of the sheet here. If this method is shared with other sheets, you can use the contextInfo to disambiguate the sheet. At this point just run the code that follows the sheet, or trigger it using a notification perhaps */ } } That's all you need. If your sheet has controls that interact with each other or external information, the 'modalDelegate' is the object that will handle that - again, it will be the sheet's controller. If your calling code currently is arranged in such a way that it appears to you *necessary* to do something like: - (void) doStuff { [mySheetController startSheetOnParent:[self windowForSheet]]; while( sheetNotDone ){} // spin hopelessly waiting for the sheet to finish - it never will because you've entered an infinite loop if ( sheetResult == NSOKButton ) { /* continue doing stuff */ } } then you need to refactor your code so that the 'keep doing stuff' part can be called directly from the sheet ended callback. One thing that should be clear but maybe isn't is that the term 'asynchronous' is a little misleading - the sheet is still being run on the same thread as the code that called it, so entering a while() loop waiting for the sheet to finish cannot possibly work - that loop will block the main thread so the sheet cannot continue to function, and there's nothing that could cause the loop to terminate. There is already a loop running that accomplishes this - the main event loop. hth, Graham On 11 Jun 2008, at 6:10 am, John Love wrote: Before I begin, I want to assure you that I have researched entries here on sheets, as well as those in MacTech.com. It's reasonably probable that I have missed some entries and so that is why I am asking for help. I also realize that this description is very long and I *really* did try to shorten it. My app is a Cocoa Document based app. I have chosen the title Prevent Asynchronous operation of beginSheetModalForWindow because I use various calls to beginSheetModalForWindow in many parts of my app code and in one case I need the calls to didEndSelector to be completed *before* the code that follows beginSheetModalForWindow is executed (see MyDocument.m below). With Asynchronous operation, the sheet will show for a very brief moment and continue as the Apple docs stipulate .. I want the sheet to stay down UNTIL I click one of the buttons in the sheet. MacTech names calls to beginSheetModalForWindow DocModalNew. What have I tried to do, but without success: *1)* After the call to [calculateSheet beginSheetModalForWindow:docWindow .. etc]; within showCalculateSheet, I have used: while (itsReturnCode == -1) // the initialized value before the call to beginSheetModalForWindow What happens is a never-ending loop from which I need to force- quit. *2)* Within shouldCloseFile I have called: while ((theReturnCode = [theSheet getReturnCode]) == -1); // same never-ending loop results The app's File's Owner is MyDocument // inside my nib file I have a NSObject named FileController and SheetController // in MyDocument.h #import Cocoa/Cocoa.h #import FileController.h @interface MyDocument:NSDocument { IBOutlet FileController*theFile