Subversion Repository Public Repository

litesoft

Diff Revisions 949 vs 950 for /trunk/Java/KeyHole/src/org/litesoft/aokeyhole/toolkit/ObjectAndAttributesWithOneDegreeSeperationGraphicView.java

Diff revisions: vs.
  @@ -1,489 +1,489 @@
1 - // This Source Code is in the Public Domain per: http://unlicense.org
2 - package org.litesoft.aokeyhole.toolkit;
3 -
4 - import java.awt.*;
5 -
6 - public class ObjectAndAttributesWithOneDegreeSeperationGraphicView
7 - implements ObjectAndAttributesWithOneDegreeSeperationView,
8 - ScaledDrawableBoxedTextClickable,
9 - View {
10 - public static final Color COLOR_BG_OBJECT = new Color( 255, 255, 196 ); // LIGHT_YELLOW
11 - public static final Color COLOR_BG_ATTRIB_SIMPLE = new Color( 196, 255, 196 ); // MID_GREEN
12 - public static final Color COLOR_BG_ATTRIB_VIRTUAL = new Color( 220, 255, 220 ); // LIGHT_GREEN
13 -
14 - private BoxedText mPrimeObject;
15 -
16 - private ConnectionSet mConnections = new ConnectionSet();
17 - private AllBoxedSet mAll = new AllBoxedSet();
18 - private MainBoxedSet mPrimePlusAttributess;
19 - private RelatedBoxedSet mRelatedObjects;
20 - private MultiBoxedSet mLowerObjects;
21 - private BoxedText mUpperObject = null;
22 -
23 - private View mView = ViewNull.INSTANCE;
24 -
25 - public ObjectAndAttributesWithOneDegreeSeperationGraphicView() {
26 - mPrimeObject = mAll.add( new BoxedText( TYPE_OBJECT_MAIN, COLOR_BG_OBJECT, "" ) );
27 -
28 - mPrimePlusAttributess = new MainBoxedSet( AttributeColorSource.INSTANCE, mPrimeObject );
29 - mPrimePlusAttributess.setView( this ); // Note Leakage of 'this'
30 -
31 - mRelatedObjects = new RelatedBoxedSet( ObjectColorSource.INSTANCE );
32 - mRelatedObjects.setView( this ); // Note Leakage of 'this'
33 -
34 - mLowerObjects = new MultiBoxedSet( ObjectColorSource.INSTANCE );
35 - mLowerObjects.setView( this ); // Note Leakage of 'this'
36 - }
37 -
38 - public void setView( View pView ) {
39 - mView = (pView != null) ? pView : ViewNull.INSTANCE;
40 - }
41 -
42 - @Override
43 - public void update() {
44 - mView.update();
45 - }
46 -
47 - @Override
48 - public void draw( Graphics pGraphics, Dimension pSize ) {
49 - mAll.finish( pGraphics );
50 -
51 - mPrimePlusAttributess.makeSameWidths();
52 - mRelatedObjects.makeSameWidths();
53 - mLowerObjects.makeSameWidths();
54 -
55 - int width = pSize.width;
56 - int height = pSize.height;
57 -
58 - int midX = width / 2;
59 -
60 - height = mLowerObjects.layout( height, midX );
61 -
62 - mPrimePlusAttributess.layout( height, midX, mUpperObject );
63 -
64 - int left = midX / 4;
65 - int right = width - left;
66 -
67 - mRelatedObjects.layout( height, left, right );
68 -
69 - mConnections.draw( pGraphics );
70 -
71 - drawConnectionToUpperObject( pGraphics );
72 - drawConnectionsToLowerObjects( pGraphics );
73 -
74 - mAll.draw( pGraphics );
75 - }
76 -
77 - private void drawConnectionToUpperObject( Graphics pGraphics ) {
78 - if ( mUpperObject != null ) {
79 - mPrimeObject.drawConnectionCenterToCenter( pGraphics, mUpperObject );
80 - }
81 - }
82 -
83 - private void drawConnectionsToLowerObjects( Graphics pGraphics ) {
84 - int size = mLowerObjects.size();
85 - if ( size > 0 ) {
86 - BoxedText lastBox = mPrimePlusAttributess.getLastBox();
87 - if ( lastBox != null ) {
88 - BoxedText[] children = mLowerObjects.getBoxes();
89 - for ( BoxedText child : children ) {
90 - lastBox.drawConnectionCenterToCenter( pGraphics, child );
91 - }
92 - }
93 - }
94 - }
95 -
96 - @Override
97 - public void dispose() {
98 - }
99 -
100 - @Override
101 - public void removeAttribute( int pID ) {
102 - if ( mPrimePlusAttributess.remove( pID ) ) {
103 - mAll.remove( pID );
104 - BoxedText related = mConnections.remove( pID );
105 - if ( related != null ) {
106 - mAll.remove( related.getID() );
107 - mRelatedObjects.remove( related.getID() );
108 - }
109 - }
110 - }
111 -
112 - @Override
113 - public void addNewAttributeOption() {
114 - mAll.add( mPrimePlusAttributess.addNewAttrib() );
115 - }
116 -
117 - @Override
118 - public int addSimpleAttribute( String pAttributeName, boolean pIsVirtual ) {
119 - return mAll.add( mPrimePlusAttributess.add( pAttributeName, pIsVirtual ) ).getID();
120 - }
121 -
122 - @Override
123 - public int addRelatedAttributeToSelf( String pAttributeName, boolean pIsVirtual ) {
124 - BoxedText attrib = mAll.add( mPrimePlusAttributess.add( pAttributeName, pIsVirtual ) );
125 -
126 - mConnections.add( attrib, null );
127 -
128 - return attrib.getID();
129 - }
130 -
131 - @Override
132 - public int addRelatedAttribute( String pAttributeName, boolean pIsVirtual, String pObjectName ) {
133 - BoxedText attrib = mAll.add( mPrimePlusAttributess.add( pAttributeName, pIsVirtual ) );
134 - BoxedText object = mAll.add( mRelatedObjects.add( pObjectName, null ) );
135 -
136 - mConnections.add( attrib, object );
137 -
138 - return attrib.getID();
139 - }
140 -
141 - @Override
142 - public void changeObjectName( String pNewName ) {
143 - mPrimeObject.setName( pNewName );
144 - }
145 -
146 - @Override
147 - public boolean changeAttributeName( int pID, String pNewName ) {
148 - return mPrimePlusAttributess.changeName( pID, pNewName );
149 - }
150 -
151 - @Override
152 - public void setParentName( String pNewName ) {
153 - if ( mUpperObject != null ) {
154 - if ( pNewName != null ) {
155 - mUpperObject.setName( pNewName );
156 - return;
157 - }
158 - int id = mUpperObject.getID();
159 - mUpperObject = null;
160 - mAll.remove( id );
161 - return;
162 - }
163 - if ( pNewName != null ) {
164 - mUpperObject = new BoxedText( TYPE_OBJECT_RELATED, COLOR_BG_OBJECT, pNewName );
165 - mAll.add( mUpperObject );
166 - mUpperObject.setControllingSet( mAll );
167 - }
168 - }
169 -
170 - /**
171 - * Note this is not really a dynamic field, so teardown and rebuild is acceptable
172 - */
173 - @Override
174 - public void setGeneratedRelationsByParentNames( String[] pNames ) {
175 - int size = mLowerObjects.size();
176 - if ( size != 0 ) {
177 - int[] ids = new int[size];
178 - BoxedText[] boxes = mLowerObjects.getBoxes();
179 - for ( int i = 0; i < boxes.length; i++ ) {
180 - ids[i] = boxes[i].getID();
181 - }
182 - for ( int id : ids ) {
183 - mLowerObjects.remove( id );
184 - mAll.remove( id );
185 - }
186 - }
187 - for ( String zName : pNames ) {
188 - mAll.add( mLowerObjects.add( zName, null ) );
189 - }
190 - }
191 -
192 - @Override
193 - public BoxedText getClickedOn( Point pAt ) {
194 - return mAll.getClickedOn( pAt );
195 - }
196 -
197 - // Supporting Classes... ==========================================================================================
198 -
199 - private static class ConnectionSet {
200 - private Connection[] mConnections = Connection.EMPTY_ARRAY;
201 -
202 - public void draw( Graphics pGraphics ) {
203 - for ( Connection connection : mConnections ) {
204 - connection.draw( pGraphics );
205 - }
206 - }
207 -
208 - public void add( BoxedText pFrom, BoxedText pTo ) {
209 - mConnections = append( mConnections, new Connection( pFrom, pTo ) );
210 - }
211 -
212 - private static Connection[] append( Connection[] pOrig, Connection pNew ) {
213 - if ( pNew == null ) {
214 - return pOrig;
215 - }
216 - Connection[] rv = new Connection[pOrig.length + 1];
217 - rv[0] = pNew;
218 - System.arraycopy( pOrig, 0, rv, 1, pOrig.length );
219 - return rv;
220 - }
221 -
222 - public BoxedText remove( int pID ) {
223 - for ( int i = 0; i < mConnections.length; i++ ) {
224 - Connection connection = mConnections[i];
225 - if ( connection.mFrom.getID() == pID ) {
226 - innerRemove( i );
227 - return connection.mTo;
228 - }
229 - if ( (connection.mTo != null) && (connection.mTo.getID() == pID) ) {
230 - innerRemove( i );
231 - return connection.mFrom;
232 - }
233 - }
234 - return null;
235 - }
236 -
237 - private void innerRemove( int pIndex ) {
238 - Connection[] shrunk = new Connection[mConnections.length - 1];
239 - if ( pIndex != 0 ) // Not First
240 - {
241 - System.arraycopy( mConnections, 0, shrunk, 0, pIndex );
242 - }
243 - if ( pIndex != mConnections.length - 1 ) // Not Last
244 - {
245 - System.arraycopy( mConnections, pIndex + 1, shrunk, pIndex, shrunk.length - pIndex );
246 - }
247 - mConnections = shrunk;
248 - }
249 -
250 - private static class Connection {
251 - public static final Connection[] EMPTY_ARRAY = new Connection[0];
252 -
253 - private BoxedText mFrom, mTo;
254 -
255 - public Connection( BoxedText pFrom, BoxedText pTo ) {
256 - mFrom = pFrom;
257 - mTo = pTo;
258 - }
259 -
260 - public void draw( Graphics pGraphics ) {
261 - mFrom.drawConnection( pGraphics, mTo );
262 - }
263 - }
264 - }
265 -
266 - private static class ObjectColorSource implements ColorSource {
267 - public static final ColorSource INSTANCE = new ObjectColorSource();
268 -
269 - @Override
270 - public Color getColor( Object pParameter ) {
271 - return COLOR_BG_OBJECT;
272 - }
273 - }
274 -
275 - private static class AttributeColorSource implements ColorSource {
276 - public static final ColorSource INSTANCE = new AttributeColorSource();
277 -
278 - @Override
279 - public Color getColor( Object pParameter ) {
280 - return Boolean.TRUE.equals( pParameter ) ? COLOR_BG_ATTRIB_VIRTUAL : COLOR_BG_ATTRIB_SIMPLE;
281 - }
282 - }
283 -
284 - private static class AllBoxedSet extends BoxedSet {
285 - private boolean mUnfinished = false;
286 -
287 - public void finish( Graphics pGraphics ) {
288 - if ( mUnfinished ) {
289 - for ( BoxedText box : mBoxes ) {
290 - box.finish( pGraphics );
291 - }
292 - }
293 - }
294 -
295 - @Override
296 - public void setChanged() {
297 - mUnfinished = true;
298 - super.setChanged();
299 - }
300 -
301 - public BoxedText getClickedOn( Point pAt ) {
302 - for ( int i = mBoxes.length; --i >= 0; ) // Note reverse order due to Overlaying
303 - {
304 - BoxedText box = mBoxes[i];
305 - if ( box.isClickedOn( pAt ) ) {
306 - return box;
307 - }
308 - }
309 - return null;
310 - }
311 - }
312 -
313 - private abstract static class ColorSourcedBoxedSet extends BoxedSet {
314 - private int mType;
315 - private ColorSource mColorSource;
316 -
317 - public ColorSourcedBoxedSet( int pType, ColorSource pColorSource ) {
318 - mType = pType;
319 - mColorSource = pColorSource;
320 - }
321 -
322 - public BoxedText add( String pName, Object pParameter ) {
323 - return add( new BoxedText( mType, mColorSource.getColor( pParameter ), pName ) );
324 - }
325 - }
326 -
327 - private static class SameWidthBoxedSet extends ColorSourcedBoxedSet {
328 - private boolean mWidthsProbablyNotSame = false;
329 -
330 - public SameWidthBoxedSet( int pType, ColorSource pColorSource ) {
331 - super( pType, pColorSource );
332 - }
333 -
334 - public void makeSameWidths() {
335 - if ( mWidthsProbablyNotSame ) {
336 - mWidthsProbablyNotSame = false;
337 - int maxBoxWidth = 0;
338 - for ( BoxedText box : mBoxes ) {
339 - maxBoxWidth = Math.max( maxBoxWidth, box.getBoxWidth() );
340 - }
341 - for ( BoxedText box : mBoxes ) {
342 - box.setBoxWidth( maxBoxWidth );
343 - }
344 - }
345 - }
346 -
347 - @Override
348 - public BoxedText add( BoxedText pNew ) {
349 - BoxedText boxedText = super.add( pNew );
350 - boxedText.setControllingSet( this );
351 - return boxedText;
352 - }
353 -
354 - @Override
355 - public void setChanged() {
356 - mWidthsProbablyNotSame = (mBoxes.length > 1);
357 - super.setChanged();
358 - }
359 - }
360 -
361 - private static class MainBoxedSet extends SameWidthBoxedSet {
362 - private BoxedText mNewAttrib = null;
363 -
364 - public MainBoxedSet( ColorSource pColorSource, BoxedText pPrime ) {
365 - super( TYPE_ATTRIB_EXISTING, pColorSource );
366 -
367 - add( pPrime );
368 - }
369 -
370 - @Override
371 - public void dispose() {
372 - mNewAttrib = null;
373 - super.dispose();
374 - }
375 -
376 - public BoxedText addNewAttrib() {
377 - if ( mNewAttrib != null ) {
378 - return null;
379 - }
380 - mNewAttrib = add( new BoxedText( TYPE_ATTRIB_NEW, COLOR_BG_ATTRIB_VIRTUAL, "+" ) );
381 - return mNewAttrib;
382 - }
383 -
384 - @Override
385 - protected void innerAdd( BoxedText pNew ) {
386 - super.innerAdd( pNew ); // Add to end
387 - if ( mNewAttrib != null ) {
388 - // Swap Ends so that the end one is "New Attribute" option
389 - BoxedText last = mBoxes[mBoxes.length - 1];
390 - mBoxes[mBoxes.length - 1] = mBoxes[mBoxes.length - 2];
391 - mBoxes[mBoxes.length - 2] = last;
392 - }
393 - }
394 -
395 - public void layout( int pHeight, int pAlignOnX, BoxedText pUpperObject ) {
396 - int boxHeight = mBoxes[0].getBoxHeight();
397 - int baseTop = 0;
398 -
399 - if ( pUpperObject != null ) {
400 - pUpperObject.setAtXY( pAlignOnX, boxHeight );
401 - baseTop = boxHeight + (boxHeight / 2);
402 - }
403 -
404 - int atY, deltaY;
405 -
406 - int overallAvail = pHeight - (baseTop + boxHeight); // remove baseTop + spacing
407 - int overallOptimalSize = size() * boxHeight;
408 -
409 - if ( overallAvail < overallOptimalSize ) {
410 - atY = baseTop + boxHeight; // spacer + centering
411 - deltaY = overallAvail / size();
412 - } else {
413 - atY = baseTop + ((overallAvail - overallOptimalSize) / 3) + boxHeight; // spacer + centering
414 - deltaY = boxHeight;
415 - }
416 -
417 - for ( BoxedText box : mBoxes ) {
418 - box.setAtXY( pAlignOnX, atY );
419 - atY += deltaY;
420 - }
421 - }
422 -
423 - public boolean changeName( int pID, String pNewName ) {
424 - for ( BoxedText box : mBoxes ) {
425 - if ( box.getID() == pID ) {
426 - box.setName( pNewName );
427 - return true;
428 - }
429 - }
430 - return false;
431 - }
432 - }
433 -
434 - private static class RelatedBoxedSet extends SameWidthBoxedSet {
435 - public RelatedBoxedSet( ColorSource pColorSource ) {
436 - super( TYPE_OBJECT_RELATED, pColorSource );
437 - }
438 -
439 - public void layout( int pHeight, int pAlignOnLeftX, int pAlignOnRightX ) {
440 - int half = (size() + 1) / 2;
441 -
442 - setBoxes( 0, half, pAlignOnLeftX, pHeight );
443 - setBoxes( 1, size() - half, pAlignOnRightX, pHeight );
444 - }
445 -
446 - private void setBoxes( int pFrom, int pCount, int pAtX, int pYspace ) {
447 - if ( pCount > 0 ) {
448 - int deltaY = pYspace / (pCount + 1);
449 - for ( int atY = 0; pFrom < mBoxes.length; pFrom += 2 ) {
450 - mBoxes[pFrom].setAtXY( pAtX, atY += deltaY );
451 - }
452 - }
453 - }
454 - }
455 -
456 - private static class MultiBoxedSet extends SameWidthBoxedSet {
457 - public MultiBoxedSet( ColorSource pColorSource ) {
458 - super( TYPE_OBJECT_RELATED, pColorSource );
459 - }
460 -
461 - public int layout( int pHeight, int pAlignOnX ) {
462 - if ( mBoxes.length == 0 ) {
463 - return pHeight;
464 - }
465 - int boxWidth = mBoxes[0].getBoxWidth();
466 - int boxHeight = mBoxes[0].getBoxHeight();
467 - int atY = pHeight - boxHeight;
468 -
469 - int spacer = boxHeight / 2;
470 - int deltaX = boxWidth + spacer;
471 -
472 - int atX = pAlignOnX;
473 - if ( (mBoxes.length & 1) == 0 ) // even
474 - {
475 - atX -= deltaX / 2;
476 - }
477 - for ( int i = mBoxes.length; i > 2; i -= 2 ) {
478 - atX -= deltaX;
479 - }
480 -
481 - for ( BoxedText box : mBoxes ) {
482 - box.setAtXY( atX, atY );
483 - atX += deltaX;
484 - }
485 -
486 - return atY - (boxHeight / 2);
487 - }
488 - }
489 - }
1 + // This Source Code is in the Public Domain per: http://unlicense.org
2 + package org.litesoft.aokeyhole.toolkit;
3 +
4 + import java.awt.*;
5 +
6 + public class ObjectAndAttributesWithOneDegreeSeperationGraphicView
7 + implements ObjectAndAttributesWithOneDegreeSeperationView,
8 + ScaledDrawableBoxedTextClickable,
9 + View {
10 + public static final Color COLOR_BG_OBJECT = new Color( 255, 255, 196 ); // LIGHT_YELLOW
11 + public static final Color COLOR_BG_ATTRIB_SIMPLE = new Color( 196, 255, 196 ); // MID_GREEN
12 + public static final Color COLOR_BG_ATTRIB_VIRTUAL = new Color( 220, 255, 220 ); // LIGHT_GREEN
13 +
14 + private BoxedText mPrimeObject;
15 +
16 + private ConnectionSet mConnections = new ConnectionSet();
17 + private AllBoxedSet mAll = new AllBoxedSet();
18 + private MainBoxedSet mPrimePlusAttributess;
19 + private RelatedBoxedSet mRelatedObjects;
20 + private MultiBoxedSet mLowerObjects;
21 + private BoxedText mUpperObject = null;
22 +
23 + private View mView = ViewNull.INSTANCE;
24 +
25 + public ObjectAndAttributesWithOneDegreeSeperationGraphicView() {
26 + mPrimeObject = mAll.add( new BoxedText( TYPE_OBJECT_MAIN, COLOR_BG_OBJECT, "" ) );
27 +
28 + mPrimePlusAttributess = new MainBoxedSet( AttributeColorSource.INSTANCE, mPrimeObject );
29 + mPrimePlusAttributess.setView( this ); // Note Leakage of 'this'
30 +
31 + mRelatedObjects = new RelatedBoxedSet( ObjectColorSource.INSTANCE );
32 + mRelatedObjects.setView( this ); // Note Leakage of 'this'
33 +
34 + mLowerObjects = new MultiBoxedSet( ObjectColorSource.INSTANCE );
35 + mLowerObjects.setView( this ); // Note Leakage of 'this'
36 + }
37 +
38 + public void setView( View pView ) {
39 + mView = (pView != null) ? pView : ViewNull.INSTANCE;
40 + }
41 +
42 + @Override
43 + public void update() {
44 + mView.update();
45 + }
46 +
47 + @Override
48 + public void draw( Graphics pGraphics, Dimension pSize ) {
49 + mAll.finish( pGraphics );
50 +
51 + mPrimePlusAttributess.makeSameWidths();
52 + mRelatedObjects.makeSameWidths();
53 + mLowerObjects.makeSameWidths();
54 +
55 + int width = pSize.width;
56 + int height = pSize.height;
57 +
58 + int midX = width / 2;
59 +
60 + height = mLowerObjects.layout( height, midX );
61 +
62 + mPrimePlusAttributess.layout( height, midX, mUpperObject );
63 +
64 + int left = midX / 4;
65 + int right = width - left;
66 +
67 + mRelatedObjects.layout( height, left, right );
68 +
69 + mConnections.draw( pGraphics );
70 +
71 + drawConnectionToUpperObject( pGraphics );
72 + drawConnectionsToLowerObjects( pGraphics );
73 +
74 + mAll.draw( pGraphics );
75 + }
76 +
77 + private void drawConnectionToUpperObject( Graphics pGraphics ) {
78 + if ( mUpperObject != null ) {
79 + mPrimeObject.drawConnectionCenterToCenter( pGraphics, mUpperObject );
80 + }
81 + }
82 +
83 + private void drawConnectionsToLowerObjects( Graphics pGraphics ) {
84 + int size = mLowerObjects.size();
85 + if ( size > 0 ) {
86 + BoxedText lastBox = mPrimePlusAttributess.getLastBox();
87 + if ( lastBox != null ) {
88 + BoxedText[] children = mLowerObjects.getBoxes();
89 + for ( BoxedText child : children ) {
90 + lastBox.drawConnectionCenterToCenter( pGraphics, child );
91 + }
92 + }
93 + }
94 + }
95 +
96 + @Override
97 + public void dispose() {
98 + }
99 +
100 + @Override
101 + public void removeAttribute( int pID ) {
102 + if ( mPrimePlusAttributess.remove( pID ) ) {
103 + mAll.remove( pID );
104 + BoxedText related = mConnections.remove( pID );
105 + if ( related != null ) {
106 + mAll.remove( related.getID() );
107 + mRelatedObjects.remove( related.getID() );
108 + }
109 + }
110 + }
111 +
112 + @Override
113 + public void addNewAttributeOption() {
114 + mAll.add( mPrimePlusAttributess.addNewAttrib() );
115 + }
116 +
117 + @Override
118 + public int addSimpleAttribute( String pAttributeName, boolean pIsVirtual ) {
119 + return mAll.add( mPrimePlusAttributess.add( pAttributeName, pIsVirtual ) ).getID();
120 + }
121 +
122 + @Override
123 + public int addRelatedAttributeToSelf( String pAttributeName, boolean pIsVirtual ) {
124 + BoxedText attrib = mAll.add( mPrimePlusAttributess.add( pAttributeName, pIsVirtual ) );
125 +
126 + mConnections.add( attrib, null );
127 +
128 + return attrib.getID();
129 + }
130 +
131 + @Override
132 + public int addRelatedAttribute( String pAttributeName, boolean pIsVirtual, String pObjectName ) {
133 + BoxedText attrib = mAll.add( mPrimePlusAttributess.add( pAttributeName, pIsVirtual ) );
134 + BoxedText object = mAll.add( mRelatedObjects.add( pObjectName, null ) );
135 +
136 + mConnections.add( attrib, object );
137 +
138 + return attrib.getID();
139 + }
140 +
141 + @Override
142 + public void changeObjectName( String pNewName ) {
143 + mPrimeObject.setName( pNewName );
144 + }
145 +
146 + @Override
147 + public boolean changeAttributeName( int pID, String pNewName ) {
148 + return mPrimePlusAttributess.changeName( pID, pNewName );
149 + }
150 +
151 + @Override
152 + public void setParentName( String pNewName ) {
153 + if ( mUpperObject != null ) {
154 + if ( pNewName != null ) {
155 + mUpperObject.setName( pNewName );
156 + return;
157 + }
158 + int id = mUpperObject.getID();
159 + mUpperObject = null;
160 + mAll.remove( id );
161 + return;
162 + }
163 + if ( pNewName != null ) {
164 + mUpperObject = new BoxedText( TYPE_OBJECT_RELATED, COLOR_BG_OBJECT, pNewName );
165 + mAll.add( mUpperObject );
166 + mUpperObject.setControllingSet( mAll );
167 + }
168 + }
169 +
170 + /**
171 + * Note this is not really a dynamic field, so teardown and rebuild is acceptable
172 + */
173 + @Override
174 + public void setGeneratedRelationsByParentNames( String[] pNames ) {
175 + int size = mLowerObjects.size();
176 + if ( size != 0 ) {
177 + int[] ids = new int[size];
178 + BoxedText[] boxes = mLowerObjects.getBoxes();
179 + for ( int i = 0; i < boxes.length; i++ ) {
180 + ids[i] = boxes[i].getID();
181 + }
182 + for ( int id : ids ) {
183 + mLowerObjects.remove( id );
184 + mAll.remove( id );
185 + }
186 + }
187 + for ( String zName : pNames ) {
188 + mAll.add( mLowerObjects.add( zName, null ) );
189 + }
190 + }
191 +
192 + @Override
193 + public BoxedText getClickedOn( Point pAt ) {
194 + return mAll.getClickedOn( pAt );
195 + }
196 +
197 + // Supporting Classes... ==========================================================================================
198 +
199 + private static class ConnectionSet {
200 + private Connection[] mConnections = Connection.EMPTY_ARRAY;
201 +
202 + public void draw( Graphics pGraphics ) {
203 + for ( Connection connection : mConnections ) {
204 + connection.draw( pGraphics );
205 + }
206 + }
207 +
208 + public void add( BoxedText pFrom, BoxedText pTo ) {
209 + mConnections = append( mConnections, new Connection( pFrom, pTo ) );
210 + }
211 +
212 + private static Connection[] append( Connection[] pOrig, Connection pNew ) {
213 + if ( pNew == null ) {
214 + return pOrig;
215 + }
216 + Connection[] rv = new Connection[pOrig.length + 1];
217 + rv[0] = pNew;
218 + System.arraycopy( pOrig, 0, rv, 1, pOrig.length );
219 + return rv;
220 + }
221 +
222 + public BoxedText remove( int pID ) {
223 + for ( int i = 0; i < mConnections.length; i++ ) {
224 + Connection connection = mConnections[i];
225 + if ( connection.mFrom.getID() == pID ) {
226 + innerRemove( i );
227 + return connection.mTo;
228 + }
229 + if ( (connection.mTo != null) && (connection.mTo.getID() == pID) ) {
230 + innerRemove( i );
231 + return connection.mFrom;
232 + }
233 + }
234 + return null;
235 + }
236 +
237 + private void innerRemove( int pIndex ) {
238 + Connection[] shrunk = new Connection[mConnections.length - 1];
239 + if ( pIndex != 0 ) // Not First
240 + {
241 + System.arraycopy( mConnections, 0, shrunk, 0, pIndex );
242 + }
243 + if ( pIndex != mConnections.length - 1 ) // Not Last
244 + {
245 + System.arraycopy( mConnections, pIndex + 1, shrunk, pIndex, shrunk.length - pIndex );
246 + }
247 + mConnections = shrunk;
248 + }
249 +
250 + private static class Connection {
251 + public static final Connection[] EMPTY_ARRAY = new Connection[0];
252 +
253 + private BoxedText mFrom, mTo;
254 +
255 + public Connection( BoxedText pFrom, BoxedText pTo ) {
256 + mFrom = pFrom;
257 + mTo = pTo;
258 + }
259 +
260 + public void draw( Graphics pGraphics ) {
261 + mFrom.drawConnection( pGraphics, mTo );
262 + }
263 + }
264 + }
265 +
266 + private static class ObjectColorSource implements ColorSource {
267 + public static final ColorSource INSTANCE = new ObjectColorSource();
268 +
269 + @Override
270 + public Color getColor( Object pParameter ) {
271 + return COLOR_BG_OBJECT;
272 + }
273 + }
274 +
275 + private static class AttributeColorSource implements ColorSource {
276 + public static final ColorSource INSTANCE = new AttributeColorSource();
277 +
278 + @Override
279 + public Color getColor( Object pParameter ) {
280 + return Boolean.TRUE.equals( pParameter ) ? COLOR_BG_ATTRIB_VIRTUAL : COLOR_BG_ATTRIB_SIMPLE;
281 + }
282 + }
283 +
284 + private static class AllBoxedSet extends BoxedSet {
285 + private boolean mUnfinished = false;
286 +
287 + public void finish( Graphics pGraphics ) {
288 + if ( mUnfinished ) {
289 + for ( BoxedText box : mBoxes ) {
290 + box.finish( pGraphics );
291 + }
292 + }
293 + }
294 +
295 + @Override
296 + public void setChanged() {
297 + mUnfinished = true;
298 + super.setChanged();
299 + }
300 +
301 + public BoxedText getClickedOn( Point pAt ) {
302 + for ( int i = mBoxes.length; --i >= 0; ) // Note reverse order due to Overlaying
303 + {
304 + BoxedText box = mBoxes[i];
305 + if ( box.isClickedOn( pAt ) ) {
306 + return box;
307 + }
308 + }
309 + return null;
310 + }
311 + }
312 +
313 + private abstract static class ColorSourcedBoxedSet extends BoxedSet {
314 + private int mType;
315 + private ColorSource mColorSource;
316 +
317 + public ColorSourcedBoxedSet( int pType, ColorSource pColorSource ) {
318 + mType = pType;
319 + mColorSource = pColorSource;
320 + }
321 +
322 + public BoxedText add( String pName, Object pParameter ) {
323 + return add( new BoxedText( mType, mColorSource.getColor( pParameter ), pName ) );
324 + }
325 + }
326 +
327 + private static class SameWidthBoxedSet extends ColorSourcedBoxedSet {
328 + private boolean mWidthsProbablyNotSame = false;
329 +
330 + public SameWidthBoxedSet( int pType, ColorSource pColorSource ) {
331 + super( pType, pColorSource );
332 + }
333 +
334 + public void makeSameWidths() {
335 + if ( mWidthsProbablyNotSame ) {
336 + mWidthsProbablyNotSame = false;
337 + int maxBoxWidth = 0;
338 + for ( BoxedText box : mBoxes ) {
339 + maxBoxWidth = Math.max( maxBoxWidth, box.getBoxWidth() );
340 + }
341 + for ( BoxedText box : mBoxes ) {
342 + box.setBoxWidth( maxBoxWidth );
343 + }
344 + }
345 + }
346 +
347 + @Override
348 + public BoxedText add( BoxedText pNew ) {
349 + BoxedText boxedText = super.add( pNew );
350 + boxedText.setControllingSet( this );
351 + return boxedText;
352 + }
353 +
354 + @Override
355 + public void setChanged() {
356 + mWidthsProbablyNotSame = (mBoxes.length > 1);
357 + super.setChanged();
358 + }
359 + }
360 +
361 + private static class MainBoxedSet extends SameWidthBoxedSet {
362 + private BoxedText mNewAttrib = null;
363 +
364 + public MainBoxedSet( ColorSource pColorSource, BoxedText pPrime ) {
365 + super( TYPE_ATTRIB_EXISTING, pColorSource );
366 +
367 + add( pPrime );
368 + }
369 +
370 + @Override
371 + public void dispose() {
372 + mNewAttrib = null;
373 + super.dispose();
374 + }
375 +
376 + public BoxedText addNewAttrib() {
377 + if ( mNewAttrib != null ) {
378 + return null;
379 + }
380 + mNewAttrib = add( new BoxedText( TYPE_ATTRIB_NEW, COLOR_BG_ATTRIB_VIRTUAL, "+" ) );
381 + return mNewAttrib;
382 + }
383 +
384 + @Override
385 + protected void innerAdd( BoxedText pNew ) {
386 + super.innerAdd( pNew ); // Add to end
387 + if ( mNewAttrib != null ) {
388 + // Swap Ends so that the end one is "New Attribute" option
389 + BoxedText last = mBoxes[mBoxes.length - 1];
390 + mBoxes[mBoxes.length - 1] = mBoxes[mBoxes.length - 2];
391 + mBoxes[mBoxes.length - 2] = last;
392 + }
393 + }
394 +
395 + public void layout( int pHeight, int pAlignOnX, BoxedText pUpperObject ) {
396 + int boxHeight = mBoxes[0].getBoxHeight();
397 + int baseTop = 0;
398 +
399 + if ( pUpperObject != null ) {
400 + pUpperObject.setAtXY( pAlignOnX, boxHeight );
401 + baseTop = boxHeight + (boxHeight / 2);
402 + }
403 +
404 + int atY, deltaY;
405 +
406 + int overallAvail = pHeight - (baseTop + boxHeight); // remove baseTop + spacing
407 + int overallOptimalSize = size() * boxHeight;
408 +
409 + if ( overallAvail < overallOptimalSize ) {
410 + atY = baseTop + boxHeight; // spacer + centering
411 + deltaY = overallAvail / size();
412 + } else {
413 + atY = baseTop + ((overallAvail - overallOptimalSize) / 3) + boxHeight; // spacer + centering
414 + deltaY = boxHeight;
415 + }
416 +
417 + for ( BoxedText box : mBoxes ) {
418 + box.setAtXY( pAlignOnX, atY );
419 + atY += deltaY;
420 + }
421 + }
422 +
423 + public boolean changeName( int pID, String pNewName ) {
424 + for ( BoxedText box : mBoxes ) {
425 + if ( box.getID() == pID ) {
426 + box.setName( pNewName );
427 + return true;
428 + }
429 + }
430 + return false;
431 + }
432 + }
433 +
434 + private static class RelatedBoxedSet extends SameWidthBoxedSet {
435 + public RelatedBoxedSet( ColorSource pColorSource ) {
436 + super( TYPE_OBJECT_RELATED, pColorSource );
437 + }
438 +
439 + public void layout( int pHeight, int pAlignOnLeftX, int pAlignOnRightX ) {
440 + int half = (size() + 1) / 2;
441 +
442 + setBoxes( 0, half, pAlignOnLeftX, pHeight );
443 + setBoxes( 1, size() - half, pAlignOnRightX, pHeight );
444 + }
445 +
446 + private void setBoxes( int pFrom, int pCount, int pAtX, int pYspace ) {
447 + if ( pCount > 0 ) {
448 + int deltaY = pYspace / (pCount + 1);
449 + for ( int atY = 0; pFrom < mBoxes.length; pFrom += 2 ) {
450 + mBoxes[pFrom].setAtXY( pAtX, atY += deltaY );
451 + }
452 + }
453 + }
454 + }
455 +
456 + private static class MultiBoxedSet extends SameWidthBoxedSet {
457 + public MultiBoxedSet( ColorSource pColorSource ) {
458 + super( TYPE_OBJECT_RELATED, pColorSource );
459 + }
460 +
461 + public int layout( int pHeight, int pAlignOnX ) {
462 + if ( mBoxes.length == 0 ) {
463 + return pHeight;
464 + }
465 + int boxWidth = mBoxes[0].getBoxWidth();
466 + int boxHeight = mBoxes[0].getBoxHeight();
467 + int atY = pHeight - boxHeight;
468 +
469 + int spacer = boxHeight / 2;
470 + int deltaX = boxWidth + spacer;
471 +
472 + int atX = pAlignOnX;
473 + if ( (mBoxes.length & 1) == 0 ) // even
474 + {
475 + atX -= deltaX / 2;
476 + }
477 + for ( int i = mBoxes.length; i > 2; i -= 2 ) {
478 + atX -= deltaX;
479 + }
480 +
481 + for ( BoxedText box : mBoxes ) {
482 + box.setAtXY( atX, atY );
483 + atX += deltaX;
484 + }
485 +
486 + return atY - (boxHeight / 2);
487 + }
488 + }
489 + }