tags:

views:

149

answers:

2

I want to use windowShouldClose: in my NSWindowController subclass to pop up a sheet asking if the user wants to save changes before closing with Save, Cancel, and Don't Save buttons.

The issue I'm running in to is that beginSheetModalForWindow:... uses a delegate instead of a return value.

I can return NO in windowShouldClose:, but then when I send [self close] to the controller in the panel's delegate nothing happens.

Can somebody explain to me how to do this or point me in the direction of some sample code?

+1  A: 

The basic solution is to put a boolean flag on the window that states whether or not the window has warned about unsaved changes. Before calling [self close], set this flag to true.

Finally, in the windowShouldClose method, return the value of the flag.

NilObject
Thanks, that makes sense. See my answer for example code using this pattern.
Lawrence Johnston
+1  A: 

This is the code I ended up using.

windowShouldCloseAfterSaveSheet_ is an instance variable in my controller class.

Remember to set the window outlet for the controller in IB.

- (BOOL)windowShouldClose:(id)window {    
  if (windowShouldCloseAfterSaveSheet_) {
    // User has already gone through save sheet and choosen to close the window
    windowShouldCloseAfterSaveSheet_ = NO; // Reset value just in case
    return YES;
  }

  if ([properties_ settingsChanged]) {
    NSAlert *saveAlert = [[NSAlert alloc] init];
    [saveAlert addButtonWithTitle:@"OK"];
    [saveAlert addButtonWithTitle:@"Cancel"];
    [saveAlert addButtonWithTitle:@"Don't Save"];
    [saveAlert setMessageText:@"Save changes to preferences?"];
    [saveAlert setInformativeText:@"If you don't save the changes, they will be lost"];
    [saveAlert beginSheetModalForWindow:window
                                modalDelegate:self
                               didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) 
                                  contextInfo:nil];

    return NO;
  }

  // Settings haven't been changed.
  return YES;
}

// This is the method that gets called when a user selected a choice from the
// do you want to save preferences sheet.
- (void)alertDidEnd:(NSAlert *)alert 
         returnCode:(int)returnCode
        contextInfo:(void *)contextInfo {
  switch (returnCode) {
    case NSAlertFirstButtonReturn:
      // Save button
      if (![properties_ saveToFile]) {
        NSAlert *saveFailedAlert = [NSAlert alertWithMessageText:@"Save Failed"
                                                   defaultButton:@"OK"
                                                 alternateButton:nil
                                                     otherButton:nil
                                       informativeTextWithFormat:@"Failed to save preferences to disk"];
        [saveFailedAlert runModal];
      }
      [[alert window] orderOut:self];
      windowShouldCloseAfterSaveSheet_ = YES;
      [[self window] performClose:self];
      break;
    case NSAlertSecondButtonReturn:
      // Cancel button
      // Do nothing
      break;
    case NSAlertThirdButtonReturn:
      // Don't Save button
      [[alert window] orderOut:self];
      windowShouldCloseAfterSaveSheet_ = YES;
      [[self window] performClose:self];
      break;
    default:
      NSAssert1(NO, @"Unknown button return: %i", returnCode);
      break;
  }
}
Lawrence Johnston