libs-gdl2/GDL2Palette/ConnectionInspector.m
Sebastian Reitenbach 829a02e46a * Apps/EOModelEditor/AdaptorsPanel.m
* Apps/EOModelEditor/Preferences.m
        * EOAccess/EOAttribute.m
        * EOAccess/EOExpressionArray.m
        * EOAccess/EORelationship.m
        * EOAdaptors/PostgreSQLAdaptor/LoginPanel/PostgreSQLLoginPanel.m
        * EOAdaptors/SQLiteAdaptor/SQLite3Channel.m
        * EOControl/EOCheapArray.m
        * EOControl/EOMutableKnownKeyDictionary.m
        * EOControl/EOPrivate.h
        * EOControl/EOPrivate.m
        * EOControl/Makefile.preamble
        * GDL2Palette/ConnectionInspector.m
        * GDL2Palette/DisplayGroupInspector.m
          Fix warnings about conflicting return types or paramters


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@36241 72102866-910b-0410-8b05-ffd578937521
2013-03-02 18:20:35 +00:00

815 lines
23 KiB
Objective-C

/**
ConnectInspector.m
Author: Matt Rice <ratmice@gmail.com>
Date: 2005, 2006
This file is part of GDL2Palette.
<license>
GDL2Palette 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.
GDL2Palette 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 GDL2Palette; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
</license>
**/
#include "ConnectionInspector.h"
#include "Foundation+Categories.h"
#include <EOAccess/EODatabaseDataSource.h>
#include <EOControl/EODetailDataSource.h>
#include <EOControl/EOClassDescription.h>
#include <AppKit/NSBrowser.h>
#include <AppKit/NSButton.h>
#include <AppKit/NSNibLoading.h>
#include <AppKit/NSPopUpButton.h>
#include <EOInterface/EOAspectConnector.h>
#include <EOInterface/EOAssociation.h>
#include <EOInterface/EODisplayGroup.h>
#include <EOModeler/EOModelExtensions.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSNull.h>
#include <Foundation/NSObject.h>
#include <Foundation/NSCharacterSet.h>
#include <GormCore/GormClassManager.h>
#include <GormCore/GormDocument.h>
#include <InterfaceBuilder/IBApplicationAdditions.h>
#include "KeyWrapper.h"
@interface GDL2ConnectionInspector (Private)
- (NSArray *) _associationClassesUsableWithObject:(id)anObject;
@end
@interface NSApplication(missingStuff)
- (GormClassManager *)classManager;
@end
@implementation GDL2ConnectionInspector
- (NSButton *)okButton
{
return nil;
}
- (id) init
{
self = [super init];
_values = [[NSMutableArray alloc] init];
[NSBundle loadNibNamed:@"GDL2ConnectionInspector" owner:self];
return self;
}
- (void) awakeFromNib
{
[oaBrowser setMaxVisibleColumns:2];
[oaBrowser setAllowsMultipleSelection:NO];
[oaBrowser setTitled:NO];
[oaBrowser setPathSeparator:@"."];
[connectionsBrowser setTitled:NO];
[connectionsBrowser setHasHorizontalScroller:NO];
[connectionsBrowser setMaxVisibleColumns:1];
[connectionsBrowser setAllowsMultipleSelection:NO];
/* doubt if this is neccessary but why not */
[connectionsBrowser setPathSeparator:@"."];
[popUp removeAllItems];
}
/* for populating the associations pop-up */
- (NSArray *) _associationClassesUsableWithObject:(id)anObject
{
NSMutableArray *usable;
NSMutableSet *superseded;
NSArray *allSuperseded;
int i,c;
usable = [[NSMutableArray alloc] init];
superseded = [[NSMutableSet alloc] init];
[usable addObjectsFromArray:[EOAssociation
associationClassesForObject:anObject]];
/* get all supserseded associations */
for (i = 0, c = [usable count]; i < c; i++)
{
id assocClass = [usable objectAtIndex:i];
[superseded addObjectsFromArray:
[assocClass associationClassesSuperseded]];
}
/* remove all superseded (even superseded associations superseded ones) */
allSuperseded = [superseded allObjects];
for (i = 0, c = [allSuperseded count]; i < c; i++)
{
id assocClass = [allSuperseded objectAtIndex:i];
[usable removeObject:assocClass];
}
RELEASE(superseded);
return AUTORELEASE(usable);
}
- (void) setObject:(id)anObject
{
id associationClasses;
int i,c;
[super setObject:anObject];
if (!object) return;
RELEASE(_connectors);
_connectors = [NSMutableArray new];
[_connectors addObjectsFromArray:[[[(id<IB>)NSApp activeDocument]
connectorsForSource:object]
arrayWithObjectsRespondingYesToSelector:@selector(isKindOfClasses:)
withObject:[NSArray arrayWithObjects:[NSNibControlConnector class],
[NSNibOutletConnector class],
[EOAspectConnector class],
nil]]];
[_values removeAllObjects];
[self updateKeys];
[popUp removeAllItems];
[popUp addItemWithTitle:@"Outlets"];
associationClasses = [self _associationClassesUsableWithObject:anObject];
for (i = 0, c = [associationClasses count]; i < c; i++)
{
Class assocSubclass = [associationClasses objectAtIndex:i];
NSString *title = [assocSubclass displayName];
[popUp addItemWithTitle:title];
[[popUp itemWithTitle:title] setRepresentedObject:assocSubclass];
}
[connectionsBrowser reloadColumn:0];
[oaBrowser loadColumnZero];
[self updateButtons];
}
- (void) _popUpAction:(id)sender
{
[self updateKeys];
[oaBrowser reloadColumn:0];
[oaBrowser reloadColumn:1];
[connectionsBrowser reloadColumn:0];
}
- (void) updateKeys
{
Class repObj = [[popUp selectedItem] representedObject];
RELEASE(_keys);
RELEASE(_signatures);
/* outlets.. */
if (repObj == nil)
{
/* gorm specific... but couldn't find a public standard api replacement */
/* see bug #17822 */
_keys = RETAIN([[NSApp classManager] allOutletsForObject:object]);
_signatures = nil;
}
else
{
_keys = RETAIN([repObj aspects]);
_signatures = RETAIN([repObj aspectSignatures]);
}
}
- (NSArray *)_keysFromClassDescription:(EOClassDescription *)classDesc
{
NSMutableArray *ret = [[NSMutableArray alloc] init];
unsigned i, c, j;
for (i = 0; i < 3; i++)
{
int type = 0;
NSArray *tmp = nil;
switch(i)
{
case 0:
tmp = [classDesc attributeKeys];
type = AttributeType;
break;
case 1:
type = ToManyRelationshipType;
tmp = [classDesc toManyRelationshipKeys];
break;
case 2:
type = ToOneRelationshipType;
tmp = [classDesc toOneRelationshipKeys];
break;
}
for (j = 0, c = [tmp count]; j < c; j++)
{
id obj = [tmp objectAtIndex:j];
id key = [KeyWrapper wrapperWithKey:obj type:type];
[ret addObject:key];
}
}
return AUTORELEASE(ret);
}
- (NSArray *) _localKeysFromDisplayGroup:(EODisplayGroup *)dg
{
NSMutableArray *ret = [[NSMutableArray alloc] init];
NSArray *local = [dg localKeys];
int i,c;
for (i = 0, c = [local count]; i < c; i++)
{
id obj = [local objectAtIndex:i];
id key = [KeyWrapper wrapperWithKey:obj type:LocalType];
[ret addObject:key];
}
return AUTORELEASE(ret);
}
/* for normal outlets/actions */
- (NSArray *) _keysFromArray:(NSArray *)arr
{
NSMutableArray *ret = [[NSMutableArray alloc] init];
int i, c;
for (i = 0, c = [arr count]; i < c; i++)
[ret addObject:[KeyWrapper wrapperWithKey:[arr objectAtIndex:i]
type:OtherType]];
return AUTORELEASE(ret);
}
- (void) updateValues
{
EODisplayGroup *dest = (EODisplayGroup *)[NSApp connectDestination];
EODataSource *ds = [dest dataSource];
[_values removeAllObjects];
if ([dest isKindOfClass:[EODisplayGroup class]])
{
if ([ds isKindOfClass:[EODataSource class]])
{
id cd = [ds classDescriptionForObjects];
[_values addObjectsFromArray:[self _keysFromClassDescription:cd]];
}
[_values addObjectsFromArray:[self _localKeysFromDisplayGroup:dest]];
}
}
- (void) _selectAction:(NSString*)label
{
[oaBrowser reloadColumn:1];
if (label != nil)
[oaBrowser selectRow:[_values indexOfObject:label] inColumn:1];
}
- (void) _oaBrowserAction:(id)sender
{
unsigned i,c;
NSNibConnector *conn;
if ([sender selectedColumn] == 0)
{
id dest;
/* not an association */
if ([[popUp selectedItem] representedObject] == nil)
{
/* browsing actions */
if ([[[sender selectedCell] stringValue] isEqual:@"target"])
{
NSArray *controlConnectors;
controlConnectors = [_connectors arrayWithObjectsRespondingYesToSelector:@selector(isKindOfClass:)
withObject:[NSNibControlConnector class]];
c = [controlConnectors count];
conn = c ? [controlConnectors objectAtIndex:0]
: nil;
dest = c ? [conn destination]
: [NSApp connectDestination];
[_values removeAllObjects];
/* gorm specific...
* but couldn't find a public standard api replacement
* for allActionsForObject
*/
[_values addObjectsFromArray:[self _keysFromArray:[[NSApp classManager]
allActionsForObject: dest]]];
if ([_values count] > 0)
{
conn = [NSNibControlConnector new];
[conn setSource: object];
[conn setDestination: [NSApp connectDestination]];
[conn setLabel: [[_values objectAtIndex:0] key]];
AUTORELEASE(conn);
}
if (_currentConnector != conn)
ASSIGN(_currentConnector, conn);
[self _selectAction: [conn label]];
}
else /* browsing outlets */
{
BOOL found = NO;
NSString *title = [[sender selectedCell] stringValue];
NSArray *oConns;
oConns = [_connectors
arrayWithObjectsRespondingYesToSelector:
@selector(isKindOfClass:)
withObject:[NSNibOutletConnector class]];
for (i = 0, c = [oConns count]; i < c; i++)
{
conn = [oConns objectAtIndex:i];
if ([conn label] == nil || [[conn label] isEqual:title])
{
found = YES;
ASSIGN(_currentConnector, conn);
break;
}
}
if (!found)
{
RELEASE(_currentConnector);
_currentConnector = [NSNibOutletConnector new];
[_currentConnector setSource:object];
[_currentConnector setDestination:[NSApp connectDestination]];
[_currentConnector setLabel:title];
}
}
[connectionsBrowser loadColumnZero];
[self selectedOutletOrAction];
}
else /* association connector */
{
[self updateValues];
[oaBrowser reloadColumn:1];
}
[okButton setEnabled:YES];
}
else
{
if ([[popUp selectedItem] representedObject] == nil)
{
BOOL found = NO;
NSString *title = [[sender selectedCell] stringValue];
NSArray *cConns;
cConns = [_connectors
arrayWithObjectsRespondingYesToSelector:
@selector(isKindOfClass:)
withObject:[NSNibControlConnector class]];
for (i = 0, c = [cConns count]; i < c; i++)
{
NSNibConnector *con = [cConns objectAtIndex:i];
NSString *action = [con label];
if ([action isEqual:title])
{
ASSIGN(_currentConnector, con);
found = YES;
break;
}
}
if (!found)
{
RELEASE(_currentConnector);
_currentConnector = [NSNibControlConnector new];
[_currentConnector setSource:object];
[_currentConnector setDestination:[NSApp connectDestination]];
[_currentConnector setLabel:title];
[connectionsBrowser loadColumnZero];
}
[self selectedOutletOrAction];
}
else
{
BOOL found = NO;
NSString *aspectName = [[sender selectedCellInColumn:0] stringValue];
NSString *key;
NSCharacterSet *dotCharSet;
NSString *prefix = [NSString stringWithFormat:@"%@.", aspectName];
NSString *label;
NSArray *aspectConnectors;
/* turn ".aspectName.foo.bar.baz." into "foo.bar.baz" */
dotCharSet = [NSCharacterSet characterSetWithCharactersInString:@"."];
key = [[[sender path] stringByTrimmingCharactersInSet:dotCharSet]
stringByDeletingPrefix:prefix];
/* "aspectName - foo.bar.baz" */
label = [NSString stringWithFormat:@"%@ - %@", aspectName, key];
aspectConnectors = [_connectors arrayWithObjectsRespondingYesToSelector:@selector(isKindOfClass:)
withObject:[EOAspectConnector class]];
for (i = 0, c = [aspectConnectors count]; i < c; i++)
{
NSNibConnector *con = [aspectConnectors objectAtIndex:i];
if ([con source] == object && [[con label] isEqual: label])
{
ASSIGN(_currentConnector, con);
found = YES;
break;
}
}
if (!found)
{
NSArray *aConns = [[(id<IB>)NSApp activeDocument] connectorsForSource:object ofClass:[EOAspectConnector class]];
Class assocClass = [[popUp selectedItem] representedObject];
_association = nil;
for (i = 0; i < c; i++)
{
EOAspectConnector *aConn = [aConns objectAtIndex:i];
EOAssociation *assoc = [aConn association];
if ([[assoc class] isEqual: assocClass])
{
ASSIGN(_association, assoc);
}
}
if (!_association)
{
_association = [[assocClass alloc] initWithObject:object];
/* this shouldn't happen until ok:. */
}
[_association bindAspect:aspectName
displayGroup:[NSApp connectDestination]
key:key];
RELEASE(_currentConnector);
_currentConnector = [[EOAspectConnector alloc]
initWithAssociation:_association
aspectName:aspectName];
[_currentConnector setSource:object];
[_currentConnector setDestination: [NSApp connectDestination]];
[_currentConnector setLabel:label];
}
/* fixme {'s and identation */
{
int i;
NSArray *vals = _values;
EODisplayGroup *dest;
EODataSource *ds;
EOClassDescription *classDesc;
id val;
KeyType type;
int wantsTypes = 0;
int column = [oaBrowser selectedColumn];
int row = [oaBrowser selectedRowInColumn:column];
int zeroRow = [oaBrowser selectedRowInColumn:0];
NSString *sig = [[_signatures objectAtIndex:zeroRow]
uppercaseString];
dest = (EODisplayGroup *)[NSApp connectDestination];
ds = [dest dataSource];
classDesc = [ds classDescriptionForObjects];
for (i = 1; i < column; i++)
{
int aRow = [sender selectedRowInColumn:i];
val = [vals objectAtIndex:aRow];
type = [val keyType];
if (type == ToManyRelationshipType
|| type == ToOneRelationshipType)
{
classDesc =
[classDesc classDescriptionForDestinationKey:[val key]];
vals = [self _keysFromClassDescription:classDesc];
}
}
val = [vals objectAtIndex:row];
type = [val keyType];
for (i = 0; i < [sig length]; i++)
{
switch ([sig characterAtIndex:i])
{
case 'A': wantsTypes |= AttributeType; break;
case '1': wantsTypes |= ToOneRelationshipType; break;
case 'M': wantsTypes |= ToManyRelationshipType; break;
}
}
[okButton setEnabled:(wantsTypes & type)
|| (type == LocalType)];
}
}
}
[self updateButtons];
}
- (void) selectedOutletOrAction
{
NSString *path;
NSString *name = [[(id <IB>)NSApp activeDocument] nameForObject:[_currentConnector destination]];
path = [@"." stringByAppendingString:[_currentConnector label]];
path = [path stringByAppendingFormat:@" (%@)", name];
[connectionsBrowser setPath:path];
}
- (void) updateButtons
{
/* FIXME enable/disable okButton based off signature */
if (!_currentConnector)
{
[okButton setState: NSOffState];
}
else
{
id src, dest;
/* FIXME i wonder why this is not [self object]; */
id firstResponder = [(GormDocument *)[(id<IB>)NSApp activeDocument] firstResponder];
src = [_currentConnector source];
dest = [_currentConnector destination];
if (src && src != firstResponder
&& ((dest && dest != firstResponder)
|| ![_currentConnector isKindOfClass:[NSNibOutletConnector class]]))
{
BOOL flag;
flag = [_connectors containsObject:_currentConnector];
[okButton setState: flag ? NSOnState : NSOffState];
}
else
{
[okButton setState: NSOnState];
}
}
}
- (void) _connectionsBrowserAction:(id)sender
{
int i,c;
NSNibConnector *con;
NSString *title = [[sender selectedCell] stringValue];
for (i = 0, c = [_connectors count]; i < c; i++)
{
NSString *label;
con = [_connectors objectAtIndex:i];
label = [con label];
if (label == nil || [title hasPrefix:label])
{
NSString *name;
id dest;
dest = [con destination];
name = [[(id <IB>)NSApp activeDocument] nameForObject:dest];
name = [label stringByAppendingFormat: @" (%@)", name];
if ([title isEqual:name])
{
ASSIGN(_currentConnector, con);
[self selectedConnector];
break;
}
}
}
[self updateButtons];
}
- (void) selectedConnector
{
NSString *path;
path = [@"." stringByAppendingString:[_currentConnector label]];
if ([_currentConnector isKindOfClass: [NSNibControlConnector class]])
{
path = [@".target" stringByAppendingString:path];
}
[oaBrowser setPath:path];
[NSApp displayConnectionBetween:object and:[_currentConnector destination]];
}
- (NSInteger) browser:(NSBrowser *)browser
numberOfRowsInColumn:(NSInteger)column
{
id repObj = [[popUp selectedItem] representedObject];
if (browser == oaBrowser)
{
if (column == 0)
{
return [_keys count];
}
else
{
if (repObj == nil)
{
if ([[[browser selectedCellInColumn:0] stringValue] isEqual:@"target"])
{
return [_values count];
}
else return 0;
}
else
{
int i;
NSArray *vals = _values;
EODisplayGroup *dest;
EODataSource *ds;
EOClassDescription *classDesc;
dest = (EODisplayGroup *)[NSApp connectDestination];
ds = [dest dataSource];
classDesc = [ds classDescriptionForObjects];
for (i = 1; i < column; i++)
{
int row = [browser selectedRowInColumn:i];
id val = [vals objectAtIndex:row];
if ([val keyType] != AttributeType)
{
classDesc =
[classDesc classDescriptionForDestinationKey: [val key]];
vals = [self _keysFromClassDescription:classDesc];
}
}
return [vals count];
}
}
}
else if (browser == connectionsBrowser)
{
return [_connectors count];
}
return 0;
}
- (void) browser:(NSBrowser *)sender
willDisplayCell:(id)cell atRow:(NSInteger)row column:(NSInteger)column
{
id repObj = [[popUp selectedItem] representedObject];
// FIXME -objectKeysTaken
if (sender == oaBrowser)
if (column == 0)
{
if (repObj == nil)
{
NSString *name;
name = [_keys objectAtIndex:row];
[cell setStringValue:name];
[cell setLeaf: [name isEqual:@"target" ] ? NO : YES];
[cell setEnabled:YES];
}
else
{
[cell setStringValue: [_keys objectAtIndex:row]];
[cell setLeaf:[[_signatures objectAtIndex:row] length] == 0];
}
}
else
{
if (repObj == nil)
{
if ([[[sender selectedCellInColumn:0] stringValue] isEqual:@"target"])
{
id val = [[_values objectAtIndex:row] key];
[cell setLeaf:YES];
[cell setStringValue: val];
[cell setEnabled:YES];
}
}
else
{
int i;
NSArray *vals = _values;
EODisplayGroup *dest;
EODataSource *ds;
EOClassDescription *classDesc;
id val;
KeyType type;
int wantsTypes = 0;
int zeroRow = [oaBrowser selectedRowInColumn:0];
NSString *sig = [[_signatures objectAtIndex:zeroRow]
uppercaseString];
dest = (EODisplayGroup *)[NSApp connectDestination];
ds = [dest dataSource];
classDesc = [ds classDescriptionForObjects];
for (i = 1; i < column; i++)
{
int aRow = [sender selectedRowInColumn:i];
val = [vals objectAtIndex:aRow];
type = [val keyType];
if (type != AttributeType || type != OtherType)
{
classDesc =
[classDesc classDescriptionForDestinationKey:[val key]];
vals = [self _keysFromClassDescription:classDesc];
}
}
val = [vals objectAtIndex:row];
type = [val keyType];
for (i = 0; i < [sig length]; i++)
{
switch ([sig characterAtIndex:i])
{
case 'A': wantsTypes |= AttributeType; break;
case '1': wantsTypes |= ToOneRelationshipType; break;
case 'M': wantsTypes |= ToManyRelationshipType; break;
}
}
[cell setLeaf: (type == AttributeType
|| type == LocalType)];
// TODO relationships should be NO...
[cell setStringValue: [val key]];
[cell setEnabled:(wantsTypes & type)
|| (wantsTypes & AttributeType)
|| (type == LocalType)];
}
}
else if (sender == connectionsBrowser)
{
NSNibConnector *conn;
NSString *name, *dest, *label;
if (row < 0 || column != 0) return;
conn = [_connectors objectAtIndex:row];
label = [conn label];
dest = [conn destination];
name = [[(id<IB>)NSApp activeDocument] nameForObject: dest];
[cell setStringValue: [label stringByAppendingFormat:@" (%@)", name]];
[cell setEnabled:YES];
[cell setLeaf:YES];
}
}
- (void) ok:(id)sender
{
if ([_currentConnector destination] == nil ||
[_currentConnector source] == nil)
{
NSRunAlertPanel(_(@"Problem making connection"),
_(@"Please select a valid destination."),
_(@"OK"), nil, nil, nil);
}
else if ([_connectors containsObject:_currentConnector] == YES)
{
[[(id<IB>)NSApp activeDocument] removeConnector: _currentConnector];
[_connectors removeObject:_currentConnector];
[connectionsBrowser loadColumnZero];
}
else
{
if ([_currentConnector isKindOfClass:[NSNibControlConnector class]])
{
int i, c;
NSArray *controlConnectors;
controlConnectors = [_connectors arrayWithObjectsRespondingYesToSelector:@selector(isKindOfClass:)
withObject:[NSNibControlConnector class]];
for (i = 0, c = [controlConnectors count]; i < c; i++)
{
NSNibConnector *con = [controlConnectors objectAtIndex:i];
[[(id<IB>)NSApp activeDocument] removeConnector: con];
[_connectors removeObject:con];
}
[_connectors addObject:_currentConnector];
}
else if ([_currentConnector isKindOfClass:[NSNibOutletConnector class]])
{
[_connectors addObject:_currentConnector];
}
else if ([_currentConnector isKindOfClass:[EOAspectConnector class]])
{
[_connectors addObject:_currentConnector];
[[(id<IB>)NSApp activeDocument]
attachObject:_association toParent:object];
}
[self _selectAction:[_currentConnector label]];
[[(id<IB>)NSApp activeDocument] addConnector: _currentConnector];
[connectionsBrowser loadColumnZero];
[self selectedConnector];
}
[[(id<IB>)NSApp activeDocument] touch];
[self updateButtons];
}
@end