Revision: 28501
          http://sourceforge.net/p/bibdesk/svn/28501
Author:   hofman
Date:     2024-01-01 18:13:36 +0000 (Mon, 01 Jan 2024)
Log Message:
-----------
Use NSMapTable for macros and document info, using case insensitive keys. 
NSPointerFunctions ategory to define one for case insensitive strings.

Modified Paths:
--------------
    trunk/bibdesk/BDSKAppController.m
    trunk/bibdesk/BDSKBibTeXParser.h
    trunk/bibdesk/BDSKBibTeXParser.m
    trunk/bibdesk/BDSKCompletionManager.h
    trunk/bibdesk/BDSKCompletionManager.m
    trunk/bibdesk/BDSKDocumentInfoWindowController.h
    trunk/bibdesk/BDSKDocumentInfoWindowController.m
    trunk/bibdesk/BDSKMacroResolver.h
    trunk/bibdesk/BDSKMacroResolver.m
    trunk/bibdesk/BDSKMacroWindowController.m
    trunk/bibdesk/BDSKScriptGroup.m
    trunk/bibdesk/BDSKSharedGroup.m
    trunk/bibdesk/BDSKSharingServer.m
    trunk/bibdesk/BDSKURLGroup.m
    trunk/bibdesk/BibDocument.h
    trunk/bibdesk/BibDocument.m
    trunk/bibdesk/BibDocument_Actions.m
    trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj

Added Paths:
-----------
    trunk/bibdesk/NSPointerFunctions_BDSKExtensions.h
    trunk/bibdesk/NSPointerFunctions_BDSKExtensions.m

Modified: trunk/bibdesk/BDSKAppController.m
===================================================================
--- trunk/bibdesk/BDSKAppController.m   2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKAppController.m   2024-01-01 18:13:36 UTC (rev 28501)
@@ -83,6 +83,7 @@
 #import <WebKit/WebKit.h>
 #import "NSDate_BDSKExtensions.h"
 #import "NSColor_BDSKExtensions.h"
+#import <FileView/FileView.h>
 
 #define WEB_URL @"https://bibdesk.sourceforge.io/";
 #define WIKI_URL @"https://sourceforge.net/p/bibdesk/wiki/Main_Page/";

Modified: trunk/bibdesk/BDSKBibTeXParser.h
===================================================================
--- trunk/bibdesk/BDSKBibTeXParser.h    2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKBibTeXParser.h    2024-01-01 18:13:36 UTC (rev 28501)
@@ -62,7 +62,7 @@
 + (NSArray *)itemsFromString:(NSString *)aString owner:(id<BDSKOwner>)anOwner 
error:(NSError **)outError;
 
 + (NSArray *)itemsFromData:(NSData *)inData
-                    macros:(NSDictionary **)outMacros
+                    macros:(NSMapTable **)outMacros
                   filePath:(NSString *)filePath
                      owner:(id<BDSKOwner>)anOwner
                   encoding:(NSStringEncoding)parserEncoding
@@ -84,7 +84,7 @@
     @result     (description)
 */
 + (NSArray *)itemsFromData:(NSData *)inData
-                    macros:(NSDictionary **)outMacros
+                    macros:(NSMapTable **)outMacros
               documentInfo:(NSDictionary **)outDocumentInfo
                     groups:(NSDictionary **)outGroups
                frontMatter:(NSString **)outFrontMatter

Modified: trunk/bibdesk/BDSKBibTeXParser.m
===================================================================
--- trunk/bibdesk/BDSKBibTeXParser.m    2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKBibTeXParser.m    2024-01-01 18:13:36 UTC (rev 28501)
@@ -60,6 +60,7 @@
 #import "CFString_BDSKExtensions.h"
 #import "NSDictionary_BDSKExtensions.h"
 #import "NSFileManager_BDSKExtensions.h"
+#import "NSPointerFunctions_BDSKExtensions.h"
 
 static NSLock *parserLock = nil;
 
@@ -80,7 +81,7 @@
 // private functions for handling different entry types; these functions do 
not do any locking around the parser
 static BOOL addItemToDictionaryOrSetDocumentInfo(AST *entry, NSMutableArray 
*returnArray, NSDictionary **outDocumentInfo, const char *buf, NSUInteger 
inputDataLength, BDSKMacroResolver *macroResolver, NSString *filePath, 
NSStringEncoding parserEncoding);
 static BOOL appendPreambleToFrontmatter(AST *entry, NSMutableString 
*frontMatter, NSString *filePath, NSStringEncoding encoding);
-static BOOL addMacroToDictionary(AST *entry, NSMutableDictionary *dictionary, 
BDSKMacroResolver *macroResolver, NSString *filePath, NSStringEncoding 
encoding, BOOL *hadCircularMacros);
+static BOOL addMacroToDictionary(AST *entry, NSMapTable *dictionary, 
BDSKMacroResolver *macroResolver, NSString *filePath, NSStringEncoding 
encoding, BOOL *hadCircularMacros);
 static BOOL appendCommentToFrontmatterOrAddGroups(AST *entry, NSMutableString 
*frontMatter, NSMutableDictionary *groups, NSString *filePath, NSStringEncoding 
encoding);
 
 // private function for preserving newlines in annote/abstract fields; does 
not lock the parser
@@ -176,19 +177,19 @@
     return [self itemsFromData:inData macros:NULL documentInfo:NULL 
groups:NULL frontMatter:NULL filePath:BDSKParserPasteDragString owner:anOwner 
encoding:NSUTF8StringEncoding error:outError];
 }
 
