/* MDKAttributeEditor.m * * Copyright (C) 2006 Free Software Foundation, Inc. * * Author: Enrico Sersale * Date: December 2006 * * This file is part of the GNUstep GWorkspace application * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. */ #include #include #include "MDKAttributeEditor.h" #include "MDKAttribute.h" #include "MDKWindow.h" enum { B_YES = 0, B_NO = 1, IS = 2, IS_NOT = 3, CONTAINS = 4, CONTAINS_NOT = 5, STARTS_WITH = 6, ENDS_WITH = 7, LESS_THEN = 8, EQUAL_TO = 9, GREATER_THEN = 10, TODAY = 11, WITHIN = 12, BEFORE = 13, AFTER = 14, EXACTLY = 15 }; enum { EMPTY, ALT_1, FIELD, ALT_2 }; #define VAL_ORIG NSMakePoint(105, 3) static NSMutableCharacterSet *skipSet = nil; @implementation MDKAttributeEditor + (void)initialize { static BOOL initialized = NO; if (initialized == NO) { initialized = YES; if (skipSet == nil) { NSCharacterSet *set; skipSet = [NSMutableCharacterSet new]; set = [NSCharacterSet controlCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet illegalCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet symbolCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet whitespaceAndNewlineCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet characterSetWithCharactersInString: @"~`@#$%^_-+\\{}:;\"\',/?"]; [skipSet formUnionWithCharacterSet: set]; } } } + (id)editorForAttribute:(MDKAttribute *)attribute inWindow:(MDKWindow *)window { int type = [attribute type]; Class edclass; id editor = nil; switch (type) { case NUMBER: edclass = [MDKNumberEditor class]; break; case DATE: edclass = [MDKDateEditor class]; break; case ARRAY: edclass = [MDKArrayEditor class]; break; case STRING: case DATA: default: edclass = [MDKStringEditor class]; break; } editor = [[edclass alloc] initForAttribute: attribute inWindow: window]; return TEST_AUTORELEASE (editor); } - (void)dealloc { RELEASE (valueBox); RELEASE (firstValueBox); RELEASE (secondValueBox); RELEASE (editorBox); RELEASE (editorInfo); [super dealloc]; } - (id)initForAttribute:(MDKAttribute *)attr inWindow:(MDKWindow *)window { [self subclassResponsibility: _cmd]; return nil; } - (id)initForAttribute:(MDKAttribute *)attr inWindow:(MDKWindow *)window nibName:(NSString *)nibname { self = [super init]; if ([NSBundle loadNibNamed: nibname owner: self]) { NSDictionary *info = [attr editorInfo]; NSArray *operatorNums = [info objectForKey: @"operator"]; int editmode = [[info objectForKey: @"value_edit"] intValue]; unsigned i; RETAIN (editorBox); RETAIN (valueBox); RETAIN (firstValueBox); [firstValueBox removeFromSuperview]; [firstValueBox setFrameOrigin: VAL_ORIG]; RETAIN (secondValueBox); [secondValueBox removeFromSuperview]; [secondValueBox setFrameOrigin: VAL_ORIG]; RELEASE (win); attribute = attr; mdkwindow = window; stateChangeLock = 0; editorInfo = [NSMutableDictionary new]; [editorInfo setObject: [attribute name] forKey: @"attrname"]; [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"casesens"]; [editorInfo setObject: [NSMutableArray array] forKey: @"values"]; [editorInfo setObject: [NSNumber numberWithInt: 0] forKey: @"opmenu_index"]; [editorInfo setObject: [NSNumber numberWithInt: 0] forKey: @"valmenu_index"]; [operatorPopup removeAllItems]; for (i = 0; i < [operatorNums count]; i++) { int opnum = [[operatorNums objectAtIndex: i] intValue]; NSString *title; switch (opnum) { case B_YES: title = NSLocalizedString(@"YES", @""); break; case B_NO: title = NSLocalizedString(@"NO", @""); break; case IS: title = NSLocalizedString(@"is", @""); break; case IS_NOT: title = NSLocalizedString(@"is not", @""); break; case CONTAINS: title = NSLocalizedString(@"contains", @""); break; case CONTAINS_NOT: title = NSLocalizedString(@"contains not", @""); break; case STARTS_WITH: title = NSLocalizedString(@"starts with", @""); break; case ENDS_WITH: title = NSLocalizedString(@"ends with", @""); break; case LESS_THEN: title = NSLocalizedString(@"less then", @""); break; case EQUAL_TO: title = NSLocalizedString(@"equal to", @""); break; case GREATER_THEN: title = NSLocalizedString(@"greater then", @""); break; case TODAY: title = NSLocalizedString(@"is today", @""); break; case WITHIN: title = NSLocalizedString(@"is within", @""); break; case BEFORE: title = NSLocalizedString(@"is before", @""); break; case AFTER: title = NSLocalizedString(@"is after", @""); break; case EXACTLY: title = NSLocalizedString(@"is exactly", @""); break; default: title = @""; break; } [operatorPopup addItemWithTitle: title]; [[operatorPopup itemAtIndex: i] setTag: opnum]; } [operatorPopup selectItemAtIndex: 0]; if (editmode != FIELD) { [valueBox removeFromSuperview]; } if (editmode == ALT_1) { NSArray *titles = [info objectForKey: @"value_menu"]; NSArray *objects = [info objectForKey: @"value_set"]; [valuesPopup removeAllItems]; for (i = 0; i < [titles count]; i++) { [valuesPopup addItemWithTitle: [titles objectAtIndex: i]]; [[valuesPopup itemAtIndex: i] setRepresentedObject: [objects objectAtIndex: i]]; } [valuesPopup selectItemAtIndex: 0]; [[editorBox contentView] addSubview: (NSView *)firstValueBox]; } else if (editmode == ALT_2) { [[editorBox contentView] addSubview: (NSView *)secondValueBox]; } [self setDefaultValues: info]; } else { NSLog(@"failed to load %@!", nibname); DESTROY (self); } return self; } - (void)setDefaultValues:(NSDictionary *)info { NSMutableArray *values = [editorInfo objectForKey: @"values"]; int tag = [[operatorPopup selectedItem] tag]; MDKOperatorType type = [self operatorTypeForTag: tag]; int editmode = [[info objectForKey: @"value_edit"] intValue]; NSString *defvalue = [info objectForKey: @"search_value"]; [editorInfo setObject: [NSNumber numberWithInt: type] forKey: @"optype"]; if (editmode == EMPTY) { [values addObject: defvalue]; } else if (editmode == ALT_1) { [values addObject: [[valuesPopup selectedItem] representedObject]]; } else if (editmode == FIELD) { if (defvalue) { [values addObject: defvalue]; } } else if (editmode == ALT_2) { /* this must be managed in the subclasses */ } } - (void)restoreSavedState:(NSDictionary *)info { id entry = [info objectForKey: @"values"]; if (entry && [entry count]) { NSMutableArray *values = [editorInfo objectForKey: @"values"]; [values removeAllObjects]; [values addObjectsFromArray: entry]; } entry = [info objectForKey: @"opmenu_index"]; if (entry) { stateChangeLock++; [operatorPopup selectItemAtIndex: [entry intValue]]; [self operatorPopupAction: operatorPopup]; stateChangeLock--; } } - (BOOL)hasValidValues { return ([[editorInfo objectForKey: @"values"] count] > 0); } - (void)stateDidChange { stateChangeLock = (stateChangeLock < 0) ? 0 : stateChangeLock; if (stateChangeLock == 0) { [mdkwindow editorStateDidChange: self]; } } - (IBAction)operatorPopupAction:(id)sender { int index = [sender indexOfSelectedItem]; if (index != [[editorInfo objectForKey: @"opmenu_index"] intValue]) { int tag = [[sender selectedItem] tag]; MDKOperatorType type = [self operatorTypeForTag: tag]; [editorInfo setObject: [NSNumber numberWithInt: type] forKey: @"optype"]; [editorInfo setObject: [NSNumber numberWithInt: [sender indexOfSelectedItem]] forKey: @"opmenu_index"]; [self stateDidChange]; } } - (IBAction)valuesPopupAction:(id)sender { [editorInfo setObject: [NSNumber numberWithInt: [sender indexOfSelectedItem]] forKey: @"valmenu_index"]; } - (MDKOperatorType)operatorTypeForTag:(int)tag { MDKOperatorType type; [editorInfo removeObjectForKey: @"leftwild"]; [editorInfo removeObjectForKey: @"rightwild"]; switch (tag) { case GREATER_THEN: case AFTER: type = MDKGreaterThanOperatorType; break; case TODAY: case WITHIN: type = MDKGreaterThanOrEqualToOperatorType; break; case LESS_THEN: case BEFORE: type = MDKLessThanOperatorType; break; case IS_NOT: type = MDKNotEqualToOperatorType; break; case CONTAINS_NOT: type = MDKNotEqualToOperatorType; [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"rightwild"]; [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"leftwild"]; break; case B_YES: case B_NO: case IS: case EQUAL_TO: case EXACTLY: type = MDKEqualToOperatorType; break; case CONTAINS: type = MDKEqualToOperatorType; [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"rightwild"]; [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"leftwild"]; break; case STARTS_WITH: type = MDKEqualToOperatorType; [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"rightwild"]; break; case ENDS_WITH: type = MDKEqualToOperatorType; [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"leftwild"]; break; default: type = MDKEqualToOperatorType; break; } return type; } - (NSView *)editorView { return editorBox; } - (MDKAttribute *)attribute { return attribute; } - (NSDictionary *)editorInfo { return editorInfo; } @end @implementation MDKStringEditor - (void)dealloc { [super dealloc]; } - (id)initForAttribute:(MDKAttribute *)attr inWindow:(MDKWindow *)window { self = [super initForAttribute: attr inWindow: window nibName: @"MDKStringEditor"]; if (self) { NSBundle *bundle = [NSBundle bundleForClass: [self class]]; NSString *impath; NSImage *image; impath = [bundle pathForResource: @"switchOff" ofType: @"tiff"]; image = [[NSImage alloc] initWithContentsOfFile: impath]; [caseSensButt setImage: image]; RELEASE (image); impath = [bundle pathForResource: @"switchOn" ofType: @"tiff"]; image = [[NSImage alloc] initWithContentsOfFile: impath]; [caseSensButt setAlternateImage: image]; RELEASE (image); [caseSensButt setState: NSOnState]; [caseSensButt setToolTip: NSLocalizedString(@"Case sensitive switch", @"")]; [valueField setDelegate: self]; } return self; } - (void)restoreSavedState:(NSDictionary *)info { int editmode; id entry; [super restoreSavedState: info]; editmode = [[[attribute editorInfo] objectForKey: @"value_edit"] intValue]; if (editmode == FIELD) { NSArray *values = [editorInfo objectForKey: @"values"]; if ([values count]) { NSString *word = [values objectAtIndex: 0]; word = [self removeWildcardsFromString: word]; [valueField setStringValue: word]; } } else { entry = [info objectForKey: @"valmenu_index"]; if (entry) { [valuesPopup selectItemAtIndex: [entry intValue]]; [self valuesPopupAction: valuesPopup]; } } entry = [info objectForKey: @"casesens"]; if (entry) { [caseSensButt setState: ([entry boolValue] ? NSOnState : NSOffState)]; [self caseSensButtAction: caseSensButt]; } } - (IBAction)operatorPopupAction:(id)sender { int index = [sender indexOfSelectedItem]; BOOL changed = (index != [[editorInfo objectForKey: @"opmenu_index"] intValue]); stateChangeLock++; [super operatorPopupAction: sender]; if ([[[attribute editorInfo] objectForKey: @"value_edit"] intValue] == FIELD) { NSMutableArray *values = [editorInfo objectForKey: @"values"]; if ([values count]) { NSString *oldword = [values objectAtIndex: 0]; NSString *word = [self removeWildcardsFromString: oldword]; word = [self appendWildcardsToString: word]; if ([word isEqual: oldword] == NO) { [values removeAllObjects]; [values addObject: word]; } } } stateChangeLock--; if (changed) { [self stateDidChange]; } } - (IBAction)valuesPopupAction:(id)sender { int index = [sender indexOfSelectedItem]; if (index != [[editorInfo objectForKey: @"valmenu_index"] intValue]) { NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSString *oldvalue = ([values count] ? [values objectAtIndex: 0] : nil); NSString *newvalue = [[valuesPopup selectedItem] representedObject]; [super valuesPopupAction: sender]; if ((oldvalue == nil) || ([oldvalue isEqual: newvalue] == NO)) { [values removeAllObjects]; [values addObject: newvalue]; [self stateDidChange]; } } } - (IBAction)caseSensButtAction:(id)sender { if ([sender state] == NSOnState) { [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"casesens"]; } else { [editorInfo setObject: [NSNumber numberWithBool: NO] forKey: @"casesens"]; } [self stateDidChange]; } - (void)controlTextDidEndEditing:(NSNotification *)aNotification { NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSString *str = [valueField stringValue]; if ([str length]) { NSScanner *scanner = [NSScanner scannerWithString: str]; NSString *word, *oldword; if ([values count]) { oldword = [self removeWildcardsFromString: [values objectAtIndex: 0]]; } else { oldword = [NSString string]; } if ([scanner scanUpToCharactersFromSet: skipSet intoString: &word]) { if (word && ([word isEqual: oldword] == NO)) { [values removeAllObjects]; [values addObject: [self appendWildcardsToString: word]]; [valueField setStringValue: word]; [self stateDidChange]; } else { [valueField setStringValue: oldword]; } } else { [valueField setStringValue: oldword]; } } else { [values removeAllObjects]; [self stateDidChange]; } } - (NSString *)appendWildcardsToString:(NSString *)str { if (str) { NSMutableString *wilded = [NSMutableString stringWithCapacity: [str length]]; if ([editorInfo objectForKey: @"leftwild"]) { [wilded appendString: @"*"]; } [wilded appendString: str]; if ([editorInfo objectForKey: @"rightwild"]) { [wilded appendString: @"*"]; } return [wilded makeImmutableCopyOnFail: NO]; } return nil; } - (NSString *)removeWildcardsFromString:(NSString *)str { if (str) { NSMutableString *mstr = [str mutableCopy]; [mstr replaceOccurrencesOfString: @"*" withString: @"" options: NSLiteralSearch range: NSMakeRange(0, [mstr length])]; return [mstr autorelease]; } return nil; } @end @implementation MDKArrayEditor - (void)dealloc { [super dealloc]; } - (id)initForAttribute:(MDKAttribute *)attr inWindow:(MDKWindow *)window { self = [super initForAttribute: attr inWindow: window nibName: @"MDKArrayEditor"]; if (self) { NSBundle *bundle = [NSBundle bundleForClass: [self class]]; NSString *impath; NSImage *image; impath = [bundle pathForResource: @"switchOff" ofType: @"tiff"]; image = [[NSImage alloc] initWithContentsOfFile: impath]; [caseSensButt setImage: image]; RELEASE (image); impath = [bundle pathForResource: @"switchOn" ofType: @"tiff"]; image = [[NSImage alloc] initWithContentsOfFile: impath]; [caseSensButt setAlternateImage: image]; RELEASE (image); [caseSensButt setToolTip: NSLocalizedString(@"Case sensitive switch", @"")]; [caseSensButt setState: NSOnState]; [valueField setDelegate: self]; } return self; } - (void)restoreSavedState:(NSDictionary *)info { NSArray *values; id entry; [super restoreSavedState: info]; values = [editorInfo objectForKey: @"values"]; if ([values count]) { [valueField setStringValue: [values componentsJoinedByString: @" "]]; } entry = [info objectForKey: @"casesens"]; if (entry) { [caseSensButt setState: ([entry boolValue] ? NSOnState : NSOffState)]; [self caseSensButtAction: caseSensButt]; } } - (IBAction)caseSensButtAction:(id)sender { if ([sender state] == NSOnState) { [editorInfo setObject: [NSNumber numberWithBool: YES] forKey: @"casesens"]; } else { [editorInfo setObject: [NSNumber numberWithBool: NO] forKey: @"casesens"]; } [self stateDidChange]; } - (void)controlTextDidEndEditing:(NSNotification *)aNotification { NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSString *str = [valueField stringValue]; if ([str length]) { NSMutableArray *words = [NSMutableArray array]; NSScanner *scanner = [NSScanner scannerWithString: str]; while ([scanner isAtEnd] == NO) { NSString *word; if ([scanner scanUpToCharactersFromSet: skipSet intoString: &word]) { if (word && [word length]) { [words addObject: word]; } } else { break; } } if ([words count] && ([words isEqual: values] == NO)) { [values removeAllObjects]; [values addObjectsFromArray: words]; [self stateDidChange]; } str = [values componentsJoinedByString: @" "]; [valueField setStringValue: str]; } else { [values removeAllObjects]; [self stateDidChange]; } } @end @implementation MDKNumberEditor - (void)dealloc { [super dealloc]; } - (id)initForAttribute:(MDKAttribute *)attr inWindow:(MDKWindow *)window { self = [super initForAttribute: attr inWindow: window nibName: @"MDKNumberEditor"]; if (self) { NSNumberFormatter *formatter = [NSNumberFormatter new]; [formatter setAllowsFloats: ([attribute numberType] == NUM_FLOAT)]; [[valueField cell] setFormatter: formatter]; RELEASE (formatter); [valueField setStringValue: @"0"]; [valueField setDelegate: self]; } return self; } - (void)restoreSavedState:(NSDictionary *)info { int editmode; id entry; [super restoreSavedState: info]; editmode = [[[attribute editorInfo] objectForKey: @"value_edit"] intValue]; if (editmode == FIELD) { NSArray *values = [editorInfo objectForKey: @"values"]; if ([values count]) { [valueField setStringValue: [values objectAtIndex: 0]]; } } else if (editmode == ALT_1) { entry = [info objectForKey: @"valmenu_index"]; if (entry) { [valuesPopup selectItemAtIndex: [entry intValue]]; [self valuesPopupAction: valuesPopup]; } } } - (IBAction)operatorPopupAction:(id)sender { int index = [sender indexOfSelectedItem]; if (index != [[editorInfo objectForKey: @"opmenu_index"] intValue]) { int editmode = [[[attribute editorInfo] objectForKey: @"value_edit"] intValue]; [super operatorPopupAction: sender]; if (editmode == EMPTY) { [self stateDidChange]; } } } - (IBAction)valuesPopupAction:(id)sender { int index = [sender indexOfSelectedItem]; if (index != [[editorInfo objectForKey: @"valmenu_index"] intValue]) { NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSString *oldvalue = ([values count] ? [values objectAtIndex: 0] : nil); NSString *newvalue = [[valuesPopup selectedItem] representedObject]; [super valuesPopupAction: sender]; if ((oldvalue == nil) || ([oldvalue isEqual: newvalue] == NO)) { [values removeAllObjects]; [values addObject: newvalue]; [self stateDidChange]; } } } - (void)controlTextDidEndEditing:(NSNotification *)aNotification { NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSString *str = [valueField stringValue]; if ([str length]) { BOOL isfloat = ([attribute numberType] == NUM_FLOAT); float newval = [str floatValue]; NSString *oldstr; if ([values count]) { oldstr = [values objectAtIndex: 0]; } else { oldstr = (isfloat ? @"0.0" : @"0"); } if (newval != 0.0) { NSString *formstr = (isfloat ? @"%f" : @"%.0f"); NSString *newstr = [NSString stringWithFormat: formstr, newval]; if ([newstr isEqual: oldstr] == NO) { [values removeAllObjects]; [values addObject: newstr]; [self stateDidChange]; } } else { [valueField setStringValue: oldstr]; } } else { [values removeAllObjects]; [self stateDidChange]; } } @end enum { LAST_DAY, LAST_2DAYS, LAST_3DAYS, LAST_WEEK, LAST_2WEEKS, LAST_3WEEKS, LAST_MONTH, LAST_2MONTHS, LAST_3MONTHS, LAST_6MONTHS }; #define MINUTE_TI (60.0) #define HOUR_TI (MINUTE_TI * 60) #define DAY_TI (HOUR_TI * 24) #define DAYS2_TI (DAY_TI * 2) #define DAYS3_TI (DAY_TI * 3) #define WEEK_TI (DAY_TI * 7) #define WEEK2_TI (WEEK_TI * 2) #define WEEK3_TI (WEEK_TI * 3) #define MONTH_TI (DAY_TI * 30) #define MONTH2_TI ((MONTH_TI * 2) + DAY_TI) #define MONTH3_TI ((MONTH_TI * 3) + (DAY_TI * 1.5)) #define MONTH6_TI ((MONTH_TI * 6) + (DAY_TI * 3)) static NSString *calformat = @"%m %d %Y"; @implementation MDKDateEditor - (void)dealloc { [super dealloc]; } - (id)initForAttribute:(MDKAttribute *)attr inWindow:(MDKWindow *)window { self = [super initForAttribute: attr inWindow: window nibName: @"MDKDateEditor"]; if (self) { NSDateFormatter *formatter; int index; [dateStepper setMaxValue: MONTH6_TI]; [dateStepper setMinValue: 0.0]; [dateStepper setIncrement: 1.0]; [dateStepper setAutorepeat: YES]; [dateStepper setValueWraps: YES]; [secondValueBox removeFromSuperview]; stepperValue = MONTH3_TI; [dateStepper setDoubleValue: stepperValue]; [dateField setDelegate: self]; formatter = [[NSDateFormatter alloc] initWithDateFormat: calformat allowNaturalLanguage: NO]; [[dateField cell] setFormatter: formatter]; RELEASE (formatter); [valuesPopup removeAllItems]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last day", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last 2 days", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last 3 days", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last week", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last 2 weeks", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last 3 weeks", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last month", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last 2 months", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last 3 months", @"")]; [valuesPopup addItemWithTitle: NSLocalizedString(@"the last 6 months", @"")]; [valuesPopup selectItemAtIndex: LAST_DAY]; index = [operatorPopup indexOfItemWithTag: TODAY]; [operatorPopup selectItemAtIndex: index]; [editorInfo setObject: [NSNumber numberWithInt: index] forKey: @"opmenu_index"]; [editorInfo setObject: [NSNumber numberWithInt: LAST_DAY] forKey: @"valmenu_index"]; } return self; } - (void)setDefaultValues:(NSDictionary *)info { NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSCalendarDate *midnight = [self midnight]; NSTimeInterval interval = [midnight timeIntervalSinceReferenceDate]; NSString *datestr = [midnight descriptionWithCalendarFormat: calformat]; [super setDefaultValues: info]; [values addObject: [NSString stringWithFormat: @"%f", interval]]; [dateField setStringValue: datestr]; } - (void)restoreSavedState:(NSDictionary *)info { NSArray *values; [super restoreSavedState: info]; values = [editorInfo objectForKey: @"values"]; if (values && [values count]) { NSTimeInterval interval = [[values objectAtIndex: 0] floatValue]; NSCalendarDate *date = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: interval]; [dateField setStringValue: [date descriptionWithCalendarFormat: calformat]]; } } - (IBAction)operatorPopupAction:(id)sender { int index = [sender indexOfSelectedItem]; if (index != [[editorInfo objectForKey: @"opmenu_index"] intValue]) { int tag = [[sender selectedItem] tag]; NSView *view = [editorBox contentView]; NSArray *views = [view subviews]; stateChangeLock++; [super operatorPopupAction: sender]; if (tag == TODAY) { if ([views containsObject: secondValueBox]) { [secondValueBox removeFromSuperview]; } if ([views containsObject: firstValueBox]) { [firstValueBox removeFromSuperview]; } [valuesPopup selectItemAtIndex: LAST_DAY]; [self valuesPopupAction: valuesPopup]; } else if (tag == WITHIN) { if ([views containsObject: secondValueBox]) { [secondValueBox removeFromSuperview]; } if ([views containsObject: firstValueBox] == NO) { [view addSubview: firstValueBox]; } [self valuesPopupAction: valuesPopup]; } else if ((tag == BEFORE) || (tag == AFTER) || (tag == EXACTLY)) { if ([views containsObject: firstValueBox]) { [firstValueBox removeFromSuperview]; } if ([views containsObject: secondValueBox] == NO) { [view addSubview: secondValueBox]; } } stateChangeLock--; [self stateDidChange]; } } - (IBAction)valuesPopupAction:(id)sender { int index = [sender indexOfSelectedItem]; NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSCalendarDate *midnight = [self midnight]; NSTimeInterval interval = [midnight timeIntervalSinceReferenceDate] + DAY_TI; NSString *datestr; stateChangeLock++; [super valuesPopupAction: sender]; switch (index) { case LAST_DAY: interval -= DAY_TI; break; case LAST_2DAYS: interval -= DAYS2_TI; break; case LAST_3DAYS: interval -= DAYS3_TI; break; case LAST_WEEK: interval -= WEEK_TI; break; case LAST_2WEEKS: interval -= WEEK2_TI; break; case LAST_3WEEKS: interval -= WEEK3_TI; break; case LAST_MONTH: interval -= MONTH_TI; break; case LAST_2MONTHS: interval -= MONTH2_TI; break; case LAST_3MONTHS: interval -= MONTH3_TI; break; case LAST_6MONTHS: interval -= MONTH6_TI; break; } [values removeAllObjects]; [values addObject: [NSString stringWithFormat: @"%f", interval]]; midnight = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: interval]; datestr = [midnight descriptionWithCalendarFormat: calformat]; [dateField setStringValue: datestr]; stateChangeLock--; [self stateDidChange]; } - (IBAction)stepperAction:(id)sender { NSString *str = [dateField stringValue]; if ([str length]) { NSCalendarDate *cdate = [NSCalendarDate dateWithString: str calendarFormat: calformat]; if (cdate) { double sv = [sender doubleValue]; if (sv > stepperValue) { cdate = [cdate addTimeInterval: DAY_TI]; } else if (sv < stepperValue) { cdate = [cdate addTimeInterval: -DAY_TI]; } str = [cdate descriptionWithCalendarFormat: calformat]; [dateField setStringValue: str]; stepperValue = sv; [editorInfo setObject: [NSNumber numberWithFloat: stepperValue] forKey: @"stepper_val"]; [self parseDateString: [dateField stringValue]]; } } } - (void)controlTextDidEndEditing:(NSNotification *)aNotification { [self parseDateString: [dateField stringValue]]; } - (void)parseDateString:(NSString *)str { if (str && [str length]) { NSCalendarDate *cdate = [NSCalendarDate dateWithString: str calendarFormat: calformat]; if (cdate) { NSMutableArray *values = [editorInfo objectForKey: @"values"]; NSTimeInterval interval = [cdate timeIntervalSinceReferenceDate]; NSString *intstr = [NSString stringWithFormat: @"%f", interval]; BOOL sameval = ([values count] && [[values objectAtIndex: 0] isEqual: intstr]); if (sameval == NO) { [values removeAllObjects]; [values addObject: intstr]; [self stateDidChange]; } } } } - (NSCalendarDate *)midnight { NSCalendarDate *midnight = [NSCalendarDate calendarDate]; midnight = [NSCalendarDate dateWithYear: [midnight yearOfCommonEra] month: [midnight monthOfYear] day: [midnight dayOfMonth] hour: 0 minute: 0 second: 0 timeZone: [midnight timeZone]]; return midnight; } - (NSTimeInterval)midnightStamp { return [[self midnight] timeIntervalSinceReferenceDate]; } @end @implementation MDKTextContentEditor - (void)dealloc { RELEASE (textContentWords); RELEASE (skipSet); [super dealloc]; } - (id)initWithSearchField:(NSTextField *)field inWindow:(MDKWindow *)window { self = [super init]; if (self) { NSCharacterSet *set; searchField = field; [searchField setDelegate: self]; mdkwindow = window; ASSIGN (textContentWords, [NSArray array]); wordsChanged = NO; skipSet = [NSMutableCharacterSet new]; set = [NSCharacterSet controlCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet illegalCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet symbolCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet whitespaceAndNewlineCharacterSet]; [skipSet formUnionWithCharacterSet: set]; set = [NSCharacterSet characterSetWithCharactersInString: @"~`@#$%^_-+\\{}:;\"\',/?"]; [skipSet formUnionWithCharacterSet: set]; } return self; } #define WORD_MAX 40 #define WORD_MIN 3 - (void)controlTextDidChange:(NSNotification *)notif { NSString *str = [searchField stringValue]; wordsChanged = NO; if ([str length]) { CREATE_AUTORELEASE_POOL(arp); NSScanner *scanner = [NSScanner scannerWithString: str]; NSMutableArray *words = [NSMutableArray array]; while ([scanner isAtEnd] == NO) { NSString *word; if ([scanner scanUpToCharactersFromSet: skipSet intoString: &word]) { if (word) { unsigned wl = [word length]; if ((wl >= WORD_MIN) && (wl < WORD_MAX)) { [words addObject: word]; } } } else { break; } } if ([words count] && ([words isEqual: textContentWords] == NO)) { ASSIGN (textContentWords, words); wordsChanged = YES; } RELEASE (arp); } else { ASSIGN (textContentWords, [NSArray array]); wordsChanged = YES; } if (wordsChanged) { [mdkwindow editorStateDidChange: self]; } } - (void)setTextContentWords:(NSArray *)words { ASSIGN (textContentWords, words); [searchField setStringValue: [words componentsJoinedByString: @" "]]; } - (NSArray *)textContentWords { return textContentWords; } - (BOOL)wordsChanged { return wordsChanged; } @end