Re: Prevent Asynchronous operation of beginSheetModalForWindow
Re: Prevent Asynchronous operation of beginSheetModalForWindow
- Subject: Re: Prevent Asynchronous operation of beginSheetModalForWindow
- From: Graham Cox <email@hidden>
- Date: Wed, 11 Jun 2008 09:14:35 +1000
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;
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"
{
NSLog(@"First Calculate Button clicked");
}
else if (returnCode == NSAlertSecondButtonReturn) // "Stop and
save"
{
NSLog(@"Second Calculate Button clicked");
}
else if (returnCode == NSAlertThirdButtonReturn) // "Stop and
don't
save"
{
NSLog(@"Third Calculate Button clicked");
}
itsReturnCode = returnCode; // set so FileController knows what
to do
}
@end
==========
_______________________________________________
Cocoa-dev mailing list (email@hidden)
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:
This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
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:
This email sent to email@hidden