-+ (NSArray *)itemsFromData:(NSData *)inData macros:(NSDictionary **)outMacros 
filePath:(NSString *)filePath owner:(id<BDSKOwner>)anOwner 
encoding:(NSStringEncoding)parserEncoding error:(NSError **)outError{
++ (NSArray *)itemsFromData:(NSData *)inData macros:(NSMapTable **)outMacros 
filePath:(NSString *)filePath owner:(id<BDSKOwner>)anOwner 
encoding:(NSStringEncoding)parserEncoding error:(NSError **)outError{
     return [self itemsFromData:inData macros:outMacros documentInfo:NULL 
groups:NULL frontMatter:NULL filePath:filePath owner:anOwner 
encoding:parserEncoding error:outError];
 }
 
-+ (NSArray *)itemsFromData:(NSData *)inData macros:(NSDictionary **)outMacros 
documentInfo:(NSDictionary **)outDocumentInfo groups:(NSDictionary **)outGroups 
frontMatter:(NSString **)outFrontMatter filePath:(NSString *)filePath 
owner:(id<BDSKOwner>)anOwner encoding:(NSStringEncoding)parserEncoding 
error:(NSError **)outError{
++ (NSArray *)itemsFromData:(NSData *)inData macros:(NSMapTable **)outMacros 
documentInfo:(NSDictionary **)outDocumentInfo groups:(NSDictionary **)outGroups 
frontMatter:(NSString **)outFrontMatter filePath:(NSString *)filePath 
owner:(id<BDSKOwner>)anOwner encoding:(NSStringEncoding)parserEncoding 
error:(NSError **)outError{
     NSMutableDictionary *groups = nil;
-    NSMutableDictionary *macros = nil;
+    NSMapTable *macros = nil;
     NSMutableString *frontMatter = nil;
     
     if (outGroups)
         *outGroups = groups = [NSMutableDictionary dictionary];
     if (outMacros)
-        *outMacros = macros = [[NSMutableDictionary newForCaseInsensitiveKeys] 
autorelease];
+        *outMacros = macros = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0];
     if (outFrontMatter)
         *outFrontMatter = frontMatter = [NSMutableString string];
     if (outDocumentInfo)
@@ -895,7 +896,7 @@
     return success;
 }
 
-static BOOL addMacroToDictionary(AST *entry, NSMutableDictionary *dictionary, 
BDSKMacroResolver *macroResolver, NSString *filePath, NSStringEncoding 
encoding, BOOL *hadCircularMacros)
+static BOOL addMacroToDictionary(AST *entry, NSMapTable *dictionary, 
BDSKMacroResolver *macroResolver, NSString *filePath, NSStringEncoding 
encoding, BOOL *hadCircularMacros)
 {
     // get the field name, there can be several macros in a single entry
     AST *field = NULL;

Modified: trunk/bibdesk/BDSKCompletionManager.h
===================================================================
--- trunk/bibdesk/BDSKCompletionManager.h       2024-01-01 15:30:23 UTC (rev 
28500)
+++ trunk/bibdesk/BDSKCompletionManager.h       2024-01-01 18:13:36 UTC (rev 
28501)
@@ -57,7 +57,7 @@
 - (NSArray *)entry:(NSString *)entry completions:(NSArray *)words 
forPartialWordRange:(NSRange)charRange ofString:(NSString *)fullString 
indexOfSelectedItem:(NSInteger *)index;
 
 - (NSRange)rangeForUserCompletion:(NSRange)charRange forBibTeXString:(NSString 
*)fullString;
-- (NSArray *)possibleMatches:(NSDictionary *)definitions 
forBibTeXString:(NSString *)fullString partialWordRange:(NSRange)charRange 
indexOfBestMatch:(NSInteger *)index;
+- (NSArray *)possibleMatches:(NSMapTable *)definitions 
forBibTeXString:(NSString *)fullString partialWordRange:(NSRange)charRange 
indexOfBestMatch:(NSInteger *)index;
 - (NSString *)possibleMatch:(NSString *)match;
 
 @end

Modified: trunk/bibdesk/BDSKCompletionManager.m
===================================================================
--- trunk/bibdesk/BDSKCompletionManager.m       2024-01-01 15:30:23 UTC (rev 
28500)
+++ trunk/bibdesk/BDSKCompletionManager.m       2024-01-01 18:13:36 UTC (rev 
28501)
@@ -285,7 +285,7 @@
        return charRange;
 }
 
-- (NSArray *)possibleMatches:(NSDictionary *)definitions 
forBibTeXString:(NSString *)fullString partialWordRange:(NSRange)charRange 
indexOfBestMatch:(NSInteger *)idx{
+- (NSArray *)possibleMatches:(NSMapTable *)definitions 
forBibTeXString:(NSString *)fullString partialWordRange:(NSRange)charRange 
indexOfBestMatch:(NSInteger *)idx{
     NSString *partialString = [fullString substringWithRange:charRange];
     NSMutableArray *matches = [NSMutableArray array];
     NSString *key = nil;
@@ -293,7 +293,7 @@
     // Search the definitions case-insensitively; we match on key or value, 
but only return keys.
     for (key in definitions) {
         if ([key rangeOfString:partialString 
options:NSCaseInsensitiveSearch].location != NSNotFound ||
-            ([definitions valueForKey:key] != nil && [[definitions 
valueForKey:key] rangeOfString:partialString 
options:NSCaseInsensitiveSearch].location != NSNotFound))
+            ([definitions objectForKey:key] != nil && [[definitions 
objectForKey:key] rangeOfString:partialString 
options:NSCaseInsensitiveSearch].location != NSNotFound))
             [matches addObject:key];
     }
     [matches sortUsingSelector:@selector(caseInsensitiveCompare:)];
@@ -311,7 +311,7 @@
     // we show the expansion in the list, this is removed by possibleMatch: 
when actually inserting
     NSMutableArray *displayMatches = [NSMutableArray array];
     for (key in matches)
-        [displayMatches addObject:[NSString stringWithFormat:@"%@ [%@]", key, 
[definitions valueForKey:key]]];
+        [displayMatches addObject:[NSString stringWithFormat:@"%@ [%@]", key, 
[definitions objectForKey:key]]];
     
     return displayMatches;
 }

Modified: trunk/bibdesk/BDSKDocumentInfoWindowController.h
===================================================================
--- trunk/bibdesk/BDSKDocumentInfoWindowController.h    2024-01-01 15:30:23 UTC 
(rev 28500)
+++ trunk/bibdesk/BDSKDocumentInfoWindowController.h    2024-01-01 18:13:36 UTC 
(rev 28501)
@@ -44,7 +44,7 @@
     NSSegmentedControl *addRemoveButton;
     NSButton *okButton;
     NSButton *cancelButton;
-    NSMutableDictionary *info;
+    NSMapTable *info;
     NSMutableArray *keys;
     BOOL ignoreEdit;
 }
@@ -59,6 +59,6 @@
 - (IBAction)changeKey:(id)sender;
 - (IBAction)changeValue:(id)sender;
 
-@property (nonatomic, copy) NSDictionary *info;
+@property (nonatomic, copy) NSMapTable *info;
 
 @end

Modified: trunk/bibdesk/BDSKDocumentInfoWindowController.m
===================================================================
--- trunk/bibdesk/BDSKDocumentInfoWindowController.m    2024-01-01 15:30:23 UTC 
(rev 28500)
+++ trunk/bibdesk/BDSKDocumentInfoWindowController.m    2024-01-01 18:13:36 UTC 
(rev 28501)
@@ -42,6 +42,7 @@
 #import "NSString_BDSKExtensions.h"
 #import "BDSKTouchBarButtonGroup.h"
 #import "NSTableView_BDSKExtensions.h"
+#import "NSPointerFunctions_BDSKExtensions.h"
 
 #define BDSKTouchBarItemIdentifierButtons 
@"edu.ucsd.mmccrack.bibdesk.touchbar-item.buttons"
 
@@ -52,7 +53,7 @@
 - (id)init {
     self = [super initWithWindowNibName:@"DocumentInfoWindow"];
     if (self) {
-        info = [NSMutableDictionary newForCaseInsensitiveKeys];
+        info = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0];
         keys = nil;
         ignoreEdit = NO;
     }
@@ -71,7 +72,9 @@
 - (void)refreshKeys{
     if (keys == nil)
         keys = [[NSMutableArray alloc] init];
-    [keys setArray:[info allKeys]];
+    [keys removeAllObjects];
+    for (NSString *key in info)
+        [keys addObject:key];
     [keys sortUsingSelector:@selector(compare:)];
 }
 
@@ -95,8 +98,11 @@
     [self finalizeChangesIgnoringEdit:YES];
 }
 
