Revision: 29281 http://sourceforge.net/p/bibdesk/svn/29281 Author: hofman Date: 2025-07-17 16:36:29 +0000 (Thu, 17 Jul 2025) Log Message: ----------- Get commit errors from text fields in editorr separately from fake delegate methods. Present the errors in the control delegate methods. Also use the errors in commitEditingAndReturnError:, so it does not display the errors in a sheet or dialog.
Modified Paths: -------------- trunk/bibdesk/BDSKEditor.m Modified: trunk/bibdesk/BDSKEditor.m =================================================================== --- trunk/bibdesk/BDSKEditor.m 2025-07-15 09:33:52 UTC (rev 29280) +++ trunk/bibdesk/BDSKEditor.m 2025-07-17 16:36:29 UTC (rev 29281) @@ -124,6 +124,9 @@ - (void)fileURLDidChange:(NSNotification *)notification; - (void)needsToBeFiledDidChange:(NSNotification *)notification; +- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)errorString error:(NSError **)error; +- (BOOL)control:(NSControl *)control isValidObject:(id)obj error:(NSError **)error; + - (void)recordChangingField:(NSString *)fieldName toValue:(NSString *)value; - (void)openParentItemForField:(NSString *)field; @@ -409,7 +412,50 @@ - (BOOL)commitEditing { - return [self commitEditingAndReturnError:NULL]; + NSResponder *firstResponder = [[self window] firstResponder]; + + /* + Need to finalize text field cells being edited or the abstract/annote text views, since the + text views bypass the normal undo mechanism for speed, and won't cause the doc to be marked + dirty on subsequent edits. + */ + if([firstResponder isKindOfClass:[NSText class]]){ + + NSTextView *textView = (NSTextView *)firstResponder; + NSInteger editedRow = -1; + NSArray *selection = [textView selectedRanges]; + if ([textView isFieldEditor]) { + firstResponder = (NSResponder *)[textView delegate]; + editedRow = [tableView rowForView:textView]; + } + + editorFlags.didSetupFields = NO; // if we we rebuild the fields, the selection will become meaningless + + // check textviews for balanced braces as needed + if (currentEditedView && [self validateCurrentEditedView] == NO) + return NO; + + // commit edits (formatters may refuse to allow this) + if ([[self window] makeFirstResponder:[self window]] == NO) + return NO; + + // for inherited fields, we should do something here to make sure the user doesn't have to go through the warning sheet + + if (editorFlags.didSetupFields == NO || editedRow == -1) { + if (editedRow != -1) { + if (editedRow < [tableView numberOfRows]) { + [tableView editColumn:1 row:editedRow withEvent:nil select:NO]; + [(NSTextView *)[[self textFieldAtRow:editedRow] currentEditor] setSafeSelectedRanges:selection]; + } + } else if ([[self window] makeFirstResponder:firstResponder]) { + if ([firstResponder isKindOfClass:[NSTextField class]]) + textView = (NSTextView *)[(NSTextField *)firstResponder currentEditor]; + [textView setSafeSelectedRanges:selection]; + } + } + + } + return YES; } - (BOOL)commitEditingAndReturnError:(NSError **)error @@ -425,24 +471,69 @@ if([firstResponder isKindOfClass:[NSText class]]){ NSTextView *textView = (NSTextView *)firstResponder; + NSTextField *textField = nil; NSInteger editedRow = -1; NSArray *selection = [textView selectedRanges]; if ([textView isFieldEditor]) { firstResponder = (NSResponder *)[textView delegate]; + if ([firstResponder isKindOfClass:[NSTextField class]]) + textField = (NSTextField *)firstResponder; editedRow = [tableView rowForView:textView]; } - editorFlags.didSetupFields = NO; // if we we rebuild the fields, the selection will become meaningless - - // check textviews for balanced braces as needed - if (currentEditedView && [self validateCurrentEditedView] == NO) { - if (error) - *error = [NSError localErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Failed to commit edits", @"Error message")]; + if (textField) { + + BOOL valid = YES; + NSString *obj = nil; + NSFormatter *formatter = [textField formatter]; + + if (formatter) { + NSString *errorString = nil; + NSString *string = [textView string]; + valid = [formatter getObjectValue:&obj forString:string errorDescription:&errorString]; + if (valid == NO) { + valid = [self control:textField didFailToFormatString:string errorDescription:errorString error:error]; + if (valid == NO) + return NO; + } + } else { + obj = [textView string]; + } + valid = [self control:textField isValidObject:obj error:error]; + + if (valid == NO) { + if (error && [*error localizedRecoveryOptions]) { + // no recovery possible, so just make it an unrecoverable error + NSError *err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:[*error localizedDescription]]; + NSString *errString = [*error localizedRecoverySuggestion]; + NSRange r = [errString rangeOfString:@"."]; + if (r.location != NSNotFound) + errString = [errString substringToIndex:NSMaxRange(r)]; + [err setValue:errString forKey:NSLocalizedRecoverySuggestionErrorKey]; + *error = err; + } + return NO; + } + + } else if (currentEditedView && [self validateCurrentEditedView] == NO) { + // the string of the edited textView has unbalanced braces + + if (error) { + NSError *err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Failed to commit edits", @"Error message")]; + [err setValue:NSLocalizedString(@"The value you entered contains unbalanced braces and cannot be saved.", @"Informative text in alert dialog") forKey:NSLocalizedRecoverySuggestionErrorKey]; + *error = err; + } return NO; + } + // commit should now succeed without errors + + editorFlags.didSetupFields = NO; // if we we rebuild the fields, the selection will become meaningless + // commit edits (formatters may refuse to allow this) if ([[self window] makeFirstResponder:[self window]] == NO) { + // should not be reached, we should have found errors above if (error) *error = [NSError localErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Failed to commit edits", @"Error message")]; return NO; @@ -457,8 +548,8 @@ [(NSTextView *)[[self textFieldAtRow:editedRow] currentEditor] setSafeSelectedRanges:selection]; } } else if ([[self window] makeFirstResponder:firstResponder]) { - if ([firstResponder isKindOfClass:[NSTextField class]]) - textView = (NSTextView *)[(NSTextField *)firstResponder currentEditor]; + if (textField) + textView = (NSTextView *)[textField currentEditor]; [textView setSafeSelectedRanges:selection]; } } @@ -1899,23 +1990,21 @@ // Don't show an annoying warning. This fails only when invalid cite key characters are used, which are simply removed by the formatter. } -// send by the formatter when formatting in getObjectValue... failed -- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)aString errorDescription:(NSString *)error{ +- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)aString errorDescription:(NSString *)errorString error:(NSError **)error { if (editorFlags.isEditable == NO) return YES; - BOOL accept = NO; + NSError *err = nil; - if (nil == error) { + if (nil == errorString) { // shouldn't get here + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; NSLog(@"%@:%d formatter failed for unknown reason", __FILENAMEASNSSTRING__, __LINE__); } else if (control == citeKeyField) { // !!! may have to revisit this with strict invalid keys? // this may occur if the cite key formatter fails to format - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"Invalid Cite Key", @"Message in alert dialog when enetring invalid cite key")]; - [alert setInformativeText:error]; - [alert beginSheetModalForWindow:[self window] completionHandler:NULL]; + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Cite Key", @"Message in alert dialog when enetring invalid cite key")]; + [err setValue:errorString forKey:NSLocalizedRecoverySuggestionErrorKey]; } else { @@ -1925,36 +2014,56 @@ NSString *fieldName = [fields objectAtIndex:row]; if ([fieldName isEqualToString:BDSKCrossrefString]) { // this may occur if the cite key formatter fails to format - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"Invalid Crossref Key", @"Message in alert dialog when entering invalid Crossref key")]; - [alert setInformativeText:error]; - - [alert beginSheetModalForWindow:[self window] completionHandler:NULL]; + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Crossref Key", @"Message in alert dialog when entering invalid Crossref key")]; + [err setValue:errorString forKey:NSLocalizedRecoverySuggestionErrorKey]; } else if ([fieldName isCitationField]) { // this may occur if the citation formatter fails to format - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"Invalid Citation Key", @"Message in alert dialog when entering invalid Crossref key")]; - [alert setInformativeText:error]; - - [alert beginSheetModalForWindow:[self window] completionHandler:NULL]; + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Citation Key", @"Message in alert dialog when entering invalid Crossref key")]; + [err setValue:errorString forKey:NSLocalizedRecoverySuggestionErrorKey]; } else if (NO == [tableCellFormatter editAsComplexString]) { // this is a simple string, an error means that there are unbalanced braces - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; - [alert setInformativeText:NSLocalizedString(@"The value you entered contains unbalanced braces and cannot be saved.", @"Informative text in alert dialog")]; - - [alert beginSheetModalForWindow:[self window] completionHandler:NULL]; + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; + [err setValue:NSLocalizedString(@"The value you entered contains unbalanced braces and cannot be saved.", @"Informative text in alert dialog") forKey:NSLocalizedRecoverySuggestionErrorKey]; + } else { + err = [NSError mutableLocalErrorWithCode:kBDSKComplexStringError localizedDescription:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; + [err setValue:errorString forKey:NSLocalizedRecoverySuggestionErrorKey]; } } - } + } + + if (error) + *error = err; + return NO; +} + +// send by the formatter when formatting in getObjectValue... failed +- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)aString errorDescription:(NSString *)error{ + if (editorFlags.isEditable == NO) + return YES; + + NSError *err = nil; + BOOL accept = [self control:control didFailToFormatString:aString errorDescription:error error:&err]; + + if (accept == NO) { + if (err && [err code] != kBDSKComplexStringError) + [self presentError:err modalForWindow:[self window] delegate:nil didPresentSelector:NULL contextInfo:NULL]; + else + NSBeep(); + } + return accept; } -- (BOOL)control:(NSControl *)control isValidObject:(id)obj { +- (BOOL)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recoveryOptionIndex { + return (recoveryOptionIndex == 0); +} + +- (BOOL)control:(NSControl *)control isValidObject:(id)obj error:(NSError **)error { if (editorFlags.isEditable == NO) return YES; BOOL isValid = YES; + NSError *err = nil; if (control == citeKeyField) { @@ -1965,21 +2074,12 @@ // check for fragile invalid characters, as the formatter doesn't do this if (r.location != NSNotFound) { - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; - [alert setInformativeText:NSLocalizedString(@"The cite key you entered contains characters that could be invalid in TeX. Do you want to keep them or remove them?", @"Informative text in alert dialog")]; - [alert addButtonWithTitle:NSLocalizedString(@"Remove", @"Button title")]; - [alert addButtonWithTitle:NSLocalizedString(@"Keep", @"Button title")]; + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; + [err setValue:NSLocalizedString(@"The cite key you entered contains characters that could be invalid in TeX. Do you want to keep them or remove them?", @"Informative text in alert dialog") forKey:NSLocalizedRecoverySuggestionErrorKey]; + [err setValue:self forKey:NSRecoveryAttempterErrorKey]; + [err setValue:@[NSLocalizedString(@"Remove", @"Button title"), NSLocalizedString(@"Keep", @"Button title")] forKey:NSLocalizedRecoveryOptionsErrorKey]; + isValid = NO; - NSInteger rv = [alert runModal]; - - if (rv == NSAlertFirstButtonReturn) { - [control setStringValue:[obj stringByReplacingCharactersInSet:invalidSet withString:@""]]; - isValid = NO; - } else { - [citeKeyField setStringValue:[control stringValue]]; - } - } else { // check whether we won't crossref to the new citekey @@ -1990,11 +2090,8 @@ message = NSLocalizedString(@"Cannot set this cite key as this would lead to a crossreff chain.", @"Informative text in alert dialog"); if (message) { - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; - [alert setInformativeText:message]; - - [alert beginSheetModalForWindow:[self window] completionHandler:NULL]; + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Value", @"Message in alert dialog when entering an invalid value")]; + [err setValue:message forKey:NSLocalizedRecoverySuggestionErrorKey]; isValid = NO; } } @@ -2019,20 +2116,37 @@ message = NSLocalizedString(@"Cannot set the Crossref field, as the current item is cross referenced.", @"Informative text in alert dialog"); if (message) { - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"Invalid Crossref Value", @"Message in alert dialog when entering an invalid Crossref key")]; - [alert setInformativeText:message]; - - [alert beginSheetModalForWindow:[self window] completionHandler:NULL]; + err = [NSError mutableLocalErrorWithCode:kBDSKFailedToCommit localizedDescription:NSLocalizedString(@"Invalid Crossref Value", @"Message in alert dialog when entering an invalid Crossref key")]; + [err setValue:message forKey:NSLocalizedRecoverySuggestionErrorKey]; isValid = NO; } } } } - - return isValid; + + if (isValid == NO && error) + *error = err; + return isValid; } +- (BOOL)control:(NSControl *)control isValidObject:(id)obj { + NSError *err = nil; + BOOL isValid = [self control:control isValidObject:obj error:&err]; + + if (isValid) + return YES; + + isValid = [self presentError:err]; + + if (control == citeKeyField && [err localizedRecoveryOptions] && isValid == NO) { + // user said to remove fragile characters + NSCharacterSet *invalidSet = [[BDSKTypeManager sharedManager] fragileCiteKeyCharacterSet]; + [control setStringValue:[obj stringByReplacingCharactersInSet:invalidSet withString:@""]]; + } + + return isValid; +} + - (void)controlTextDidEndEditing:(NSNotification *)notification{ [self setEditing:NO]; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. _______________________________________________ Bibdesk-commit mailing list Bibdesk-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/bibdesk-commit