/* Copyright (c) 1998, 1999, 2000, 2001, 2003, 2004 Benhur Stein This file is part of Pajé. Pajé is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Pajé 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Pajé; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ // DrawView+Drawing // ---------------- // methods for drawing entities in DrawView. #include "DrawView.h" #ifdef GNUSTEP #ifndef __FreeBSD__ #include #else #include #endif #endif @implementation DrawView (Drawing) - (void)drawEventsWithDescriptor:(STEventTypeLayout *)layout inContainer:(PajeContainer *)container fromEnumerator:(NSEnumerator *)enumerator drawFunction:(DrawFunction *)drawFunction { shapefunction *path; drawfunction *draw; drawfunction *highlight; NSColor *color; NSColor *lastColor = nil; id entity; float x, y, w, h; BOOL drawNames; NSMutableAttributedString *name; NSDictionary *attributes; attributes = [NSDictionary dictionaryWithObjectsAndKeys: [NSFont systemFontOfSize:8], @"NSFontAttributeName", nil]; drawNames = [layout drawsName]; if (drawNames) { name = [[NSMutableAttributedString alloc] initWithString:@"" attributes:attributes]; } y = [layout yInContainer:container]; w = [layout width]; h = [layout height]; path = [[layout shapeFunction] function]; draw = [drawFunction function]; highlight = [[layout highlightFunction] function]; PSgsave(); while ((entity = [enumerator nextObject]) != nil) { x = TIMEtoX([filter timeForEntity:entity]); if ([filter isAggregateEntity:entity]) { float x2 = TIMEtoX([filter endTimeForEntity:entity]); int condensedEntitiesCount; unsigned i; unsigned count; float xi = x; float dx; int sup; PSgsave(); condensedEntitiesCount = [entity condensedEntitiesCount]; sup = [layout isSupEvent]; count = [filter subCountForEntity:entity]; for (i = 0; i < count; i++) { [[filter subColorAtIndex:i forEntity:entity] set]; dx = (x2-x) * [filter subCountAtIndex:i forEntity:entity] / condensedEntitiesCount; NSRectFill(NSMakeRect(xi, y+(sup*(-h+8))+(!sup*(h-10)), dx, 3)); xi += dx; } PSmoveto(x, y+2*sup-2*!sup); PSlineto(x, y-(h-7)*sup+(h-7)*!sup); PSlineto(x2, y-(h-7)*sup+(h-7)*!sup); PSlineto(x2, y+2*sup-2*!sup); if ([filter isSelectedEntity:entity]) { [[NSColor yellowColor] set]; } else { [[NSColor blackColor] set]; } PSstroke(); [[NSString stringWithFormat:@"%d", condensedEntitiesCount] drawAtPoint:NSMakePoint(x, y-(h+1)*sup+(h-8)*!sup) withAttributes:attributes]; PSgrestore(); } else { color = [filter colorForEntity:entity]; if (![color isEqual:lastColor]) { lastColor = color; [color set]; } PSgsave(); path(x, y, w, h); if ([filter isSelectedEntity:entity]) { highlight(); } else { draw(); } PSgrestore(); if (drawNames) { PSgsave(); [name replaceCharactersInRange:NSMakeRange(0, [name length]) withString:[filter nameForEntity:entity]]; if ([layout isSupEvent]) { y = y - h; h = 12; } x -= w; NSRectFill(NSMakeRect(x, y, w * 2, h)); if ([[color colorUsingColorSpaceName:NSCalibratedWhiteColorSpace device:nil] whiteComponent] > 0.5) { [[NSColor blackColor] set]; } else { [[NSColor whiteColor] set]; } [name drawInRect:NSMakeRect(x, y, w * 2, h)]; PSgrestore(); } } } PSgrestore(); } #define drawBlackRect(x1, x2, r) \ do { \ [[NSColor darkGrayColor] set]; \ NSFrameRect(NSMakeRect(x1, NSMinY(r) + NSHeight(r) / 4, \ x2 - x1 + 1, NSHeight(r) / 2)); \ } while (0) - (void)drawStatesWithDescriptor:(STStateTypeLayout *)layout inContainer:(PajeContainer *)container fromEnumerator:(NSEnumerator *)enumerator drawFunction:(DrawFunction *)drawFunction { float insetBy; NSColor *color; NSColor *lcolor = [NSColor blackColor]; NSColor *hcolor = [NSColor yellowColor];; NSBezierPath *bp = [NSBezierPath bezierPath]; id entity; float firstUndrawnX = MAXFLOAT; float lastUndrawnX = MAXFLOAT; float undrawnHeight = 0; float x, y, w, h; BOOL drawNames; NSMutableAttributedString *name; drawNames = [layout drawsName]; if (drawNames) { name = [[NSMutableAttributedString alloc] initWithString:@"" attributes:[NSDictionary dictionaryWithObjectsAndKeys: [NSFont systemFontOfSize:8], @"NSFontAttributeName", nil]]; } y = [layout yInContainer:container]; h = [layout height]; insetBy = [layout insetAmount]; [bp setLineWidth:1.0]; while ((entity = [enumerator nextObject]) != nil) { int imbricationLevel; float newHeight; float x2; float ox, ow; x = TIMEtoX([filter startTimeForEntity:entity]); x2 = TIMEtoX([filter endTimeForEntity:entity]); ox = x; ow = x2 - x; #ifdef GNUSTEP // Very big rectangles are not drawn in GNUstep if (x < NSMinX(cutRect)) x = NSMinX(cutRect); if (x2 > NSMaxX(cutRect)) x2 = NSMaxX(cutRect); #endif w = x2 - x; imbricationLevel = [filter imbricationLevelForEntity:entity]; newHeight = h - insetBy * imbricationLevel; if (newHeight < 2) { newHeight = 2; } if (newHeight > h) { newHeight = h; } if (w < 2) { if ((lastUndrawnX != MAXFLOAT) && (x2 < firstUndrawnX)) { drawBlackRect(firstUndrawnX, lastUndrawnX, NSMakeRect(x, y, w, undrawnHeight)); undrawnHeight = 0; lastUndrawnX = x2; } if (lastUndrawnX == MAXFLOAT) { lastUndrawnX = x2; } if (firstUndrawnX > x) { firstUndrawnX = x; } if (newHeight > undrawnHeight) { undrawnHeight = newHeight; } continue; } if (firstUndrawnX != MAXFLOAT) { drawBlackRect(firstUndrawnX, lastUndrawnX, NSMakeRect(x, y, w, undrawnHeight)); firstUndrawnX = lastUndrawnX = MAXFLOAT; undrawnHeight = 0; } NSRect rect; rect = NSMakeRect(x, y, w, newHeight); [bp removeAllPoints]; [bp appendBezierPathWithRect:rect]; if ([filter isAggregateEntity:entity]) { [bp moveToPoint:NSMakePoint(x, y)]; [bp lineToPoint:NSMakePoint(x+w, y+newHeight)]; unsigned i; unsigned count = [filter subCountForEntity:entity]; for (i = 0; i < count; i++) { float dx; [[filter subColorAtIndex:i forEntity:entity] set]; dx = [filter subDurationAtIndex:i forEntity:entity] * pointsPerSecond; rect.size.width = dx; NSRectFill(rect); rect.origin.x += dx; } if (rect.origin.x <= (x + w - 1)) { [bp moveToPoint:NSMakePoint(rect.origin.x, y+newHeight)]; [bp lineToPoint:NSMakePoint(x+w, y)]; } } else { color = [filter colorForEntity:entity]; [color set]; NSRectFill(rect); if (drawNames && ow > smallEntityWidth && newHeight > 8) { [name replaceCharactersInRange:NSMakeRange(0, [name length]) withString:[filter nameForEntity:entity]]; [name drawInRect:NSMakeRect(ox+2, y + newHeight - 10, ow, 10)]; } } if ([filter isSelectedEntity:entity]) { [hcolor set]; } else { [lcolor set]; } [bp stroke]; } if (firstUndrawnX != MAXFLOAT) { drawBlackRect(firstUndrawnX, lastUndrawnX, NSMakeRect(x, y, w, undrawnHeight)); } if (drawNames) { [name release]; } } - (void)drawValuesWithDescriptor:(STVariableTypeLayout *)layout inContainer:(PajeContainer *)container fromEnumerator:(NSEnumerator *)enumerator drawFunction:(DrawFunction *)drawFunction { NSBezierPath *path; NSColor *color; id entity; float yMin; float yMax; float yScale; float yOffset; float off3d; float xOld; float yOld; float yvMin; float yvMax; BOOL first = YES; PajeEntityType *entityType; entityType = [layout entityType]; yMin = [layout minValue]; yMax = [layout maxValue]; if (yMax <= yMin) { yMin = [[filter minValueForEntityType:entityType] floatValue]; yMax = [[filter maxValueForEntityType:entityType] floatValue]; } if (yMin != yMax) { yScale = -([layout height] - 4) / (yMax - yMin); } else { yScale = 1; } off3d = [layout threeD] ? -1 : 0; yOffset = [layout yInContainer:container] + 2 - (yMax * yScale) + off3d; path = [[NSBezierPath alloc] init]; [path setLineJoinStyle: NSBevelLineJoinStyle]; while ((entity = [enumerator nextObject]) != nil) { float x; float y; x = TIMEtoX([filter startTimeForEntity:entity]) + off3d; y = [[filter valueForEntity:entity] doubleValue] * yScale + yOffset; // do not draw if on same column; get max & min y values if (!first && (xOld - x) < 1) { if (y < yvMin) yvMin = y; if (y > yvMax) yvMax = y; continue; } #ifdef GNUSTEP // Very big lines are not drawn in GNUstep if (x < NSMinX(cutRect)) x = NSMinX(cutRect); #endif if (first) { [path moveToPoint: NSMakePoint(NSMaxX(cutRect), y)]; [path lineToPoint: NSMakePoint(x, y)]; } else { if (yvMin != yOld && yvMin != y) { [path lineToPoint: NSMakePoint(xOld, yvMin)]; yOld = yvMin; } if (yvMax != yOld) { [path lineToPoint: NSMakePoint(xOld, yvMax)]; yOld = yvMax; } if (y != yOld) { [path lineToPoint: NSMakePoint(xOld, y)]; } [path lineToPoint: NSMakePoint(x, y)]; } xOld = x; yvMin = yvMax = yOld = y; first = NO; } color = [filter colorForEntityType:entityType]; if ([layout threeD]) { NSAffineTransform *transform = [NSAffineTransform transform]; [path setLineWidth:[layout lineWidth] - 2]; [[color highlightWithLevel:0.3] set]; [path stroke]; [transform translateXBy: 2.0 yBy: 2.0]; [path transformUsingAffineTransform: transform]; [[color shadowWithLevel:0.3] set]; [path stroke]; [transform translateXBy: -3.0 yBy: -3.0]; [path transformUsingAffineTransform: transform]; [color set]; [path stroke]; } else { [path setLineWidth:[layout lineWidth]]; [color set]; [path stroke]; } [path release]; } - (void)drawLinksWithDescriptor:(STLinkTypeLayout *)layout inContainer:(PajeContainer *)container fromEnumerator:(NSEnumerator *)enumerator drawFunction:(DrawFunction *)drawFunction { shapefunction *path; drawfunction *draw; drawfunction *highlight; NSColor *color; NSColor *lastColor = nil; id entity; float x1, x2, y1, y2; path = [[layout shapeFunction] function]; draw = [drawFunction function]; highlight = [[layout highlightFunction] function]; PSgsave(); PSsetlinewidth([layout lineWidth]); while ((entity = [enumerator nextObject]) != nil) { PajeContainer *sourceContainer; PajeContainer *destContainer; sourceContainer = [filter sourceContainerForEntity:entity]; if (sourceContainer != nil) { PajeEntityType *eType; STContainerTypeLayout *cDesc; NSRect rect; eType = [filter sourceEntityTypeForEntity:entity]; cDesc = (STContainerTypeLayout *)[controller descriptorForEntityType:eType]; rect = [cDesc rectOfInstance:sourceContainer]; y1 = NSMinY(rect) + [cDesc linkOffset]; x1 = TIMEtoX([filter startTimeForEntity:entity]); } destContainer = [filter destContainerForEntity:entity]; if (destContainer != nil) { PajeEntityType *eType; STContainerTypeLayout *cDesc; NSRect rect; eType = [filter destEntityTypeForEntity:entity]; cDesc = (STContainerTypeLayout *)[controller descriptorForEntityType:eType]; rect = [cDesc rectOfInstance:destContainer]; y2 = NSMinY(rect) + [cDesc linkOffset]; x2 = TIMEtoX([filter endTimeForEntity:entity]); } color = [filter colorForEntity:entity]; //color = [color colorWithAlphaComponent:0.5]; if (![color isEqual:lastColor]) { lastColor = color; [color set]; } //if (sourceContainer && destContainer) { PSgsave(); if (sourceContainer == nil) { shapefunction PSIn; PSIn(x2, y2, 8, 8); } else if (destContainer == nil) { shapefunction PSOut; PSOut(x1, y1, 8, 8); } else { path(x1, y1, x2-x1, y2-y1); } if ([filter isSelectedEntity:entity]) { highlight(); } else { draw(); } PSgrestore(); } PSgrestore(); } - (void)drawEntitiesWithDescriptor:(STEntityTypeLayout *)layoutDescriptor inContainer:(PajeContainer *)container insideRect:(NSRect)drawRect { NSDate *drawStartTime; NSDate *drawEndTime; NSEnumerator *enumerator; NSRect rect; PajeDrawingType drawingType; rect = [layoutDescriptor rectInContainer:container]; drawingType = [layoutDescriptor drawingType]; /* if (drawingType != PajeLinkDrawingType) { NSRect r = rect; r.origin.x = drawRect.origin.x; r.size.width = drawRect.size.width; if (NSIsEmptyRect(NSIntersectionRect(r, drawRect))) return; } */ if (![layoutDescriptor intersectsRect:drawRect inContainer:container]) { return; } float width; if (drawingType == PajeEventDrawingType) { width = [(STEventTypeLayout *)layoutDescriptor width]; } else { width = 1; } drawStartTime = XtoTIME(NSMinX(drawRect) - width); drawEndTime = XtoTIME(NSMaxX(drawRect) + width); enumerator = [filter enumeratorOfEntitiesTyped:[layoutDescriptor entityType] inContainer:container fromTime:drawStartTime toTime:drawEndTime minDuration:SMALL_ENTITY_DURATION]; switch (drawingType) { case PajeEventDrawingType: [self drawEventsWithDescriptor:(STEventTypeLayout *)layoutDescriptor inContainer:container fromEnumerator:enumerator drawFunction:[layoutDescriptor drawFunction]]; break; case PajeStateDrawingType: [self drawStatesWithDescriptor:(STStateTypeLayout *)layoutDescriptor inContainer:container fromEnumerator:enumerator drawFunction:[layoutDescriptor drawFunction]]; break; case PajeLinkDrawingType: [self drawLinksWithDescriptor:(STLinkTypeLayout *)layoutDescriptor inContainer:container fromEnumerator:enumerator drawFunction:[layoutDescriptor drawFunction]]; break; case PajeVariableDrawingType: [self drawValuesWithDescriptor:(STVariableTypeLayout *) layoutDescriptor inContainer:container fromEnumerator:enumerator drawFunction:[layoutDescriptor drawFunction]]; break; case PajeContainerDrawingType: break; default: NSAssert1(0, @"Invalid drawing type %d", drawingType); } } - (void)drawInstance:(id)entity ofDescriptor:(STContainerTypeLayout *)layoutDescriptor inRect:(NSRect)drawRect { NSEnumerator *sublayoutEnum; STEntityTypeLayout *sublayout; if (![layoutDescriptor isInstance:entity inRect:drawRect]) { return; } if ([[filter selectedContainers] containsObject:entity]) { [[NSColor whiteColor] set]; NSRectFill([layoutDescriptor rectOfInstance:entity]); } // draw all subtypes in this container sublayoutEnum = [[layoutDescriptor subtypes] objectEnumerator]; while ((sublayout = [sublayoutEnum nextObject]) != nil) { if (![sublayout isContainer]) { [self drawEntitiesWithDescriptor:sublayout inContainer:entity insideRect:drawRect]; } else { STContainerTypeLayout *subcontainerLayout; subcontainerLayout = (STContainerTypeLayout *)sublayout; [self drawAllInstancesOfDescriptor:subcontainerLayout inContainer:entity inRect:drawRect]; } } } - (void)drawAllInstancesOfDescriptor:(STContainerTypeLayout *)layoutDescriptor inContainer:(PajeContainer *)container inRect:(NSRect)drawRect { if (/*container == nil || */[layoutDescriptor intersectsRect:drawRect inContainer:container]) { NSEnumerator *ienum; id instance; PajeEntityType *entityType; NSAutoreleasePool *pool; pool = [NSAutoreleasePool new]; entityType = [layoutDescriptor entityType]; ienum = [filter enumeratorOfContainersTyped:entityType inContainer:container]; while ((instance = [ienum nextObject]) != nil) { [self drawInstance:instance ofDescriptor:layoutDescriptor inRect:drawRect]; } [pool release]; } } @end