-- (void)setInfo:(NSDictionary *)newInfo {
-    [info setDictionary:newInfo];
+- (void)setInfo:(NSMapTable *)newInfo {
+    if (info != newInfo) {
+        [info release];
+        info = [newInfo copy];
+    }
     [self refreshKeys];
 }
 
@@ -135,8 +141,9 @@
         id responder = [[self window] firstResponder];
         if ([responder isKindOfClass:[NSText class]] && [tableView 
rowForView:responder] != -1 && [[self window] makeFirstResponder:nil] == NO)
             [[self window] endEditingFor:[responder delegate]];
-
-        [info removeObjectsForKeys:[keys objectsAtIndexes:[tableView 
selectedRowIndexes]]];
+        
+        for (NSString *key in [keys objectsAtIndexes:[tableView 
selectedRowIndexes]])
+            [info removeObjectForKey:key];
         [self refreshKeys];
         [tableView reloadData];
         

Modified: trunk/bibdesk/BDSKMacroResolver.h
===================================================================
--- trunk/bibdesk/BDSKMacroResolver.h   2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKMacroResolver.h   2024-01-01 18:13:36 UTC (rev 28501)
@@ -53,7 +53,7 @@
 @protocol BDSKOwner;
 
 @interface BDSKMacroResolver : NSObject {
-    NSMutableDictionary *macroDefinitions;
+    NSMapTable *macroDefinitions;
     __weak id<BDSKOwner>owner;
     unsigned long long modification;
 }
@@ -68,10 +68,10 @@
 
 @property (nonatomic, readonly) NSString *bibTeXString;
 
-@property (nonatomic, copy) NSDictionary *macroDefinitions;
+@property (nonatomic, copy) NSMapTable *macroDefinitions;
 
 // returns global definitions + local overrides
-- (NSDictionary *)allMacroDefinitions;
+- (NSMapTable *)allMacroDefinitions;
 
 - (NSString *)valueOfMacro:(NSString *)macro;
 // these use undo
@@ -79,7 +79,7 @@
 - (void)changeMacro:(NSString *)oldMacro to:(NSString *)newMacro;
 
 - (BOOL)string:(NSString *)string dependsOnMacro:(NSString *)macro;
-- (BOOL)string:(NSString *)string dependsOnMacro:(NSString *)macro 
inMacroDefinitions:(NSDictionary *)dictionary;
+- (BOOL)string:(NSString *)string dependsOnMacro:(NSString *)macro 
inMacroDefinitions:(NSMapTable *)dictionary;
 
 @property (nonatomic, readonly) unsigned long long modification;
 

Modified: trunk/bibdesk/BDSKMacroResolver.m
===================================================================
--- trunk/bibdesk/BDSKMacroResolver.m   2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKMacroResolver.m   2024-01-01 18:13:36 UTC (rev 28501)
@@ -47,6 +47,7 @@
 #import "BibDocument.h"
 #import "NSError_BDSKExtensions.h"
 #import "NSString_BDSKExtensions.h"
+#import "NSPointerFunctions_BDSKExtensions.h"
 
 NSString *BDSKMacroResolverTypeKey = @"type";
 NSString *BDSKMacroResolverMacroKey = @"macro";
@@ -62,11 +63,11 @@
 static char BDSKMacroResolverDefaultsObservationContext;
 
 @interface BDSKGlobalMacroResolver : BDSKMacroResolver {
-    NSDictionary *standardMacroDefinitions;
-    NSMutableDictionary *fileMacroDefinitions;
+    NSMapTable *standardMacroDefinitions;
+    NSMapTable *fileMacroDefinitions;
 }
 
-- (NSDictionary *)fileMacroDefinitions;
+- (NSMapTable *)fileMacroDefinitions;
 - (void)loadMacrosFromFiles;
 - (void)synchronize;
 
@@ -124,8 +125,12 @@
     
     // bibtex requires that macros whose definitions contain macros are 
ordered in the document after the macros on which they depend
     NSMutableArray *orderedMacros = [NSMutableArray array];
+    NSMutableArray *sortedMacros = [NSMutableArray array];
     
-    for (NSString *macro in [[macroDefinitions allKeys] 
sortedArrayUsingSelector:@selector(compare:)])
+    for (NSString *macro in macroDefinitions)
+        [sortedMacros addObject:macro];
+    [sortedMacros sortUsingSelector:@selector(compare:)];
+    for (NSString *macro in sortedMacros)
         [self addMacro:macro toArray:orderedMacros];
     
     BOOL shouldTeXify = [[NSUserDefaults standardUserDefaults] 
boolForKey:BDSKShouldTeXifyWhenSavingAndCopyingKey];
@@ -145,7 +150,7 @@
     return [self string:string dependsOnMacro:macro inMacroDefinitions:nil];
 }
 
-- (BOOL)string:(NSString *)string dependsOnMacro:(NSString *)macro 
inMacroDefinitions:(NSDictionary *)dictionary {
+- (BOOL)string:(NSString *)string dependsOnMacro:(NSString *)macro 
inMacroDefinitions:(NSMapTable *)dictionary {
     if ([string isComplex] == NO) 
         return NO;
     
@@ -171,22 +176,29 @@
 #pragma mark Macros management
 
 // used for autocompletion; returns global macro definitions + local 
(document) definitions
-- (NSDictionary *)allMacroDefinitions {
-    NSMutableDictionary *allDefs = [[[[BDSKMacroResolver defaultMacroResolver] 
allMacroDefinitions] mutableCopy] autorelease];
-    [allDefs addEntriesFromDictionary:[self macroDefinitions]];
+- (NSMapTable *)allMacroDefinitions {
+    NSMapTable *allDefs = [[BDSKMacroResolver defaultMacroResolver] 
allMacroDefinitions];
+    NSMapTable *table = [self macroDefinitions];
+    for (NSString *macro in table)
+        [allDefs setObject:[table objectForKey:macro] forKey:macro];
     return allDefs;
 }
 
-- (NSDictionary *)macroDefinitions {
+- (NSMapTable *)macroDefinitions {
     if (macroDefinitions == nil)
         [self loadMacroDefinitions];
     return macroDefinitions;
 }
 
-- (void)setMacroDefinitions:(NSDictionary *)dictionary {
+- (void)setMacroDefinitions:(NSMapTable *)newMacroDefinitions {
     if (macroDefinitions == nil)
         [self loadMacroDefinitions];
-    [macroDefinitions setDictionary:dictionary];
+    if (newMacroDefinitions == nil) {
+        [macroDefinitions removeAllObjects];
+    } else if (newMacroDefinitions != macroDefinitions) {
+        [macroDefinitions release];
+        macroDefinitions = [newMacroDefinitions copy];
+    }
     
     modification++;
 
@@ -263,7 +275,7 @@
     // It is not quite correct because bibtex does discriminate,
     // but this is the best we can do.  The 
OFCreateCaseInsensitiveKeyMutableDictionary()
     // is used to create a dictionary with case-insensitive keys.
-    macroDefinitions = [NSMutableDictionary newForCaseInsensitiveKeys];
+    macroDefinitions = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0];
     modification++;
 }
 
@@ -299,7 +311,10 @@
         NSString *shortMonthNames[12] = {@"jan", @"feb", @"mar", @"apr", 
@"may", @"jun", @"jul", @"aug", @"sep", @"oct", @"nov", @"dec"};
         NSString *monthNames[12];
         [[formatter standaloneMonthSymbols] getObjects:monthNames 
range:NSMakeRange(0, 12)];
-        standardMacroDefinitions = [NSDictionary newWithObjects:monthNames 
forCaseInsensitiveKeys:shortMonthNames count:12];
+        standardMacroDefinitions = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0];
+        NSUInteger i;
+        for (i = 0; i < 12; i++)
+            [standardMacroDefinitions setObject:monthNames[i] 
forKey:shortMonthNames[i]];
         // these need to be loaded lazily, because loading them can use 
ourselves, but we aren't yet initialized
         fileMacroDefinitions = nil; 
                
@@ -322,13 +337,8 @@
 - (void)loadMacroDefinitions{
     NSUserDefaults*sud = [NSUserDefaults standardUserDefaults];
     
-    macroDefinitions = [NSMutableDictionary newForCaseInsensitiveKeys];
+    macroDefinitions = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0];;
     
-    // legacy, load old style prefs
-    NSDictionary *oldMacros = [sud 
dictionaryForKey:BDSKBibStyleMacroDefinitionsKey];
-    if ([oldMacros count])
-        [macroDefinitions addEntriesFromDictionary:oldMacros];
-    
     NSDictionary *macros = [sud 
dictionaryForKey:BDSKGlobalMacroDefinitionsKey];
     NSString *value;
     NSError *error = nil;
@@ -342,11 +352,6 @@
             NSLog(@"Ignoring invalid complex macro: %@", [error 
localizedDescription]);
         }
     }
-    if ([oldMacros count]) {
-        // we remove the old style prefs, as they are now merged with the new 
ones
-        [sud removeObjectForKey:BDSKBibStyleMacroDefinitionsKey];
-        [self synchronize];
-    }
     modification++;
 }
 
