libs-gdl2/DBModeler/DiagramView.m
Matt Rice 34b5413f0f * DBModeler/DiagramView.m: Add missing include.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@29865 72102866-910b-0410-8b05-ffd578937521
2010-03-07 10:48:25 +00:00

424 lines
14 KiB
Objective-C

/**
DiagramView.m
Author: Matt Rice <ratmice@gmail.com>
Date: Oct 2006
This file is part of DBModeler.
<license>
DBModeler 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 3 of the License, or
(at your option) any later version.
DBModeler 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 DBModeler; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
</license>
**/
#include "DiagramView.h"
#include "EntityView.h"
#include "AttributeCell.h"
#ifdef NeXT_Foundation_LIBRARY
#include <Foundation/Foundation.h>
#else
#include <Foundation/NSSet.h>
#endif
#ifdef NeXT_GUI_LIBRARY
#include <AppKit/AppKit.h>
#else
#include <AppKit/NSColor.h>
#include <AppKit/NSScrollView.h>
#include <AppKit/NSEvent.h>
#include <AppKit/NSGraphics.h>
#include <AppKit/NSBezierPath.h>
#include <AppKit/NSPasteboard.h>
#include <AppKit/NSDragging.h>
#endif
#include <EOAccess/EOModel.h>
#include <EOAccess/EOAttribute.h>
#include <EOAccess/EORelationship.h>
#include <EOModeler/EOModelExtensions.h>
#include <GNUstepBase/GNUstep.h>
#include <math.h>
#include <stdlib.h>
@implementation DiagramView
- (id) initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
_shownEntities = [NSMutableDictionary new];
_relationships = [NSMutableArray new];
_subview_order = [NSMutableArray new];
_bgColor = RETAIN([NSColor colorWithCalibratedRed:0.881437 green:0.941223 blue:1.0 alpha:1.0]);
[self registerForDraggedTypes:[NSArray arrayWithObject:NSColorPboardType]];
return self;
}
- (void) dealloc
{
RELEASE(_shownEntities);
RELEASE(_relationships);
RELEASE(_subview_order);
RELEASE(_bgColor);
RELEASE(_model);
[super dealloc];
}
- (BOOL) prepareForDragOperation:(id <NSDraggingInfo>)sender
{
return YES;
}
- (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
{
return [sender draggingSourceOperationMask];
}
- (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
{
NSPasteboard *pb = [sender draggingPasteboard];
NSColor *color = [NSColor colorFromPasteboard:pb];
ASSIGN(_bgColor, color);
[self setNeedsDisplay:YES];
return YES;
}
- (BOOL) isFlipped
{
return YES;
}
- (void) setModel:(EOModel *)model
{
ASSIGN(_model, model);
}
- (void) showEntity:(NSString *)name
{
EntityView *ev;
if (!(ev = [_shownEntities objectForKey:name]))
{
EOEntity *entity = [_model entityNamed:name];
NSArray *attribs = [entity attributes];
NSRect vis = [[self enclosingScrollView] documentVisibleRect];
NSRect evFrame;
NSPoint toPoint;
int i, c = [attribs count];
ev = [[EntityView alloc] initWithFrame:NSMakeRect(0,0,1,1)];
[ev setTitle:[entity name]];
[ev setNumberOfAttributes:c];
for (i = 0; i < c; i++)
{
EOAttribute *attrib = [attribs objectAtIndex:i];
AttributeCell *cell = [ev cellAtRow:i];
[cell setStringValue:[attrib name]];
[cell setLock:[[entity attributesUsedForLocking] containsObject:attrib]];
[cell setProp:[[entity classProperties] containsObject:attrib]];
[cell setKey:[[entity primaryKeyAttributes] containsObject:attrib]];
}
[ev sizeToFit];
evFrame = [ev frame];
/* this "layout mechanism" is pure evil... */
toPoint.x = floor(vis.origin.x + ((vis.size.width - evFrame.size.width) * rand()/(RAND_MAX + vis.origin.x)));
toPoint.y = floor(vis.origin.y + ((vis.size.height - evFrame.size.height) * rand()/(RAND_MAX + vis.origin.y)));
[ev setFrameOrigin:toPoint];
[_shownEntities setObject:ev forKey:[entity name]];
[self addSubview:ev];
[_subview_order addObject:ev];
[self setupRelationships];
}
else
{
EOEntity *entity = [_model entityNamed:name];
NSArray *attribs = [entity attributes];
int i, c = [attribs count];
[ev setTitle:[entity name]];
[ev setNumberOfAttributes:c];
for (i = 0; i < c; i++)
{
EOAttribute *attrib = [attribs objectAtIndex:i];
AttributeCell *cell = [ev cellAtRow:i];
[cell setStringValue:[attrib name]];
[cell setLock:[[entity attributesUsedForLocking] containsObject:attrib]];
[cell setProp:[[entity classProperties] containsObject:attrib]];
[cell setKey:[[entity primaryKeyAttributes] containsObject:attrib]];
}
[ev sizeToFit];
[ev setNeedsDisplay:YES];
}
}
int sortSubviews(id view1, id view2, void *context)
{
DiagramView *self = context;
unsigned idx1, idx2;
idx1 = [self->_subview_order indexOfObject:view1];
idx2 = [self->_subview_order indexOfObject:view2];
return (idx1 < idx2) ? NSOrderedAscending : NSOrderedDescending;
}
- (void) orderViewFront:(NSView *)v
{
int idx = [_subview_order indexOfObject:v];
RETAIN(v);
[_subview_order removeObjectAtIndex:idx];
[_subview_order addObject:v];
RELEASE(v);
[self sortSubviewsUsingFunction:(int (*)(id, id, void *))sortSubviews context:self];
[self setNeedsDisplay:YES];
}
- (void) setupRelationships
{
int i,c;
NSArray *stuff = [_shownEntities allKeys];
[_relationships removeAllObjects];
for (i = 0, c = [stuff count]; i < c; i++)
{
int j, d;
NSString *entName = [stuff objectAtIndex:i];
EOEntity *ent = [_model entityNamed:entName];
NSArray *rels = [ent relationships];
for (j = 0, d = [rels count]; j < d; j++)
{
EORelationship *rel = [rels objectAtIndex:j];
EOEntity *dest = [rel destinationEntity];
id srcName = [ent name];
id destName = [dest name];
EntityView *from = [_shownEntities objectForKey:srcName];
EntityView *to = [_shownEntities objectForKey:destName];
NSArray *srcAttribs = [rel sourceAttributes];
NSArray *destAttribs = [rel destinationAttributes];
int k, e;
for (k = 0, e = [srcAttribs count]; k < e; k++)
{
id sAttrib = [srcAttribs objectAtIndex:k];
id dAttrib = [destAttribs objectAtIndex:k];
int sIdx = [[ent attributes] indexOfObject:sAttrib];
int dIdx = [[dest attributes] indexOfObject:dAttrib];
NSRect fromRect = [from attributeRectAtRow:sIdx];
NSRect toRect = [to attributeRectAtRow:dIdx];
NSRect fromViewFrame = [from frame];
NSRect toViewFrame = [to frame];
NSPoint midPoint;
NSPoint tmp;
float arrowOffset;
NSBezierPath *path = [NSBezierPath bezierPath];
BOOL fromRight;
BOOL toRight;
[path setLineWidth:2];
fromRect.origin.y += fromViewFrame.origin.y;
toRect.origin.y += toViewFrame.origin.y;
/* which side of the EntityView the arrow line will be connecting
* to, for the source and destination entities */
fromRight = (fromViewFrame.origin.x - 40 < toViewFrame.origin.x + toViewFrame.size.width);
toRight = (toViewFrame.origin.x - 40 < fromViewFrame.origin.x + fromViewFrame.size.width);
if (fromRight)
{
fromRect.origin.x = fromViewFrame.origin.x + fromViewFrame.size.width + 5;
}
else
{
fromRect.origin.x = fromViewFrame.origin.x - 5;
}
if (toRight)
{
toRect.origin.x = toViewFrame.origin.x + toViewFrame.size.width;
toRect.origin.x += 5;
/* <- */
arrowOffset = -5.0;
}
else
{
toRect.origin.x = toViewFrame.origin.x;
toRect.origin.x -= 5;
/* -> */
arrowOffset = 5.0;
}
fromRect.origin.y = NSMidY(fromRect);
toRect.origin.y = NSMidY(toRect);
/* every line segment is drawn forwards and backwards so we dont
* end up with lightning bolts when filling the arrow */
/* a recursive relationship...
* Don't think they are particularly useful but... */
if (fromRect.origin.y == toRect.origin.y
&& fromRect.origin.x == toRect.origin.x)
{
[path moveToPoint:NSMakePoint(toRect.origin.x + 15, toRect.origin.y)];
[path lineToPoint:NSMakePoint(toRect.origin.x + 15, toRect.origin.y + 5)];
[path lineToPoint:NSMakePoint(toRect.origin.x + 15, toRect.origin.y)];
[path moveToPoint:NSMakePoint(toRect.origin.x + 15, toRect.origin.y + 5)];
[path lineToPoint:NSMakePoint(toRect.origin.x + 20, toRect.origin.y + 5)];
[path lineToPoint:NSMakePoint(toRect.origin.x + 15, toRect.origin.y + 5)];
[path moveToPoint:NSMakePoint(toRect.origin.x + 20, toRect.origin.y + 5)];
[path lineToPoint:NSMakePoint(toRect.origin.x + 20, toRect.origin.y)];
[path lineToPoint:NSMakePoint(toRect.origin.x + 20, toRect.origin.y + 5)];
}
if ((fromRight || toRight) && !(fromRight && toRight))
{
/* a line like... +-----
* |
* -----+
* (from the right side, to the left side or vice versa)
*/
[path moveToPoint:fromRect.origin];
midPoint.x = (fromRect.origin.x + toRect.origin.x) / 2;
midPoint.y = fromRect.origin.y;
[path lineToPoint:midPoint];
[path lineToPoint:fromRect.origin];
[path moveToPoint:midPoint];
tmp = midPoint;
midPoint.x = (fromRect.origin.x + toRect.origin.x) / 2;
midPoint.y = toRect.origin.y;
[path lineToPoint:midPoint];
[path lineToPoint:tmp];
[path moveToPoint:midPoint];
[path lineToPoint:toRect.origin];
[path lineToPoint:midPoint];
}
else if (fromRight && toRight)
{
/* need to --+ or -------+ <- joint end or start.
* make a line | |
* like... -------+ --+ <- joint start or end.
* from the right side to the right side.
*/
NSPoint jointStart;
NSPoint jointEnd;
if (toRect.origin.x + toRect.size.width < fromRect.origin.x + fromRect.size.width)
{
jointStart = NSMakePoint(fromRect.origin.x + 20, fromRect.origin.y);
jointEnd = NSMakePoint(fromRect.origin.x + 20, toRect.origin.y);
}
else
{
jointStart = NSMakePoint(toRect.origin.x + 20, fromRect.origin.y);
jointEnd = NSMakePoint(toRect.origin.x + 20, toRect.origin.y);
}
[path moveToPoint:fromRect.origin];
[path lineToPoint:jointStart];
[path lineToPoint:fromRect.origin];
[path moveToPoint:jointStart];
[path lineToPoint:jointEnd];
[path lineToPoint:jointStart];
[path moveToPoint:jointEnd];
[path lineToPoint:toRect.origin];
[path lineToPoint:jointEnd];
}
/* draw arrows.. */
if ([rel isToMany])
{
[path moveToPoint:toRect.origin];
[path lineToPoint:NSMakePoint(toRect.origin.x - arrowOffset, toRect.origin.y + arrowOffset)];
[path lineToPoint:NSMakePoint(toRect.origin.x - arrowOffset, toRect.origin.y - arrowOffset)];
[path lineToPoint:toRect.origin];
toRect.origin.x -= arrowOffset;
[path moveToPoint:toRect.origin];
[path lineToPoint:NSMakePoint(toRect.origin.x - arrowOffset, toRect.origin.y + arrowOffset)];
[path lineToPoint:NSMakePoint(toRect.origin.x - arrowOffset, toRect.origin.y - arrowOffset)];
[path lineToPoint:toRect.origin];
}
else
{
[path moveToPoint:toRect.origin];
[path lineToPoint:NSMakePoint(toRect.origin.x - arrowOffset, toRect.origin.y + arrowOffset)];
[path lineToPoint:NSMakePoint(toRect.origin.x - arrowOffset, toRect.origin.y - arrowOffset)];
[path lineToPoint:toRect.origin];
}
[path closePath];
[_relationships addObject:path];
}
}
}
}
- (void) mouseDown:(NSEvent *)ev
{
/* create new relationshp??*/
}
- (BOOL) autoscroll:(NSEvent *)ev
{
NSPoint pt = [self convertPoint:[ev locationInWindow] fromView:nil];
NSRect r = _frame;
BOOL flag;
if (!NSPointInRect(pt, r))
{
if (pt.x > r.size.width)
r.size.width = pt.x;
if (pt.y > r.size.height)
r.size.height = pt.y;
[self setFrameSize:r.size];
}
flag = [super autoscroll:ev];
return flag;
}
- (void) drawRect:(NSRect)aRect
{
int i, c;
[_bgColor set];
NSRectFill([self frame]);
[self setupRelationships];
for (i = 0, c = [_relationships count]; i < c; i++)
{
[[NSColor blackColor] set];
NSBezierPath *path = [_relationships objectAtIndex:i];
[path stroke];
[path fill];
}
}
@end