mirror of
https://github.com/gnustep/libs-gdl2.git
synced 2025-02-16 08:20:59 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@29865 72102866-910b-0410-8b05-ffd578937521
424 lines
14 KiB
Objective-C
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
|