@@ -353,7 +358,7 @@
 - (void)loadMacrosFromFiles{
     NSUserDefaults*sud = [NSUserDefaults standardUserDefaults];
     
-    fileMacroDefinitions = [NSMutableDictionary newForCaseInsensitiveKeys];
+    fileMacroDefinitions = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0];
     
     for (NSString *file in [sud stringArrayForKey:BDSKGlobalMacroFilesKey]) {
         NSString *fileContent = [NSString stringWithContentsOfURL:[NSURL 
fileURLWithPath:file isDirectory:NO] guessedEncoding:0];
@@ -387,14 +392,18 @@
     [macros release];
 }
 
-- (NSDictionary *)allMacroDefinitions {
-    NSMutableDictionary *allDefs = [[standardMacroDefinitions mutableCopy] 
autorelease];
-    [allDefs addEntriesFromDictionary:[self fileMacroDefinitions]];
-    [allDefs addEntriesFromDictionary:[self macroDefinitions]];
+- (NSMapTable *)allMacroDefinitions {
+    NSMapTable *allDefs = [[standardMacroDefinitions copy] autorelease];
+    NSMapTable *table = [self fileMacroDefinitions];
+    for (NSString *macro in table)
+        [allDefs setObject:[table objectForKey:macro] forKey:macro];
+    table = [self macroDefinitions];
+    for (NSString *macro in table)
+        [allDefs setObject:[table objectForKey:macro] forKey:macro];
     return allDefs;
 }
 
-- (NSDictionary *)fileMacroDefinitions{
+- (NSMapTable *)fileMacroDefinitions{
     if (fileMacroDefinitions == nil)
         [self loadMacrosFromFiles];
     return fileMacroDefinitions;

Modified: trunk/bibdesk/BDSKMacroWindowController.m
===================================================================
--- trunk/bibdesk/BDSKMacroWindowController.m   2024-01-01 15:30:23 UTC (rev 
28500)
+++ trunk/bibdesk/BDSKMacroWindowController.m   2024-01-01 18:13:36 UTC (rev 
28501)
@@ -185,7 +185,7 @@
 }
 
 - (void)reloadMacros {
-    NSDictionary *macroDefinitions = showAll ? [macroResolver 
allMacroDefinitions] : [macroResolver macroDefinitions];
+    NSMapTable *macroDefinitions = showAll ? [macroResolver 
allMacroDefinitions] : [macroResolver macroDefinitions];
     NSMutableArray *tmpMacros = [NSMutableArray 
arrayWithCapacity:[macroDefinitions count]];
     
     for (NSString *key in macroDefinitions) {
@@ -279,7 +279,7 @@
     if (sender && [sender selectedSegment] == 0) { // add
         
         BDSKASSERT(isEditable);
-        NSDictionary *macroDefinitions = [macroResolver macroDefinitions];
+        NSMapTable *macroDefinitions = [macroResolver macroDefinitions];
         // find a unique new macro key
         NSInteger i = 0;
         NSString *newKey = @"macro";
@@ -360,7 +360,7 @@
     NSInteger row = [tableView rowForView:sender];
     NSArray *arrangedMacros = [arrayController arrangedObjects];
     NSParameterAssert(row >= 0 && row < (NSInteger)[arrangedMacros count]);
-    NSDictionary *macroDefinitions = [macroResolver macroDefinitions];
+    NSMapTable *macroDefinitions = [macroResolver macroDefinitions];
     BDSKMacro *macro = [arrangedMacros objectAtIndex:row];
     NSString *key = [macro name];
     NSString *object = [sender objectValue];
@@ -430,7 +430,7 @@
     
     NSArray *arrangedMacros = [arrayController arrangedObjects];
     NSParameterAssert(row >= 0 && row < (NSInteger)[arrangedMacros count]);
-    NSDictionary *macroDefinitions = [macroResolver macroDefinitions];
+    NSMapTable *macroDefinitions = [macroResolver macroDefinitions];
     BDSKMacro *macro = [arrangedMacros objectAtIndex:row];
     NSString *key = [macro name];
     NSString *object = [sender objectValue];

Modified: trunk/bibdesk/BDSKScriptGroup.m
===================================================================
--- trunk/bibdesk/BDSKScriptGroup.m     2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKScriptGroup.m     2024-01-01 18:13:36 UTC (rev 28501)
@@ -285,7 +285,7 @@
         outputString = [outputString stringWithPhoneyCiteKeys:[BibItem 
placeholderCiteKey]];
         type = BDSKStringTypeBibTeX;
     }
-    NSDictionary *macros = nil;
+    NSMapTable *macros = nil;
     
     if (type == BDSKStringTypeBibTeX) {
         pubs = [BDSKBibTeXParser itemsFromData:[outputString 
dataUsingEncoding:NSUTF8StringEncoding] macros:&macros filePath:@"" owner:self 
encoding:NSUTF8StringEncoding error:&error];

Modified: trunk/bibdesk/BDSKSharedGroup.m
===================================================================
--- trunk/bibdesk/BDSKSharedGroup.m     2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKSharedGroup.m     2024-01-01 18:13:36 UTC (rev 28501)
@@ -43,6 +43,7 @@
 #import "BDSKPublicationsArray.h"
 #import "BDSKMacroResolver.h"
 #import "BibItem.h"
+#import "NSPointerFunctions_BDSKExtensions.h"
 
 
 @implementation BDSKSharedGroup
@@ -187,13 +188,20 @@
     NSData *pubsArchive = [client archivedPublications];
     NSData *macrosArchive = [client archivedMacros];
     NSArray *pubs = nil;
-    NSDictionary *macros = nil;
+    NSMapTable *macros = nil;
     
     [NSString setMacroResolverForUnarchiving:[self macroResolver]];
     if (pubsArchive)
         pubs = [NSKeyedUnarchiver unarchiveObjectWithData:pubsArchive];
-    if (macrosArchive)
-        macros = [NSKeyedUnarchiver unarchiveObjectWithData:macrosArchive];
+    if (macrosArchive) {
+        NSDictionary *dict = [NSKeyedUnarchiver 
unarchiveObjectWithData:macrosArchive];
+        if (dict) {
+            macros = [[[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0] autorelease];
+            [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString 
*value, BOOL *stop){
+                [macros setObject:[dict objectForKey:key] forKey:key];
+            }];
+        }
+    }
     [NSString setMacroResolverForUnarchiving:nil];
     
     // we set the macroResolver so we know the fields of this item may refer 
to it, so we can prevent scripting from adding this to the wrong document

Modified: trunk/bibdesk/BDSKSharingServer.m
===================================================================
--- trunk/bibdesk/BDSKSharingServer.m   2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKSharingServer.m   2024-01-01 18:13:36 UTC (rev 28501)
@@ -844,7 +844,9 @@
         NSMutableDictionary *allMacros = [[NSMutableDictionary alloc] init];
         for (BibDocument *doc in [NSApp orderedDocuments]) {
             [allPubs addObjectsFromArray:[doc publications]];
-            [allMacros addEntriesFromDictionary:[[doc macroResolver] 
macroDefinitions]];
+            NSMapTable *macroDefs = [[doc macroResolver] macroDefinitions];
+            for (NSString *key in macroDefs)
+                [allMacros setObject:[macroDefs objectForKey:key] forKey:key];
         }
         NSData *pubsData = [NSKeyedArchiver 
archivedDataWithRootObject:[allPubs allObjects]];
         if ([pubsData length]) {

Modified: trunk/bibdesk/BDSKURLGroup.m
===================================================================
--- trunk/bibdesk/BDSKURLGroup.m        2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BDSKURLGroup.m        2024-01-01 18:13:36 UTC (rev 28501)
@@ -160,7 +160,7 @@
     // tried using -[NSString stringWithContentsOfFile:usedEncoding:error:] 
but it fails too often
     NSString *contentString = [NSString stringWithContentsOfURL:fileURL 
guessedEncoding:0];
     NSArray *pubs = nil;
-    NSDictionary *macros = nil;
+    NSMapTable *macros = nil;
     if (nil == contentString) {
         failedDownload = YES;
         [self setErrorMessage:NSLocalizedString(@"Unable to find content", 
@"Error description")];

Modified: trunk/bibdesk/BibDocument.h
===================================================================
--- trunk/bibdesk/BibDocument.h 2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BibDocument.h 2024-01-01 18:13:36 UTC (rev 28501)
@@ -230,7 +230,7 @@
 
     BDSKMacroResolver *macroResolver;
        
-    NSMutableDictionary *documentInfo;
+    NSMapTable *documentInfo;
     
        NSString *frontMatter;    // for preambles, and stuff
        
@@ -369,7 +369,7 @@
 
 @property (nonatomic, readonly) BDSKGroupsArray *groups;
 
-@property (nonatomic, copy) NSDictionary *documentInfo;
+@property (nonatomic, copy) NSMapTable *documentInfo;
 - (NSString *)documentInfoForKey:(NSString *)key;
 - (void)setDocumentInfo:(NSString *)value forKey:(NSString *)key;
 - (id)valueForUndefinedKey:(NSString *)key;

Modified: trunk/bibdesk/BibDocument.m
===================================================================
--- trunk/bibdesk/BibDocument.m 2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BibDocument.m 2024-01-01 18:13:36 UTC (rev 28501)
@@ -126,6 +126,7 @@
 #import "NSObject_BDSKExtensions.h"
 #import "NSLayoutConstraint_BDSKExtensions.h"
 #import "BDSKCompletionManager.h"
+#import "NSPointerFunctions_BDSKExtensions.h"
 
 // these are the same as in Info.plist
 NSString *BDSKBibTeXDocumentType = @"BibTeX Database";
@@ -212,7 +213,7 @@
         groups = [(BDSKGroupsArray *)[BDSKGroupsArray alloc] 
initWithDocument:self];
         
         frontMatter = nil;
-        documentInfo = [NSMutableDictionary newForCaseInsensitiveKeys];
+        documentInfo = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0];
         macroResolver = [[BDSKMacroResolver alloc] initWithOwner:self];
         
         openDate = [[NSDate alloc] init];
@@ -1008,20 +1009,21 @@
 
 #pragma mark Document Info
 
-- (void)setDocumentInfo:(NSDictionary *)dict{
-    if (dict != documentInfo) {
+- (void)setDocumentInfo:(NSMapTable *)docInfo{
+    if (docInfo != documentInfo) {
         [[[self undoManager] prepareWithInvocationTarget:self] 
setDocumentInfo:[[documentInfo copy] autorelease]];
-        [documentInfo setDictionary:dict];
+        [documentInfo release];
+        documentInfo = [docInfo copy];
     }
 }
 
 - (NSString *)documentInfoForKey:(NSString *)key{
-    return [documentInfo valueForKey:key];
+    return [documentInfo objectForKey:key];
 }
 
 - (void)setDocumentInfo:(NSString *)value forKey:(NSString *)key {
     [[[self undoManager] prepareWithInvocationTarget:self] 
setDocumentInfo:[documentInfo objectForKey:key] forKey:key];
-    [documentInfo setValue:value forKey:key];
+    [documentInfo setObject:value forKey:key];
 }
 
 - (id)valueForUndefinedKey:(NSString *)key{
@@ -1869,9 +1871,9 @@
     
     NSError *error = nil;
        NSArray *newPubs;
-       NSDictionary *newMacros = nil;
+       NSMapTable *newMacros = nil;
        NSDictionary *newGroups = nil;
-       NSDictionary *newDocumentInfo = nil;
+       NSMutableDictionary *newDocumentInfo = nil;
        NSString *newFrontMatter = nil;
     
     newPubs = [BDSKBibTeXParser itemsFromData:data macros:&newMacros 
documentInfo:&newDocumentInfo groups:&newGroups frontMatter:&newFrontMatter 
filePath:[fileURL path] owner:self encoding:parserEncoding error:&error];
@@ -1904,7 +1906,11 @@
         [[NSFileManager defaultManager] removeItemAtURL:fileURL error:NULL];
     
     if (error == nil) {
-        [self setPublications:newPubs macros:newMacros 
documentInfo:newDocumentInfo groups:newGroups frontMatter:newFrontMatter 
encoding:encoding];
+        NSMapTable *newDocInfo = [[[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality] capacity:0] autorelease];
+        [newDocumentInfo enumerateKeysAndObjectsUsingBlock:^(NSString *key, 
NSString *value, BOOL *stop){
+            [newDocInfo setObject:value forKey:key];
+        }];
+        [self setPublications:newPubs macros:newMacros documentInfo:newDocInfo 
groups:newGroups frontMatter:newFrontMatter encoding:encoding];
         return YES;
     } else {
         return NO;
@@ -1918,13 +1924,13 @@
     return success;
 }
 
-- (void)setPublications:(NSArray *)newPubs macros:(NSDictionary *)newMacros 
documentInfo:(NSDictionary *)newDocumentInfo groups:(NSDictionary *)newGroups 
frontMatter:(NSString *)newFrontMatter encoding:(NSStringEncoding)newEncoding {
+- (void)setPublications:(NSArray *)newPubs macros:(NSMapTable *)newMacros 
documentInfo:(NSMapTable *)newDocumentInfo groups:(NSDictionary *)newGroups 
frontMatter:(NSString *)newFrontMatter encoding:(NSStringEncoding)newEncoding {
     NSEnumerator *wcEnum = [[self windowControllers] objectEnumerator];
     BOOL wasLoaded = nil != [wcEnum nextObject]; // initial read is before 
makeWindowControllers
     
     if (wasLoaded) {
         NSArray *oldPubs = [[publications copy] autorelease];
-        NSDictionary *oldMacros = [[[[self macroResolver] macroDefinitions] 
copy] autorelease];
+        NSMapTable *oldMacros = [[[[self macroResolver] macroDefinitions] 
copy] autorelease];
         NSMutableDictionary *oldGroups = [NSMutableDictionary dictionary];
         NSData *groupData;
         
@@ -1959,7 +1965,8 @@
     
     [self setDocumentStringEncoding:newEncoding];
     [self setPublications:newPubs];
-    [documentInfo setDictionary:newDocumentInfo];
+    [documentInfo release];
+    documentInfo = [newDocumentInfo copy];
     [[self macroResolver] setMacroDefinitions:newMacros];
     // important that groups are loaded after publications, otherwise the 
static groups won't find their publications
     for (NSNumber *groupType in newGroups)

Modified: trunk/bibdesk/BibDocument_Actions.m
===================================================================
--- trunk/bibdesk/BibDocument_Actions.m 2024-01-01 15:30:23 UTC (rev 28500)
+++ trunk/bibdesk/BibDocument_Actions.m 2024-01-01 18:13:36 UTC (rev 28501)
@@ -1322,8 +1322,8 @@
     [infoController setInfo:[self documentInfo]];
     [infoController beginSheetModalForWindow:documentWindow 
completionHandler:^(NSInteger result){
         if (result == NSModalResponseOK) {
-            NSDictionary *info = [infoController info];
-            if ([info isEqualToDictionary:[self documentInfo]] == NO) {
+            NSMapTable *info = [infoController info];
+            if ([info isEqual:[self documentInfo]] == NO) {
                 [self setDocumentInfo:info];
                 [[self undoManager] setActionName:NSLocalizedString(@"Change 
Document Info", @"Undo action name")];
             }

Modified: trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj
===================================================================
--- trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj     2024-01-01 15:30:23 UTC 
(rev 28500)
+++ trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj     2024-01-01 18:13:36 UTC 
(rev 28501)
@@ -496,6 +496,8 @@
                CE5C29CE0AE14FEC00C3E6B5 /* BibDocument_Actions.m in Sources */ 
= {isa = PBXBuildFile; fileRef = CE5C29CC0AE14FEC00C3E6B5 /* 
BibDocument_Actions.m */; };
                CE5C868926D44E1F00C3A2CE /* BDSKFileImageTransformer.h in 
Headers */ = {isa = PBXBuildFile; fileRef = CE5C868726D44E1F00C3A2CE /* 
BDSKFileImageTransformer.h */; };
                CE5C868A26D44E1F00C3A2CE /* BDSKFileImageTransformer.m in 
Sources */ = {isa = PBXBuildFile; fileRef = CE5C868826D44E1F00C3A2CE /* 
BDSKFileImageTransformer.m */; };
+               CE5CE31F2B43230400952262 /* NSPointerFunctions_BDSKExtensions.h 
in Headers */ = {isa = PBXBuildFile; fileRef = CE5CE31D2B43230400952262 /* 
NSPointerFunctions_BDSKExtensions.h */; };
+               CE5CE3202B43230400952262 /* NSPointerFunctions_BDSKExtensions.m 
in Sources */ = {isa = PBXBuildFile; fileRef = CE5CE31E2B43230400952262 /* 
NSPointerFunctions_BDSKExtensions.m */; };
                CE6003290AF3E465000B5680 /* BDSKColoredView.m in Sources */ = 
{isa = PBXBuildFile; fileRef = CE6003270AF3E465000B5680 /* BDSKColoredView.m 
*/; };
                CE600E300AF405D6000B5680 /* BDSKRISParser.m in Sources */ = 
{isa = PBXBuildFile; fileRef = CE600E2E0AF405D6000B5680 /* BDSKRISParser.m */; 
};
                CE601A4F0AF4C488000B5680 /* BDSKReferenceMinerParser.m in 
Sources */ = {isa = PBXBuildFile; fileRef = CE601A4D0AF4C488000B5680 /* 
BDSKReferenceMinerParser.m */; };
@@ -952,6 +954,69 @@
                        remoteGlobalIDString = CEA5F53D0E2CED0E00F65088;
                        remoteInfo = SkimNotesBase;
                };
+               CE5CE33F2B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 726E07AD1CAF08D6001A286B;
+                       remoteInfo = SparkleInstallerLauncher;
+               };
+               CE5CE3412B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 724BB36C1D31D0B7005D534A;
+                       remoteInfo = SparkleInstallerConnection;
+               };
+               CE5CE3432B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 724BB3931D333832005D534A;
+                       remoteInfo = SparkleInstallerStatus;
+               };
+               CE5CE3452B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 726E07EF1CAF37BD001A286B;
+                       remoteInfo = SparkleDownloader;
+               };
+               CE5CE3472B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 726E4A161C86C88F00C57C6A;
+                       remoteInfo = TestAppHelper;
+               };
+               CE5CE3492B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 72D9549E1CBB415B006F28BD;
+                       remoteInfo = "sparkle-cli";
+               };
+               CE5CE34B2B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = 721C24451CB753E6005440CB;
+                       remoteInfo = "Installer Progress";
+               };
+               CE5CE34D2B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = EA1E280F22B64522004AA304;
+                       remoteInfo = bsdiff;
+               };
+               CE5CE34F2B43230400952262 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = EA1E282D22B660BE004AA304;
+                       remoteInfo = ed25519;
+               };
                CE65AE2122637A4700465ECC /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 29B97313FDCFA39411CA2CEA /* Project 
object */;
@@ -994,13 +1059,6 @@
                        remoteGlobalIDString = 726B2B5D1C645FC900388755;
                        remoteInfo = "UI Tests";
                };
-               CE8766CD2231784C00363006 /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
-                       proxyType = 2;
-                       remoteGlobalIDString = 722954B41D04ADAF00ECF9CA;
-                       remoteInfo = fileop;
-               };
                CE8766CF2231784C00363006 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = CEB34B980F5CB2CC0037E086 /* 
Sparkle.xcodeproj */;
@@ -1540,6 +1598,8 @@
                CE5C29CC0AE14FEC00C3E6B5 /* BibDocument_Actions.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= BibDocument_Actions.m; sourceTree = "<group>"; };
                CE5C868726D44E1F00C3A2CE /* BDSKFileImageTransformer.h */ = 
{isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 
BDSKFileImageTransformer.h; sourceTree = "<group>"; };
                CE5C868826D44E1F00C3A2CE /* BDSKFileImageTransformer.m */ = 
{isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
BDSKFileImageTransformer.m; sourceTree = "<group>"; };
+               CE5CE31D2B43230400952262 /* NSPointerFunctions_BDSKExtensions.h 
*/ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 
NSPointerFunctions_BDSKExtensions.h; sourceTree = "<group>"; };
+               CE5CE31E2B43230400952262 /* NSPointerFunctions_BDSKExtensions.m 
*/ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
NSPointerFunctions_BDSKExtensions.m; sourceTree = "<group>"; };
                CE6003260AF3E465000B5680 /* BDSKColoredView.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
BDSKColoredView.h; sourceTree = "<group>"; };
                CE6003270AF3E465000B5680 /* BDSKColoredView.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= BDSKColoredView.m; sourceTree = "<group>"; };
                CE600E2D0AF405D6000B5680 /* BDSKRISParser.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
BDSKRISParser.h; sourceTree = "<group>"; };
@@ -2724,6 +2784,7 @@
                                CE3402730E014B3800A7FFE6 /* 
NSNumber_BDSKExtensions.m */,
                                CE8C731D0B0CA6C500E31E5A /* 
NSObject_BDSKExtensions.m */,
                                CEC46E2F1629743200B51A9D /* 
NSPasteboard_BDSKExtensions.m */,
+                               CE5CE31E2B43230400952262 /* 
NSPointerFunctions_BDSKExtensions.m */,
                                CE564D240AECBA5B002F0A24 /* 
NSScanner_BDSKExtensions.m */,
                                CE0D24C7215A84F3001A3F47 /* 
NSScriptCommand_BDSKExtensions.m */,
                                CEF94413092668EA00750FFA /* 
NSSortDescriptor_BDSKExtensions.m */,
@@ -3045,15 +3106,23 @@
                        isa = PBXGroup;
                        children = (
                                CEB34BA20F5CB2CC0037E086 /* Sparkle.framework 
*/,
+                               CE8766CA2231784C00363006 /* Autoupdate */,
+                               CE5CE3402B43230400952262 /* Installer.xpc */,
+                               CE5CE3422B43230400952262 /* 
InstallerConnection.xpc */,
+                               CE5CE3442B43230400952262 /* InstallerStatus.xpc 
*/,
+                               CE5CE3462B43230400952262 /* Downloader.xpc */,
                                CEB34BA40F5CB2CC0037E086 /* Sparkle Test 
App.app */,
+                               CE5CE3482B43230400952262 /* TestAppHelper.xpc 
*/,
                                CEB34BA80F5CB2CC0037E086 /* Sparkle Unit 
Tests.xctest */,
                                CE8766C82231784C00363006 /* BinaryDelta */,
-                               CE8766CA2231784C00363006 /* Autoupdate.app */,
+                               CE5CE34A2B43230400952262 /* sparkle.app */,
+                               CE5CE34C2B43230400952262 /* Updater.app */,
                                CE8766CC2231784C00363006 /* UI Tests.xctest */,
-                               CE8766CE2231784C00363006 /* fileop */,
                                CE8766D02231784C00363006 /* generate_appcast */,
                                CE8766D22231784C00363006 /* generate_keys */,
                                CE8766D42231784C00363006 /* sign_update */,
+                               CE5CE34E2B43230400952262 /* libbsdiff.a */,
+                               CE5CE3502B43230400952262 /* libed25519.a */,
                        );
                        name = Products;
                        sourceTree = "<group>";
@@ -3520,6 +3589,7 @@
                                CE8C731C0B0CA6C500E31E5A /* 
NSObject_BDSKExtensions.h */,
                                CE6C04420BEDFA2D007BF0B5 /* 
NSParagraphStyle_BDSKExtensions.h */,
                                CEC46E2E1629743200B51A9D /* 
NSPasteboard_BDSKExtensions.h */,
+                               CE5CE31D2B43230400952262 /* 
NSPointerFunctions_BDSKExtensions.h */,
                                CE7611500EA49B6E00301E45 /* 
NSPrintOperation_BDSKExtensions.h */,
                                CE564D230AECBA5B002F0A24 /* 
NSScanner_BDSKExtensions.h */,
                                CE0D24C6215A84F3001A3F47 /* 
NSScriptCommand_BDSKExtensions.h */,
@@ -3659,6 +3729,7 @@
                                CE2A0A45224599F600A8F31C /* 
BDSKPreferenceRecord.h in Headers */,
                                CE2A0A5D22459A0A00A8F31C /* BDSKScriptHook.h in 
Headers */,
                                CE2A0AC222459A4B00A8F31C /* html2tex.h in 
Headers */,
+                               CE5CE31F2B43230400952262 /* 
NSPointerFunctions_BDSKExtensions.h in Headers */,
                                CE963637283D292F00D8A983 /* 
BDSKTextUndoManager.h in Headers */,
                                CE2A09D8224599B300A8F31C /* 
BDSKCondition+Scripting.h in Headers */,
                                CE2A0A1D224599EF00A8F31C /* 
BDSKISIGroupServer.h in Headers */,
@@ -4129,6 +4200,69 @@
                        remoteRef = CE52E69C0E2D2B87007B6C62 /* 
PBXContainerItemProxy */;
                        sourceTree = BUILT_PRODUCTS_DIR;
                };
+               CE5CE3402B43230400952262 /* Installer.xpc */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = "wrapper.xpc-service";
+                       path = Installer.xpc;
+                       remoteRef = CE5CE33F2B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE3422B43230400952262 /* InstallerConnection.xpc */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = "wrapper.xpc-service";
+                       path = InstallerConnection.xpc;
+                       remoteRef = CE5CE3412B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE3442B43230400952262 /* InstallerStatus.xpc */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = "wrapper.xpc-service";
+                       path = InstallerStatus.xpc;
+                       remoteRef = CE5CE3432B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE3462B43230400952262 /* Downloader.xpc */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = "wrapper.xpc-service";
+                       path = Downloader.xpc;
+                       remoteRef = CE5CE3452B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE3482B43230400952262 /* TestAppHelper.xpc */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = "wrapper.xpc-service";
+                       path = TestAppHelper.xpc;
+                       remoteRef = CE5CE3472B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE34A2B43230400952262 /* sparkle.app */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = wrapper.application;
+                       path = sparkle.app;
+                       remoteRef = CE5CE3492B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE34C2B43230400952262 /* Updater.app */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = wrapper.application;
+                       path = Updater.app;
+                       remoteRef = CE5CE34B2B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE34E2B43230400952262 /* libbsdiff.a */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = archive.ar;
+                       path = libbsdiff.a;
+                       remoteRef = CE5CE34D2B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
+               CE5CE3502B43230400952262 /* libed25519.a */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = archive.ar;
+                       path = libed25519.a;
+                       remoteRef = CE5CE34F2B43230400952262 /* 
PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
                CE7DC8DA0E0952BB00D6D76D /* SkimNotes.framework */ = {
                        isa = PBXReferenceProxy;
                        fileType = wrapper.framework;
@@ -4150,10 +4284,10 @@
                        remoteRef = CE8766C72231784C00363006 /* 
PBXContainerItemProxy */;
                        sourceTree = BUILT_PRODUCTS_DIR;
                };
-               CE8766CA2231784C00363006 /* Autoupdate.app */ = {
+               CE8766CA2231784C00363006 /* Autoupdate */ = {
                        isa = PBXReferenceProxy;
-                       fileType = wrapper.application;
-                       path = Autoupdate.app;
+                       fileType = "compiled.mach-o.executable";
+                       path = Autoupdate;
                        remoteRef = CE8766C92231784C00363006 /* 
PBXContainerItemProxy */;
                        sourceTree = BUILT_PRODUCTS_DIR;
                };
@@ -4164,13 +4298,6 @@
                        remoteRef = CE8766CB2231784C00363006 /* 
PBXContainerItemProxy */;
                        sourceTree = BUILT_PRODUCTS_DIR;
                };
-               CE8766CE2231784C00363006 /* fileop */ = {
-                       isa = PBXReferenceProxy;
-                       fileType = "compiled.mach-o.executable";
-                       path = fileop;
-                       remoteRef = CE8766CD2231784C00363006 /* 
PBXContainerItemProxy */;
-                       sourceTree = BUILT_PRODUCTS_DIR;
-               };
                CE8766D02231784C00363006 /* generate_appcast */ = {
                        isa = PBXReferenceProxy;
                        fileType = "compiled.mach-o.executable";
@@ -4728,6 +4855,7 @@
                                F94DE74C09CB46FF00B5FD51 /* 
BDSKPersistentSearch.m in Sources */,
                                CEF7295709CC88AB00802D48 /* 
BDSKFieldSheetController.m in Sources */,
                                CE3B5E7D09CEDE470017D339 /* BDSKMacroResolver.m 
in Sources */,
+                               CE5CE3202B43230400952262 /* 
NSPointerFunctions_BDSKExtensions.m in Sources */,
                                CEC9B8B5217244AB00108617 /* 
BDSKTemplate+Scripting.m in Sources */,
                                CE3B682B09D1B0190017D339 /* 
BDSKImagePopUpButton.m in Sources */,
                                CE3B682D09D1B0190017D339 /* 
BDSKImagePopUpButtonCell.m in Sources */,

Added: trunk/bibdesk/NSPointerFunctions_BDSKExtensions.h
===================================================================
--- trunk/bibdesk/NSPointerFunctions_BDSKExtensions.h                           
(rev 0)
+++ trunk/bibdesk/NSPointerFunctions_BDSKExtensions.h   2024-01-01 18:13:36 UTC 
(rev 28501)
@@ -0,0 +1,46 @@
+//
+//  NSPointerFunctions_BDSKExtensions.h
+//  BibDesk
+//
+//  Created by Christiaan Hofman on 01/01/2024.
+/*
+ This software is Copyright (c) 2006-2023
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface NSPointerFunctions (BDSKExtensions)
+
++ (NSPointerFunctions *)caseInsensitiveStringPointerFunctions;
+
+@end

Added: trunk/bibdesk/NSPointerFunctions_BDSKExtensions.m
===================================================================
--- trunk/bibdesk/NSPointerFunctions_BDSKExtensions.m                           
(rev 0)
+++ trunk/bibdesk/NSPointerFunctions_BDSKExtensions.m   2024-01-01 18:13:36 UTC 
(rev 28501)
@@ -0,0 +1,80 @@
+//
+//  NSPointerFunctions_BDSKExtensions.m
+//  BibDesk
+//
+//  Created by Christiaan Hofman on 01/01/2024.
+/*
+ This software is Copyright (c) 2006-2023
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "NSPointerFunctions_BDSKExtensions.h"
+
+static BOOL caseInsensitiveStringEqual(const void *item1, const void *item2, 
NSUInteger (*size)(const void *item)) {
+    return CFStringCompare(item1, item2, kCFCompareCaseInsensitive | 
kCFCompareNonliteral) == kCFCompareEqualTo;
+}
+
+#define STACK_BUFFER_SIZE 256
+
+static NSUInteger caseInsensitiveStringHash(const void *item, NSUInteger 
(*size)(const void *item)) {
+    if(item == NULL) return 0;
+    
+    NSUInteger hash = 0;
+    CFAllocatorRef allocator = CFGetAllocator(item);
+    CFIndex len = CFStringGetLength(item);
+    
+    // use a generous length, in case the lowercase changes the number of 
characters
+    UniChar *buffer, stackBuffer[STACK_BUFFER_SIZE];
+    if (len + 10 >= STACK_BUFFER_SIZE)
+        buffer = (UniChar *)CFAllocatorAllocate(allocator, (len + 10) * 
sizeof(UniChar), 0);
+    else
+        buffer = stackBuffer;
+    CFStringGetCharacters(item, CFRangeMake(0, len), buffer);
+    
+    // If we create the string with external characters, 
CFStringGetCharactersPtr is guaranteed to succeed; since we're going to call 
CFStringGetCharacters anyway in fastHash if CFStringGetCharactsPtr fails, let's 
do it now when we lowercase the string
+    CFMutableStringRef mutableString = 
CFStringCreateMutableWithExternalCharactersNoCopy(allocator, buffer, len, len + 
10, (buffer != stackBuffer ? allocator : kCFAllocatorNull));
+    CFStringLowercase(mutableString, NULL);
+    hash = CFHash(mutableString);//[(id)mutableString hash];
+    // if we used the allocator, this should free the buffer for us
+    CFRelease(mutableString);
+    return hash;
+}
+
+@implementation NSPointerFunctions (BDSKExtensions)
+
++ (NSPointerFunctions *)caseInsensitiveStringPointerFunctions {
+    NSPointerFunctions *pointerFunctions = [self 
pointerFunctionsWithOptions:NSPointerFunctionsWeakMemory | 
NSPointerFunctionsObjectPersonality];;
+    [pointerFunctions setIsEqualFunction:&caseInsensitiveStringEqual];
+    [pointerFunctions setHashFunction:&caseInsensitiveStringHash];
+    return pointerFunctions;
+}
+
+@end

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit

Reply via email to