// ADPersonPropertyView+Private.m (this is -*- ObjC -*-) // // \author: Björn Giesler // // Address Book Application for GNUstep // // $Author: rmottola $ // $Locker: $ // $Revision: 1.1 $ // $Date: 2007/03/29 22:36:04 $ /* system includes */ /* (none) */ /* my includes */ #include "ADPersonPropertyView.h" @implementation ADPersonPropertyView (Private) - (ADPersonPropertyCell*) addCellWithValue: (NSString*) val inRect: (NSRect*) rect editable: (BOOL) yesno font: (NSFont*) font alignment: (NSTextAlignment) alignment details: (id) details { ADPersonPropertyCell *cell; NSSize s; cell = [[[ADPersonPropertyCell alloc] init] autorelease]; [cell setEditable: yesno]; [cell setStringValue: val]; [cell setFont: font]; [cell setAlignment: alignment]; if(details) [cell setDetails: details]; s = [cell cellSize]; rect->size.width = MAX(rect->size.width, s.width); rect->size.height = MAX(rect->size.height, s.height); [cell setRect: *rect]; [_cells addObject: cell]; return cell; } - (ADPersonPropertyCell*) addValueCellForValue: (NSString*) val inRect: (NSRect*) rect details: (id) details { ADPersonPropertyCell *cell; cell = [self addCellWithValue: val inRect: rect editable: YES font: [self font] alignment: NSLeftTextAlignment details: details]; if(_delegate && [_delegate respondsToSelector: _clickSel] && [_delegate canPerformClickForProperty: _property] && !_editable) [cell setTextColor: [NSColor blueColor]]; else if(_editable) [cell setTextColor: [NSColor darkGrayColor]]; else [cell setTextColor: [NSColor blackColor]]; return cell; } - (ADPersonPropertyCell*) addValueCellForValue: (NSString*) val inRect: (NSRect*) rect { return [self addValueCellForValue: val inRect: rect details: nil]; } - (ADPersonPropertyCell*) addLabelCellForLabel: (NSString*) label inRect: (NSRect*) rect { NSString *str; NSFont *font; int w; str = ADLocalizedPropertyOrLabel(label); font = [self boldFont]; w = [font widthOfString: str]; _neededLabelWidth = MAX(_neededLabelWidth, w); str = [str stringByAbbreviatingToFitWidth: _maxLabelWidth inFont: font]; rect->size.width = [self maxLabelWidth]; return [self addCellWithValue: str inRect: rect editable: NO font: font alignment: NSRightTextAlignment details: nil]; } - (ADPersonPropertyCell*) addConstantCellForString: (NSString*) str inRect: (NSRect*) rect { NSFont *font = [self font]; rect->size.width = [font widthOfString: str]; return [self addCellWithValue: str inRect: rect editable: NO font: font alignment: NSRightTextAlignment details: nil]; } - (ADPersonActionCell*) addActionCellWithType: (ADActionType) t inRect: (NSRect*) rect details: (id) details { id cell; NSSize cellSize; NSPoint cellOrigin; cell = [[[ADPersonActionCell alloc] init] autorelease]; switch(t) { case ADAddAction: [cell setImage: _addImg]; break; case ADRemoveAction: [cell setImage: _rmvImg]; break; case ADChangeAction: [cell setImage: _chgImg]; break; default: NSLog(@"Unknown action type %d\n", t); } cellSize = [cell rect].size; cellOrigin = rect->origin; if(rect->size.height > cellSize.height) cellOrigin.y += (rect->size.height - cellSize.height)/2 + 2; [cell setOrigin: cellOrigin]; rect->size = [cell rect].size; [cell setDetails: details]; [cell setActionType: t]; [_cells addObject: cell]; return cell; } - (NSArray*) layoutDictionary: (NSDictionary*) dict withLabel: (NSString*) label forDisplayInRect: (NSRect*) rect { NSArray *layout, *row; NSString *field; NSEnumerator *rowEnumerator, *fieldEnumerator; ADPersonPropertyCell *labelCell; NSSize rowSize; NSRect rowRect; float labelX; BOOL firstRow; NSMutableArray *cells; layout = [self layoutRuleForValue: dict]; cells = [NSMutableArray array]; labelCell = [self addLabelCellForLabel: label inRect: rect]; rect->origin.x += rect->size.width + 5; labelX = rect->origin.x; rect->size.width += 5; rowRect = *rect; rowSize = rect->size; firstRow = YES; rowEnumerator = [layout objectEnumerator]; while((row = [rowEnumerator nextObject])) { // do we have to layout anything in this row at all? fieldEnumerator = [row objectEnumerator]; while((field = [fieldEnumerator nextObject])) { if([field hasPrefix: @"$"] || ![dict objectForKey: field]) continue; break; } if(!field) continue; fieldEnumerator = [row objectEnumerator]; while((field = [fieldEnumerator nextObject])) { NSString *str; id cell; if([field hasPrefix: @"$"]) str = [field substringFromIndex: 1]; else str = [dict objectForKey: field]; if(!str || [str length] == 0) continue; rowRect.size = NSMakeSize(0, 0); cell = [self addConstantCellForString: str inRect: &rowRect]; [cells addObject: cell]; rowRect.origin.x += rowRect.size.width + 5; rowSize.width += rowRect.size.width + 5; rowSize.height = MAX(rowSize.height, rowRect.size.height); } // add +1 to compensate tiny GNUstep bug rect->size.width = MAX(rect->size.width, rowSize.width+1); if(!firstRow) rect->size.height += rowSize.height+1; firstRow = NO; rowRect.origin.x = rect->origin.x; rowRect.origin.y += rowRect.size.height; rowSize = NSMakeSize(labelX, 0); } return [NSArray arrayWithArray: cells]; } - (NSArray*) layoutDictionary: (NSDictionary*) dict withLabel: (NSString*) label details: (NSDictionary*) details buttons: (BOOL) buttons forEditInRect: (NSRect*) rect { NSArray *layout, *row; NSString *field; NSEnumerator *rowEnumerator, *fieldEnumerator; ADPersonPropertyCell *labelCell; NSSize rowSize; NSRect rowRect; float labelX; BOOL firstRow, showsDefault; NSMutableArray *cells; layout = [self layoutRuleForValue: dict]; cells = [NSMutableArray array]; labelCell = [self addLabelCellForLabel: label inRect: rect]; rect->origin.x += rect->size.width + 5; rect->size.width += 5; rowSize = rect->size; if(buttons) { rect->size.height = rowSize.height; [self addActionCellWithType: ADChangeAction inRect: rect details: details]; rect->origin.x += rect->size.width + 5; rowSize.width += rect->size.width + 5; rowSize.height = MAX(rowSize.height, rect->size.height); rect->size.height = rowSize.height; [self addActionCellWithType: ADRemoveAction inRect: rect details: details]; rect->origin.x += rect->size.width + 5; rowSize.width += rect->size.width + 5; rowSize.height = MAX(rowSize.height, rect->size.height); } labelX = rect->origin.x; rowRect = *rect; firstRow = YES; rowEnumerator = [layout objectEnumerator]; while((row = [rowEnumerator nextObject])) { fieldEnumerator = [row objectEnumerator]; while((field = [fieldEnumerator nextObject])) { NSString *str; BOOL label; label = NO; showsDefault = NO; if([field hasPrefix: @"$"]) { str = [field substringFromIndex: 1]; label = YES; } else str = [dict objectForKey: field]; if(!str) { str = [NSString stringWithFormat: @"[%@]", ADLocalizedPropertyOrLabel(field)]; showsDefault = YES; } rowRect.size = NSMakeSize(0, 0); if(label) { ADPersonPropertyCell *cell = [self addConstantCellForString: str inRect: &rowRect]; [cells addObject: cell]; } else { ADPersonPropertyCell *cell; NSMutableDictionary *myDetails = [NSMutableDictionary dictionaryWithDictionary: details]; [myDetails setObject: field forKey: @"Key"]; cell = [self addValueCellForValue: str inRect: &rowRect details: myDetails]; if(showsDefault) [cell setTextColor: [NSColor lightGrayColor]]; [cells addObject: cell]; } rowRect.origin.x += rowRect.size.width + 5; rowSize.width += rowRect.size.width + 5; rowSize.height = MAX(rowSize.height, rowRect.size.height); } rect->size.width = MAX(rect->size.width, rowSize.width+1); if(!firstRow) rect->size.height += rowSize.height+1; firstRow = NO; rowRect.origin.x = rect->origin.x; rowRect.origin.y += rowRect.size.height; rowSize = NSMakeSize(labelX, 0); } return [NSArray arrayWithArray: cells]; } - (void) layoutForEdit { id val; id cell; ADPropertyType type; NSRect r; NSSize allSize; int i; BOOL showsDefault; if(!_property || !_person) return; val = [_person valueForProperty: _property]; type = [_person typeOfProperty: _property]; r = NSMakeRect(0, 0, 0, 0); allSize = NSMakeSize(0, 0); // convert types if(val) switch(type) { case ADStringProperty: break; case ADIntegerProperty: val = [NSString stringWithFormat: @"%d", [val intValue]]; break; case ADRealProperty: val = [NSString stringWithFormat: @"%f", [val floatValue]]; break; case ADDateProperty: case ADArrayProperty: case ADDictionaryProperty: case ADDataProperty: case ADErrorInProperty: NSLog(@"Can't layout object of type %d\n", type); return; default: break; } // layout single-cell values if(!(type & ADMultiValueMask)) { if(_displaysLabel) { [self addLabelCellForLabel: _property inRect: &r]; r.origin.x += r.size.width + 5; allSize.width = r.size.width + 5; allSize.width += r.size.width + 5; r.size = NSMakeSize(0, 0); } showsDefault = NO; if(!val) { val = [self emptyValue]; showsDefault = YES; } cell = [self addValueCellForValue: val inRect: &r]; if(showsDefault) [cell setTextColor: [NSColor lightGrayColor]]; allSize.width += r.size.width; allSize.height = MAX(allSize.height, r.size.height); } else if(type == ADMultiStringProperty) { if(![val count]) { ADPersonPropertyCell *labelCell, *valueCell; NSString *label, *value; NSDictionary *details; label = [self defaultLabel]; details = [NSDictionary dictionaryWithObjectsAndKeys: label, @"Label", nil]; labelCell = [self addLabelCellForLabel: label inRect: &r]; [labelCell setDetails: details]; r.origin.x += r.size.width + 5; allSize.width = r.size.width + 5; allSize.height = r.size.height; r.size = NSMakeSize(0, 0); value = [self emptyValue]; valueCell = [self addValueCellForValue: value inRect: &r]; [valueCell setTextColor: [NSColor lightGrayColor]]; [valueCell setDetails: details]; allSize.width += r.size.width; allSize.height = MAX(allSize.height, r.size.height); } else { for(i=0; i<[val count]; i++) { ADPersonPropertyCell *labelCell, *valueCell; NSString *label, *identifier, *value; NSDictionary *details; NSSize rowSize; label = [val labelAtIndex: i]; identifier = [val identifierAtIndex: i]; value = [val valueAtIndex: i]; details = [NSDictionary dictionaryWithObjectsAndKeys: identifier, @"Identifier", label, @"Label", nil]; labelCell = [self addLabelCellForLabel: label inRect: &r]; [labelCell setDetails: details]; r.origin.x += r.size.width + 5; rowSize.width = r.size.width + 5; rowSize.height = r.size.height; r.size.height = rowSize.height; [self addActionCellWithType: ADChangeAction inRect: &r details: details]; r.origin.x += r.size.width + 5; rowSize.width += r.size.width + 5; rowSize.height = MAX(rowSize.height, r.size.height); r.size.height = rowSize.height; [self addActionCellWithType: ADRemoveAction inRect: &r details: details]; r.origin.x += r.size.width + 5; rowSize.width += r.size.width + 5; rowSize.height = MAX(rowSize.height, r.size.height); r.size = NSMakeSize(0, 0); valueCell = [self addValueCellForValue: value inRect: &r]; [valueCell setDetails: details]; if([value isEqualToString: [self emptyValue]]) [valueCell setTextColor: [NSColor lightGrayColor]]; else [valueCell setTextColor: [NSColor darkGrayColor]]; rowSize.width += r.size.width; rowSize.height = MAX(rowSize.height, r.size.height); r.size = NSMakeSize(0, 0); r.origin.x = 0; r.origin.y += rowSize.height; allSize.width = MAX(allSize.width, rowSize.width); allSize.height += rowSize.height; } r.origin.x = _maxLabelWidth + 5 + [_chgImg size].width + 5; r.origin.y += 5; [self addActionCellWithType: ADAddAction inRect: &r details: nil]; allSize.height += r.size.height + 5; } } else if(type == ADMultiDictionaryProperty) { NSRect rect = NSZeroRect; if(![val count]) { NSDictionary *details; NSString *label; NSMutableDictionary *value; label = [self defaultLabel]; value = [self emptyValue]; details = [NSDictionary dictionaryWithObjectsAndKeys: label, @"Label", nil]; [self layoutDictionary: value withLabel: label details: details buttons: NO forEditInRect: &rect]; allSize = rect.size; } else { for(i=0; i<[val count]; i++) { NSString *label, *value, *identifier; NSDictionary *details; label = [val labelAtIndex: i]; value = [val valueAtIndex: i]; identifier = [val identifierAtIndex: i]; details = [NSDictionary dictionaryWithObjectsAndKeys: identifier, @"Identifier", label, @"Label", nil]; [self layoutDictionary: [val valueAtIndex: i] withLabel: [val labelAtIndex: i] details: details buttons: YES forEditInRect: &rect]; allSize.width = MAX(allSize.width, rect.size.width); allSize.height += rect.size.height; rect.origin.x = 0; rect.origin.y += rect.size.height; rect.size = NSZeroSize; } rect.origin.x = _maxLabelWidth + 5 + [_chgImg size].width + 5; rect.origin.y += 5; [self addActionCellWithType: ADAddAction inRect: &rect details: nil]; allSize.height += rect.size.height + 5; } } else NSLog(@"Can't layout values of type %d yet\n", type); _requiredSize = allSize; [self setFrameSize: _requiredSize]; } - (void) layoutForDisplay { NSRect r; NSSize allSize; int i; id cell; id val; ADPropertyType type; if(!_property || !_person) return; val = [_person valueForProperty: _property]; type = [_person typeOfProperty: _property]; r = NSMakeRect(0, 0, 0, 0); allSize = NSMakeSize(0, 0); if(!val) return; // convert types switch(type) { case ADStringProperty: break; case ADIntegerProperty: val = [NSString stringWithFormat: @"%d", [val intValue]]; break; case ADRealProperty: val = [NSString stringWithFormat: @"%f", [val floatValue]]; break; case ADDateProperty: case ADArrayProperty: case ADDictionaryProperty: case ADDataProperty: case ADErrorInProperty: NSLog(@"Can't layout object of type %d\n", type); return; default: break; } // layout single-cell values if(!(type & ADMultiValueMask)) { if(_displaysLabel) { [self addLabelCellForLabel: _property inRect: &r]; allSize.width += r.size.width + 5; allSize.height = r.size.height; r.origin.x += r.size.width + 5; r.size = NSMakeSize(0, 0); } cell = [self addValueCellForValue: val inRect: &r]; allSize.width += r.size.width; allSize.height = MAX(allSize.height, r.size.height); } // layout multi-cell values; multi-string only this far else if(type == ADMultiStringProperty) { for(i=0; i<[val count]; i++) { ADPersonPropertyCell *labelCell, *valueCell; NSString *label, *value, *identifier; NSSize rowSize; NSDictionary *details; label = [val labelAtIndex: i]; value = [val valueAtIndex: i]; identifier = [val identifierAtIndex: i]; details = [NSDictionary dictionaryWithObjectsAndKeys: identifier, @"Identifier", label, @"Label", nil]; labelCell = [self addLabelCellForLabel: label inRect: &r]; r.origin.x += r.size.width + 5; rowSize.width = r.size.width + 5; rowSize.height = r.size.height; r.size = NSMakeSize(0, 0); valueCell = [self addValueCellForValue: value inRect: &r details: details]; rowSize.width += r.size.width; rowSize.height = MAX(rowSize.height, r.size.height); r.size = NSMakeSize(0, 0); r.origin.x = 0; r.origin.y += rowSize.height; allSize.width = MAX(allSize.width, rowSize.width); allSize.height += rowSize.height; } } // layout dictionaries else if(type == ADMultiDictionaryProperty) { NSRect rect = NSZeroRect; for(i=0; i<[val count]; i++) { NSArray *cells; NSDictionary *details; NSString *identifier, *label; int j; label = [val labelAtIndex: i]; identifier = [val identifierAtIndex: i]; details = [NSDictionary dictionaryWithObjectsAndKeys: identifier, @"Identifier", label, @"Label", nil]; cells = [self layoutDictionary: [val valueAtIndex: i] withLabel: [val labelAtIndex: i] forDisplayInRect: &rect]; for(j=0; j<[cells count]; j++) [[cells objectAtIndex: j] setDetails: details]; allSize.width = MAX(allSize.width, rect.size.width); allSize.height += rect.size.height; rect.origin.x = 0; rect.origin.y += rect.size.height; rect.size = NSZeroSize; } } else NSLog(@"Can't layout values of type %d yet\n", type); _requiredSize = allSize; [self setFrameSize: _requiredSize]; } - (void) layout { float heightBefore, heightAfter; _neededLabelWidth = 0; // clear everything [_cells release]; _cells = [[NSMutableArray alloc] init]; heightBefore = [self frame].size.height; if([self isEditable]) [self layoutForEdit]; else [self layoutForDisplay]; heightAfter = [self frame].size.height; if((heightBefore != heightAfter) && (_delegate != nil) && [_delegate respondsToSelector: @selector(view:changedHeightFrom:to:)]) [_delegate view: self changedHeightFrom: heightBefore to: heightAfter]; [self setNeedsDisplay: YES]; } @end