Revision: 12999
http://bibdesk.svn.sourceforge.net/bibdesk/?rev=12999&view=rev
Author: hofman
Date: 2008-03-07 14:50:38 -0800 (Fri, 07 Mar 2008)
Log Message:
-----------
Add some substring comparisons for linked files and URLs for smart group
conditions.
Modified Paths:
--------------
trunk/bibdesk/BDSKCondition+Scripting.h
trunk/bibdesk/BDSKCondition+Scripting.m
trunk/bibdesk/BDSKCondition.h
trunk/bibdesk/BDSKCondition.m
trunk/bibdesk/BDSKConditionController.h
trunk/bibdesk/BDSKConditionController.m
trunk/bibdesk/Info.plist
Modified: trunk/bibdesk/BDSKCondition+Scripting.h
===================================================================
--- trunk/bibdesk/BDSKCondition+Scripting.h 2008-03-07 18:51:57 UTC (rev
12998)
+++ trunk/bibdesk/BDSKCondition+Scripting.h 2008-03-07 22:50:38 UTC (rev
12999)
@@ -42,7 +42,10 @@
@interface BDSKCondition (Scripting)
- (NSString *)scriptingKey;
+- (void)setScriptingKey:(NSString *)newKey;
- (int)scriptingComparison;
+- (void)setScriptingComparison:(int)newComparison;
- (id)scriptingValue;
+- (void)setScriptingValue:(id)newValue;
@end
Modified: trunk/bibdesk/BDSKCondition+Scripting.m
===================================================================
--- trunk/bibdesk/BDSKCondition+Scripting.m 2008-03-07 18:51:57 UTC (rev
12998)
+++ trunk/bibdesk/BDSKCondition+Scripting.m 2008-03-07 22:50:38 UTC (rev
12999)
@@ -129,13 +129,17 @@
case BDSKInDateRange: return BDSKInDateRange;
default: return BDSKASToday;
}
- } else if ([self isCountCondition]) {
- switch ([self countComparison]) {
- case BDSKCountEqual: return BDSKASCountEqual;
- case BDSKCountNotEqual: return BDSKASCountNotEqual;
- case BDSKCountLarger: return BDSKASCountLarger;
- case BDSKCountSmaller: return BDSKASCountSmaller;
- default: return BDSKASCountEqual;
+ } else if ([self isAttachmentCondition]) {
+ switch ([self attachmentComparison]) {
+ case BDSKCountEqual: return BDSKASCountEqual;
+ case BDSKCountNotEqual: return BDSKASCountNotEqual;
+ case BDSKCountLarger: return BDSKASCountLarger;
+ case BDSKCountSmaller: return BDSKASCountSmaller;
+ case BDSKAttachmentContain: return BDSKASContain;
+ case BDSKAttachmentNotContain: return BDSKASNotContain;
+ case BDSKAttachmentStartWith: return BDSKASStartWith;
+ case BDSKAttachmentEndWith: return BDSKASEndWith;
+ default: return BDSKASCountEqual;
}
} else {
switch ([self stringComparison]) {
@@ -179,12 +183,16 @@
[cmd setScriptErrorString:NSLocalizedString(@"Invalid
condition for smart condition.",@"Error description")];
break;
}
- } else if ([self isCountCondition]) {
+ } else if ([self isAttachmentCondition]) {
switch (newComparison) {
- case BDSKASCountEqual: [self
setCountComparison:BDSKCountEqual]; break;
- case BDSKASCountNotEqual: [self
setCountComparison:BDSKCountNotEqual]; break;
- case BDSKASCountLarger: [self
setCountComparison:BDSKCountLarger]; break;
- case BDSKASCountSmaller: [self
setCountComparison:BDSKCountSmaller]; break;
+ case BDSKASCountEqual: [self
setAttachmentComparison:BDSKCountEqual]; break;
+ case BDSKASCountNotEqual: [self
setAttachmentComparison:BDSKCountNotEqual]; break;
+ case BDSKASCountLarger: [self
setAttachmentComparison:BDSKCountLarger]; break;
+ case BDSKASCountSmaller: [self
setAttachmentComparison:BDSKCountSmaller]; break;
+ case BDSKASContain: [self
setAttachmentComparison:BDSKAttachmentContain]; break;
+ case BDSKASNotContain: [self
setAttachmentComparison:BDSKAttachmentNotContain]; break;
+ case BDSKASStartWith: [self
setAttachmentComparison:BDSKAttachmentStartWith]; break;
+ case BDSKASEndWith: [self
setAttachmentComparison:BDSKAttachmentEndWith]; break;
default:
[cmd setScriptErrorNumber:NSArgumentsWrongScriptError];
[cmd setScriptErrorString:NSLocalizedString(@"Invalid
condition for smart condition.",@"Error description")];
@@ -236,8 +244,20 @@
default:
return [NSNull null];
}
- } else if ([self isCountCondition]) {
- return [NSNumber numberWithInt:countValue];
+ } else if ([self isAttachmentCondition]) {
+ switch ([self attachmentComparison]) {
+ case BDSKCountEqual:
+ case BDSKCountNotEqual:
+ case BDSKCountLarger:
+ case BDSKCountSmaller:
+ return [NSNumber numberWithInt:countValue];
+ case BDSKAttachmentContain:
+ case BDSKAttachmentNotContain:
+ case BDSKAttachmentStartWith:
+ case BDSKAttachmentEndWith:
+ default:
+ return [self stringValue];
+ }
} else {
return [self stringValue];
}
@@ -274,9 +294,23 @@
[cmd setScriptErrorNumber:NSArgumentsWrongScriptError];
[cmd setScriptErrorString:NSLocalizedString(@"Invalid value for
smart condition.",@"Error description")];
}
- } else if ([self isCountCondition]) {
+ } else if ([self isAttachmentCondition]) {
if ([newValue isKindOfClass:[NSNumber class]] || [newValue
isKindOfClass:[NSString class]]) {
- [self setCountValue:[newValue intValue]];
+ switch ([self attachmentComparison]) {
+ case BDSKCountEqual:
+ case BDSKCountNotEqual:
+ case BDSKCountLarger:
+ case BDSKCountSmaller:
+ [self setCountValue:[newValue intValue]];
+ break;
+ case BDSKAttachmentContain:
+ case BDSKAttachmentNotContain:
+ case BDSKAttachmentStartWith:
+ case BDSKAttachmentEndWith:
+ default:
+ [self setStringValue:newValue];
+ break;
+ }
} else {
[cmd setScriptErrorNumber:NSArgumentsWrongScriptError];
[cmd setScriptErrorString:NSLocalizedString(@"Invalid value for
smart condition.",@"Error description")];
Modified: trunk/bibdesk/BDSKCondition.h
===================================================================
--- trunk/bibdesk/BDSKCondition.h 2008-03-07 18:51:57 UTC (rev 12998)
+++ trunk/bibdesk/BDSKCondition.h 2008-03-07 22:50:38 UTC (rev 12999)
@@ -57,8 +57,12 @@
BDSKCountEqual = 0,
BDSKCountNotEqual,
BDSKCountLarger,
- BDSKCountSmaller
-} BDSKCountComparison;
+ BDSKCountSmaller,
+ BDSKAttachmentContain,
+ BDSKAttachmentNotContain,
+ BDSKAttachmentStartWith,
+ BDSKAttachmentEndWith
+} BDSKAttachmentComparison;
// this should correspond to the tags of the items in the popup
typedef enum {
@@ -90,7 +94,7 @@
@interface BDSKCondition : NSObject <NSCopying, NSCoding> {
NSString *key;
BDSKStringComparison stringComparison;
- BDSKCountComparison countComparison;
+ BDSKAttachmentComparison attachmentComparison;
BDSKDateComparison dateComparison;
NSString *stringValue;
int countValue;
@@ -128,8 +132,8 @@
- (void)setStringValue:(NSString *)newValue;
// Count accessors
-- (BDSKCountComparison)countComparison;
-- (void)setCountComparison:(BDSKCountComparison)newComparison;
+- (BDSKAttachmentComparison)attachmentComparison;
+- (void)setAttachmentComparison:(BDSKAttachmentComparison)newComparison;
- (int)countValue;
- (void)setCountValue:(int)newValue;
@@ -151,7 +155,7 @@
- (void)setDefaultValue;
- (BOOL)isDateCondition;
-- (BOOL)isCountCondition;
+- (BOOL)isAttachmentCondition;
- (BDSKSmartGroup *)group;
- (void)setGroup:(BDSKSmartGroup *)newGroup;
Modified: trunk/bibdesk/BDSKCondition.m
===================================================================
--- trunk/bibdesk/BDSKCondition.m 2008-03-07 18:51:57 UTC (rev 12998)
+++ trunk/bibdesk/BDSKCondition.m 2008-03-07 22:50:38 UTC (rev 12999)
@@ -61,7 +61,7 @@
@implementation BDSKCondition
+ (void)initialize {
- [self setKeys:[NSArray arrayWithObjects:@"stringComparison",
@"countComparison", @"dateComparison", nil]
triggerChangeNotificationsForDependentKey:@"comparison"];
+ [self setKeys:[NSArray arrayWithObjects:@"stringComparison",
@"attachmentComparison", @"dateComparison", nil]
triggerChangeNotificationsForDependentKey:@"comparison"];
[self setKeys:[NSArray arrayWithObjects:@"stringValue", @"countValue",
@"numberValue", @"andNumberValue", @"periodValue", @"dateValue",
@"toDateValue", nil] triggerChangeNotificationsForDependentKey:@"value"];
}
@@ -71,15 +71,10 @@
- (id)init {
if (self = [super init]) {
- // when called from scripting we need to set the key first, so
scripting setters know what type of field it is
- NSScriptCommand *cmd = [NSScriptCommand currentCommand];
- if ([cmd isKindOfClass:[NSCreateCommand class]] && [[[(NSCreateCommand
*)cmd createClassDescription] className] isEqualToString:@"condition"])
- key = [[[(NSCreateCommand *)cmd resolvedKeyDictionary]
objectForKey:@"scriptingKey"] retain];
- if (key == nil)
- key = [@"" retain];
+ key = [@"" retain];
stringValue = [@"" retain];
stringComparison = BDSKContain;
- countComparison = BDSKCountNotEqual;
+ attachmentComparison = BDSKCountNotEqual;
countValue = 0;
dateComparison = BDSKToday;
numberValue = 0;
@@ -91,6 +86,16 @@
cachedStartDate = nil;
cachedEndDate = nil;
cacheTimer = nil;
+
+ // when called from scripting we need to set the key and comparison
first, so scripting setters know what type of field it is
+ NSScriptCommand *cmd = [NSScriptCommand currentCommand];
+ if ([cmd isKindOfClass:[NSCreateCommand class]] && [[[(NSCreateCommand
*)cmd createClassDescription] className] isEqualToString:@"condition"]) {
+ [self setKey:[[(NSCreateCommand *)cmd resolvedKeyDictionary]
objectForKey:@"scriptingKey"]];
+ NSNumber *comparisonNumber = [[(NSCreateCommand *)cmd
resolvedKeyDictionary] objectForKey:@"scriptingComparison"];
+ if (comparisonNumber)
+ [self setScriptingComparison:[comparisonNumber intValue]];
+ }
+
[self startObserving];
}
return self;
@@ -203,7 +208,7 @@
return ((cachedStartDate == nil || [date compare:cachedStartDate] ==
NSOrderedDescending) &&
(cachedEndDate == nil || [date compare:cachedEndDate] ==
NSOrderedAscending));
- } else if ([self isCountCondition]) {
+ } else if ([self isAttachmentCondition]) {
int count = 0;
if ([key isEqualToString:BDSKLocalFileString])
@@ -211,7 +216,7 @@
else if ([key isEqualToString:BDSKRemoteURLString])
count = [[item remoteURLs] count];
- switch (countComparison) {
+ switch (attachmentComparison) {
case BDSKCountEqual:
return count == countValue;
case BDSKCountNotEqual:
@@ -222,6 +227,28 @@
return count < countValue;
}
+ NSArray *itemValues = nil;
+ if ([key isEqualToString:BDSKLocalFileString])
+ itemValues = [[item existingLocalFiles] valueForKey:@"path"];
+ else if ([key isEqualToString:BDSKRemoteURLString])
+ itemValues = [[item remoteURLs] valueForKey:@"absoluteString"];
+ NSEnumerator *itemEnum = [itemValues objectEnumerator];
+ NSString *itemValue;
+
+ CFOptionFlags options = kCFCompareCaseInsensitive;
+ if (attachmentComparison == BDSKAttachmentEndWith)
+ options |= kCFCompareBackwards | kCFCompareAnchored;
+ else if (attachmentComparison == BDSKAttachmentStartWith)
+ options |= kCFCompareAnchored;
+ BOOL matchReturnValue = (stringComparison != BDSKAttachmentNotContain);
+ CFRange range;
+
+ while (itemValue = [itemEnum nextObject]) {
+ if (CFStringFindWithOptions((CFStringRef)itemValue,
(CFStringRef)stringValue, CFRangeMake(0, [itemValue length]), options, &range))
+ return matchReturnValue;
+ }
+ return NO == matchReturnValue;
+
} else {
OBASSERT(stringValue != nil);
@@ -246,20 +273,20 @@
// minor optimization: Shark showed -[NSString rangeOfString:options:]
as a bottleneck, calling through to CFStringFindWithOptions
CFOptionFlags options = kCFCompareCaseInsensitive;
- if (stringComparison == BDSKEndWith)
- options = options | kCFCompareBackwards;
+ if (attachmentComparison == BDSKEndWith)
+ options |= kCFCompareBackwards | kCFCompareAnchored;
+ else if (attachmentComparison == BDSKStartWith)
+ options |= kCFCompareAnchored;
CFRange range;
CFIndex itemLength = CFStringGetLength((CFStringRef)itemValue);
Boolean foundString = CFStringFindWithOptions((CFStringRef)itemValue,
(CFStringRef)stringValue, CFRangeMake(0, itemLength), options, &range);
switch (stringComparison) {
case BDSKContain:
+ case BDSKStartWith:
+ case BDSKEndWith:
return foundString;
case BDSKNotContain:
return foundString == FALSE;
- case BDSKStartWith:
- return foundString && range.location == 0;
- case BDSKEndWith:
- return foundString && (range.location + range.length) ==
itemLength;
default:
break; // other enum types are handled before the switch, but
the compiler doesn't know that
}
@@ -294,14 +321,14 @@
}
- (int)comparison {
- return [self isDateCondition] ? dateComparison : [self isCountCondition] ?
countComparison : stringComparison;
+ return [self isDateCondition] ? dateComparison : [self
isAttachmentCondition] ? attachmentComparison : stringComparison;
}
- (void)setComparison:(int)newComparison {
if ([self isDateCondition])
[self setDateComparison:(BDSKDateComparison)newComparison];
- if ([self isCountCondition])
- [self setCountComparison:(BDSKCountComparison)newComparison];
+ if ([self isAttachmentCondition])
+ [self setAttachmentComparison:(BDSKAttachmentComparison)newComparison];
else
[self setStringComparison:(BDSKStringComparison)newComparison];
}
@@ -324,8 +351,20 @@
default:
return @"";
}
- } else if ([self isCountCondition]) {
- return [NSString stringWithFormat:@"%i", countValue];
+ } else if ([self isAttachmentCondition]) {
+ switch (dateComparison) {
+ case BDSKCountEqual:
+ case BDSKCountNotEqual:
+ case BDSKCountLarger:
+ case BDSKCountSmaller:
+ return [NSString stringWithFormat:@"%i", countValue];
+ case BDSKAttachmentContain:
+ case BDSKAttachmentNotContain:
+ case BDSKAttachmentStartWith:
+ case BDSKAttachmentEndWith:
+ default:
+ return [self stringValue];
+ }
} else {
return [self stringValue];
}
@@ -366,8 +405,23 @@
default:
break;
}
- } else if ([self isCountCondition]) {
- [self setCountValue:[newValue intValue]];
+ } else if ([self isAttachmentCondition]) {
+ switch (dateComparison) {
+ case BDSKCountEqual:
+ case BDSKCountNotEqual:
+ case BDSKCountLarger:
+ case BDSKCountSmaller:
+ [self setCountValue:[newValue intValue]];
+ break;
+ case BDSKAttachmentContain:
+ case BDSKAttachmentNotContain:
+ case BDSKAttachmentStartWith:
+ case BDSKAttachmentEndWith:
+ [self setStringValue:newValue];
+ break;
+ default:
+ break;
+ }
} else {
[self setStringValue:newValue];
}
@@ -398,12 +452,12 @@
#pragma mark | count (linked files/URLs)
-- (BDSKCountComparison)countComparison {
- return countComparison;
+- (BDSKAttachmentComparison)attachmentComparison {
+ return attachmentComparison;
}
-- (void)setCountComparison:(BDSKCountComparison)newComparison {
- countComparison = newComparison;
+- (void)setAttachmentComparison:(BDSKAttachmentComparison)newComparison {
+ attachmentComparison = newComparison;
}
- (int)countValue {
@@ -476,7 +530,7 @@
return [key fieldType] == BDSKDateField;
}
-- (BOOL)isCountCondition {
+- (BOOL)isAttachmentCondition {
return [key fieldType] == BDSKLinkedField;
}
@@ -487,7 +541,7 @@
[self setDateComparison:BDSKToday];
break;
case BDSKLinkedField:
- [self setCountComparison:BDSKCountNotEqual];
+ [self setAttachmentComparison:BDSKCountNotEqual];
break;
case BDSKStringField:
[self setStringComparison:BDSKContain];
@@ -513,6 +567,7 @@
}
case BDSKLinkedField:
[self setCountValue:0];
+ [self setStringValue:@""];
break;
case BDSKBooleanField:
[self setStringValue:[NSString stringWithBool:NO]];
Modified: trunk/bibdesk/BDSKConditionController.h
===================================================================
--- trunk/bibdesk/BDSKConditionController.h 2008-03-07 18:51:57 UTC (rev
12998)
+++ trunk/bibdesk/BDSKConditionController.h 2008-03-07 22:50:38 UTC (rev
12999)
@@ -45,7 +45,7 @@
@interface BDSKConditionController : NSWindowController {
IBOutlet NSComboBox *keyComboBox;
IBOutlet NSPopUpButton *comparisonPopUp;
- IBOutlet NSPopUpButton *countComparisonPopUp;
+ IBOutlet NSPopUpButton *attachmentComparisonPopUp;
IBOutlet NSPopUpButton *dateComparisonPopUp;
IBOutlet NSTextField *valueTextField;
IBOutlet NSTextField *countTextField;
Modified: trunk/bibdesk/BDSKConditionController.m
===================================================================
--- trunk/bibdesk/BDSKConditionController.m 2008-03-07 18:51:57 UTC (rev
12998)
+++ trunk/bibdesk/BDSKConditionController.m 2008-03-07 22:50:38 UTC (rev
12999)
@@ -79,7 +79,7 @@
//NSLog(@"dealloc conditionController");
[condition removeObserver:self forKeyPath:@"key"];
[condition removeObserver:self forKeyPath:@"dateComparison"];
- [condition removeObserver:self forKeyPath:@"countComparison"];
+ [condition removeObserver:self forKeyPath:@"attachmentComparison"];
[condition removeObserver:self forKeyPath:@"stringComparison"];
[condition removeObserver:self forKeyPath:@"stringValue"];
[condition removeObserver:self forKeyPath:@"countValue"];
@@ -96,7 +96,7 @@
[view release];
view = nil;
[[dateComparisonPopUp superview] release];
- [[countComparisonPopUp superview] release];
+ [[attachmentComparisonPopUp superview] release];
[[comparisonPopUp superview] release];
[[valueTextField superview] release];
[[countTextField superview] release];
@@ -120,7 +120,7 @@
// we add/remove these controls, so we need to retain them
[view retain];
[[dateComparisonPopUp superview] retain];
- [[countComparisonPopUp superview] retain];
+ [[attachmentComparisonPopUp superview] retain];
[[comparisonPopUp superview] retain];
[[valueTextField superview] retain];
[[countTextField superview] retain];
@@ -149,7 +149,7 @@
[condition addObserver:self forKeyPath:@"key"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:NULL];
[condition addObserver:self forKeyPath:@"dateComparison" options:
NSKeyValueObservingOptionOld context:NULL];
- [condition addObserver:self forKeyPath:@"countComparison"
options:NSKeyValueObservingOptionOld context:NULL];
+ [condition addObserver:self forKeyPath:@"attachmentComparison"
options:NSKeyValueObservingOptionOld context:NULL];
[condition addObserver:self forKeyPath:@"stringComparison"
options:NSKeyValueObservingOptionOld context:NULL];
[condition addObserver:self forKeyPath:@"stringValue"
options:NSKeyValueObservingOptionOld context:NULL];
[condition addObserver:self forKeyPath:@"countValue"
options:NSKeyValueObservingOptionOld context:NULL];
@@ -235,6 +235,24 @@
break;
}
break;
+ case BDSKLinkedField:
+ switch ([condition attachmentComparison]) {
+ case BDSKCountEqual:
+ case BDSKCountNotEqual:
+ case BDSKCountLarger:
+ case BDSKCountSmaller:
+ controls = [NSArray arrayWithObjects:countTextField, nil];
+ break;
+ case BDSKAttachmentContain:
+ case BDSKAttachmentNotContain:
+ case BDSKAttachmentStartWith:
+ case BDSKAttachmentEndWith:
+ controls = [NSArray arrayWithObjects:valueTextField, nil];
+ break;
+ default:
+ break;
+ }
+ break;
case BDSKBooleanField:
controls = [NSArray arrayWithObjects:booleanButton, nil];
break;
@@ -244,9 +262,6 @@
case BDSKRatingField:
controls = [NSArray arrayWithObjects:ratingButton, nil];
break;
- case BDSKLinkedField:
- controls = [NSArray arrayWithObjects:countTextField, nil];
- break;
default:
controls = [NSArray arrayWithObjects:valueTextField, nil];
}
@@ -272,9 +287,9 @@
if ([condition isDateCondition]) {
[[dateComparisonPopUp superview] setFrameOrigin:NSZeroPoint];
[comparisonBox addSubview:[dateComparisonPopUp superview]];
- } else if ([condition isCountCondition]) {
- [[countComparisonPopUp superview] setFrameOrigin:NSZeroPoint];
- [comparisonBox addSubview:[countComparisonPopUp superview]];
+ } else if ([condition isAttachmentCondition]) {
+ [[attachmentComparisonPopUp superview] setFrameOrigin:NSZeroPoint];
+ [comparisonBox addSubview:[attachmentComparisonPopUp superview]];
} else {
[[comparisonPopUp superview] setFrameOrigin:NSZeroPoint];
[comparisonBox addSubview:[comparisonPopUp superview]];
@@ -309,8 +324,9 @@
} else if ([keyPath isEqualToString:@"dateComparison"]) {
[self layoutValueControls];
[[undoManager prepareWithInvocationTarget:condition]
setDateComparison:[oldValue intValue]];
- } else if ([keyPath isEqualToString:@"countComparison"]) {
- [[undoManager prepareWithInvocationTarget:condition]
setCountComparison:[oldValue intValue]];
+ } else if ([keyPath isEqualToString:@"attachmentComparison"]) {
+ [self layoutValueControls];
+ [[undoManager prepareWithInvocationTarget:condition]
setAttachmentComparison:[oldValue intValue]];
} else if ([keyPath isEqualToString:@"stringComparison"]) {
[[undoManager prepareWithInvocationTarget:condition]
setStringComparison:[oldValue intValue]];
} else if ([keyPath isEqualToString:@"stringValue"]) {
Modified: trunk/bibdesk/Info.plist
===================================================================
--- trunk/bibdesk/Info.plist 2008-03-07 18:51:57 UTC (rev 12998)
+++ trunk/bibdesk/Info.plist 2008-03-07 22:50:38 UTC (rev 12999)
@@ -732,16 +732,14 @@
<string>LucidaGrande</string>
<key>BDSKPersonTableViewFontSizeKey</key>
<real>0.0</real>
- <key>BDSKPreviewTemplateStyleKey</key>
- <string>Default RSS template</string>
<key>BDSKBottomPreviewDisplayKey</key>
<integer>0</integer>
<key>BDSKBottomPreviewDisplayTemplateKey</key>
- <string>Default RSS template</string>
+ <string>Default RTF template</string>
<key>BDSKSidePreviewDisplayKey</key>
<integer>1</integer>
<key>BDSKSidePreviewDisplayTemplateKey</key>
- <string>Default RSS template</string>
+ <string>Default RTF template</string>
<key>BDSKSortGroupsDescendingKey</key>
<false/>
<key>BDSKSortGroupsKey</key>
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit