/[gnustep]/gnustep/dev-apps/Gorm/GormDocument.m
ViewVC logotype

Contents of /gnustep/dev-apps/Gorm/GormDocument.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.156 - (show annotations) (download)
Sun Sep 28 23:31:35 2003 UTC (20 years, 6 months ago) by gcasa
Branch: MAIN
CVS Tags: Gorm-0_4_0
Changes since 1.155: +35 -2 lines
Corrected Report #5205.   Menus for Windows/Services should now be designated by Gorm and correctly shown when the .gorm file loads.

1 /* GormDocument.m
2 *
3 * Copyright (C) 1999 Free Software Foundation, Inc.
4 *
5 * Author: Richard Frith-Macdonald <richard@brainstrom.co.uk>
6 * Date: 1999
7 * Author: Gregory John Casamento <greg_casamento@yahoo.com>
8 * Date: 2002
9 *
10 * This file is part of GNUstep.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27 #include "GormPrivate.h"
28 #include "GormClassManager.h"
29 #include "GormCustomView.h"
30 #include "GormOutlineView.h"
31 #include <AppKit/NSImage.h>
32 #include <AppKit/NSSound.h>
33 #include <Foundation/NSUserDefaults.h>
34 #include <AppKit/NSNibConnector.h>
35
36 @interface GormDisplayCell : NSButtonCell
37 @end
38 @implementation GormDisplayCell
39 - (void) setShowsFirstResponder: (BOOL)flag
40 {
41 [super setShowsFirstResponder: NO]; // Never show ugly frame round button
42 }
43 @end
44
45 // Internal only
46 NSString *GSCustomClassMap = @"GSCustomClassMap";
47
48 @interface GormDocument (GModel)
49 - (id) openGModel: (NSString *)path;
50 @end
51
52 @implementation GormFirstResponder
53 - (NSImage*) imageForViewer
54 {
55 static NSImage *image = nil;
56
57 if (image == nil)
58 {
59 NSBundle *bundle = [NSBundle mainBundle];
60 NSString *path = [bundle pathForImageResource: @"GormFirstResponder"];
61
62 image = [[NSImage alloc] initWithContentsOfFile: path];
63 }
64 return image;
65 }
66 - (NSString*) inspectorClassName
67 {
68 return @"GormNotApplicableInspector";
69 }
70 - (NSString*) connectInspectorClassName
71 {
72 return @"GormConnectionInspector";
73 }
74 - (NSString*) sizeInspectorClassName
75 {
76 return @"GormNotApplicableInspector";
77 }
78 - (NSString*) classInspectorClassName
79 {
80 return @"GormNotApplicableInspector";
81 }
82 @end
83
84 @implementation GormFontManager
85 - (NSImage*) imageForViewer
86 {
87 static NSImage *image = nil;
88
89 if (image == nil)
90 {
91 NSBundle *bundle = [NSBundle mainBundle];
92 NSString *path = [bundle pathForImageResource: @"GormFontManager"];
93
94 image = [[NSImage alloc] initWithContentsOfFile: path];
95 }
96 return image;
97 }
98 - (NSString*) inspectorClassName
99 {
100 return @"GormNotApplicableInspector";
101 }
102 - (NSString*) connectInspectorClassName
103 {
104 return @"GormConnectionInspector";
105 }
106 - (NSString*) sizeInspectorClassName
107 {
108 return @"GormNotApplicableInspector";
109 }
110 - (NSString*) classInspectorClassName
111 {
112 return @"GormNotApplicableInspector";
113 }
114 @end
115
116
117
118 /*
119 * Trivial classes for connections from objects to their editors, and from
120 * child editors to their parents. This does nothing special, but we can
121 * use the fact that it's a different class to search for it in the connections
122 * array.
123 */
124 @interface GormObjectToEditor : NSNibConnector
125 @end
126
127 @implementation GormObjectToEditor
128 @end
129
130 @interface GormEditorToParent : NSNibConnector
131 @end
132
133 @implementation GormEditorToParent
134 @end
135
136 @implementation GormDocument
137
138 static NSImage *objectsImage = nil;
139 static NSImage *imagesImage = nil;
140 static NSImage *soundsImage = nil;
141 static NSImage *classesImage = nil;
142
143 + (void) initialize
144 {
145 if (self == [GormDocument class])
146 {
147 NSBundle *bundle;
148 NSString *path;
149
150 bundle = [NSBundle mainBundle];
151 path = [bundle pathForImageResource: @"GormObject"];
152 if (path != nil)
153 {
154 objectsImage = [[NSImage alloc] initWithContentsOfFile: path];
155 }
156 path = [bundle pathForImageResource: @"GormImage"];
157 if (path != nil)
158 {
159 imagesImage = [[NSImage alloc] initWithContentsOfFile: path];
160 }
161 path = [bundle pathForImageResource: @"GormSound"];
162 if (path != nil)
163 {
164 soundsImage = [[NSImage alloc] initWithContentsOfFile: path];
165 }
166 path = [bundle pathForImageResource: @"GormClass"];
167 if (path != nil)
168 {
169 classesImage = [[NSImage alloc] initWithContentsOfFile: path];
170 }
171
172 [self setVersion: GNUSTEP_NIB_VERSION];
173 }
174 }
175
176 - (void) awakeWithContext: (NSDictionary *)context
177 {
178 // do nothing.. This is defined to override the one in GSNibContainer.
179 }
180
181 - (void) addConnector: (id<IBConnectors>)aConnector
182 {
183 if ([connections indexOfObjectIdenticalTo: aConnector] == NSNotFound)
184 {
185 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
186 [nc postNotificationName: IBWillAddConnectorNotification
187 object: aConnector];
188 [connections addObject: aConnector];
189 [nc postNotificationName: IBDidAddConnectorNotification
190 object: aConnector];
191 }
192 }
193
194 - (NSArray*) allConnectors
195 {
196 return [NSArray arrayWithArray: connections];
197 }
198
199
200 - (void) attachObject: (id)anObject toParent: (id)aParent
201 {
202 NSArray *old;
203
204 /*
205 * Create a connector that links this object to its parent.
206 * A nil parent is the root of the hierarchy so we use a dummy object for it.
207 */
208 if (aParent == nil)
209 {
210 aParent = filesOwner;
211 }
212 old = [self connectorsForSource: anObject ofClass: [NSNibConnector class]];
213 if ([old count] > 0)
214 {
215 [[old objectAtIndex: 0] setDestination: aParent];
216 }
217 else
218 {
219 NSNibConnector *con = [NSNibConnector new];
220
221 [con setSource: anObject];
222 [con setDestination: aParent];
223 [self addConnector: (id<IBConnectors>)con];
224 RELEASE(con);
225 }
226 /*
227 * Make sure that there is a name for this object.
228 */
229 if ([self nameForObject: anObject] == nil)
230 {
231 [self setName: nil forObject: anObject];
232 }
233
234 /*
235 * Add top-level objects to objectsView and open their editors.
236 */
237 if ([anObject isKindOfClass: [NSWindow class]] == YES
238 // || [anObject isKindOfClass: [NSMenu class]] == YES
239 || [anObject isKindOfClass: [GSNibItem class]] == YES)
240 {
241 [objectsView addObject: anObject];
242 [[self openEditorForObject: anObject] activate];
243 if ([anObject isKindOfClass: [NSWindow class]] == YES)
244 {
245 // RETAIN(anObject);
246 [anObject setReleasedWhenClosed: NO];
247 }
248 }
249
250 if ([anObject isKindOfClass: [NSMenu class]] == YES)
251 {
252 [[self openEditorForObject: anObject] activate];
253 }
254
255 /*
256 * if this a scrollview, it is interesting to add its contentview
257 * if it is a tableview or a textview
258 */
259 if (([anObject isKindOfClass: [NSScrollView class]] == YES)
260 && ([(NSScrollView *)anObject documentView] != nil))
261 {
262 if ([[anObject documentView] isKindOfClass:
263 [NSTableView class]] == YES)
264 {
265 int i;
266 int count;
267 NSArray *tc;
268 id tv = [anObject documentView];
269 tc = [tv tableColumns];
270 count = [tc count];
271 [self attachObject: tv toParent: aParent];
272
273 for (i = 0; i < count; i++)
274 {
275 [self attachObject: [tc objectAtIndex: i]
276 toParent: aParent];
277 }
278 }
279 else if ([[anObject documentView] isKindOfClass:
280 [NSTextView class]] == YES)
281 {
282 [self attachObject: [anObject documentView] toParent: aParent];
283 }
284 }
285 }
286
287 - (void) attachObjects: (NSArray*)anArray toParent: (id)aParent
288 {
289 NSEnumerator *enumerator = [anArray objectEnumerator];
290 NSObject *obj;
291
292 while ((obj = [enumerator nextObject]) != nil)
293 {
294 [self attachObject: obj toParent: aParent];
295 }
296 }
297
298 // sound support
299 - (GormSound *)_createSoundPlaceHolder: (NSString *)path
300 {
301 NSString *name = [[path lastPathComponent] stringByDeletingPathExtension];
302 return AUTORELEASE([[GormSound alloc] initWithName: name path: path]);
303 }
304
305 // image support
306 - (GormImage *)_createImagePlaceHolder: (NSString *)path
307 {
308 NSString *name = [[path lastPathComponent] stringByDeletingPathExtension];
309 return TEST_AUTORELEASE([[GormImage alloc] initWithName: name path: path]);
310 }
311
312 - (void) beginArchiving
313 {
314 NSEnumerator *enumerator;
315 id<IBConnectors> con;
316 id obj;
317
318 /*
319 * Map all connector sources and destinations to their name strings.
320 * Deactivate editors so they won't be archived.
321 */
322
323 enumerator = [connections objectEnumerator];
324 while ((con = [enumerator nextObject]) != nil)
325 {
326 if ([con isKindOfClass: [GormObjectToEditor class]] == YES)
327 {
328 [savedEditors addObject: con];
329 [[con destination] deactivate];
330 }
331 else if ([con isKindOfClass: [GormEditorToParent class]] == YES)
332 {
333 [savedEditors addObject: con];
334 }
335 else
336 {
337 NSString *name;
338 obj = [con source];
339 name = [self nameForObject: obj];
340 [con setSource: name];
341 obj = [con destination];
342 name = [self nameForObject: obj];
343 [con setDestination: name];
344 }
345 }
346 [connections removeObjectsInArray: savedEditors];
347
348 NSDebugLog(@"*** customClassMap = %@",[classManager customClassMap]);
349 [nameTable setObject: [classManager customClassMap] forKey: GSCustomClassMap];
350
351 /*
352 * Remove objects and connections that shouldn't be archived.
353 */
354 NSMapRemove(objToName, (void*)[nameTable objectForKey: @"NSOwner"]);
355 [nameTable removeObjectForKey: @"NSOwner"];
356 NSMapRemove(objToName, (void*)[nameTable objectForKey: @"NSFirst"]);
357 [nameTable removeObjectForKey: @"NSFirst"];
358 if (fontManager != nil)
359 {
360 NSMapRemove(objToName, (void*)[nameTable objectForKey: @"NSFont"]);
361 [nameTable removeObjectForKey: @"NSFont"];
362 }
363
364 /* Add information about the NSOwner to the archive */
365 NSMapInsert(objToName, (void*)[filesOwner className], (void*)@"NSOwner");
366 [nameTable setObject: [filesOwner className] forKey: @"NSOwner"];
367 }
368
369 - (void) changeCurrentClass: (id)sender
370 {
371 int row = [classesView selectedRow];
372 if (row >= 0)
373 {
374 [classEditor setSelectedClassName: [classesView itemAtRow: row]];
375 [self setSelectionFromEditor: (id)classEditor];
376 }
377 }
378
379 // class selection...
380 - (void) _selectClass: (NSString *)className
381 {
382 NSString *newClassName;
383 NSString *currentClass = nil;
384 NSArray *classes;
385 NSEnumerator *en;
386 int row = 0;
387
388 if(className != nil)
389 {
390 if([className isEqualToString: @"CustomView"])
391 return; // return only if it is a special class name...
392 }
393 else
394 {
395 return; // return if it is nil
396 }
397
398 newClassName = [GormClassManager correctClassName: className];
399 classes = [[self classManager] allSuperClassesOf: newClassName];
400 en = [classes objectEnumerator];
401
402 // open the items...
403 while ((currentClass = [en nextObject]) != nil)
404 {
405 [classesView expandItem: currentClass];
406 }
407
408 // select the item...
409 row = [classesView rowForItem: newClassName];
410 if (row != NSNotFound)
411 {
412 [classesView selectRow: row byExtendingSelection: NO];
413 [classesView scrollRowToVisible: row];
414 }
415 }
416
417 - (void) selectClassWithObject: (id)obj
418 {
419 NSString *customClass = [classManager customClassForObject: obj];
420
421 if(customClass != nil)
422 {
423 [self _selectClass: customClass];
424 }
425 else if ([obj respondsToSelector: @selector(className)])
426 {
427 [self _selectClass: [obj className]];
428 }
429 }
430
431 // change the views...
432 - (void) changeView: (id)sender
433 {
434 int tag = [[sender selectedCell] tag];
435
436 switch (tag)
437 {
438 case 0: // objects
439 {
440 [selectionBox setContentView: scrollView];
441 }
442 break;
443 case 1: // images
444 {
445 [selectionBox setContentView: imagesScrollView];
446 }
447 break;
448 case 2: // sounds
449 {
450 [selectionBox setContentView: soundsScrollView];
451 }
452 break;
453 case 3: // classes
454 {
455 NSArray *selection = [[(id<IB>)NSApp selectionOwner] selection];
456 [selectionBox setContentView: classesScrollView];
457
458 // if something is selected, in the object view.
459 // show the equivalent class in the classes view.
460 if ([selection count] > 0)
461 {
462 id obj = [selection objectAtIndex: 0];
463 // if it's a scrollview focus on it's contents.
464 if([obj isKindOfClass: [NSScrollView class]])
465 {
466 id newobj = nil;
467 newobj = [obj documentView];
468 if(newobj != nil)
469 {
470 obj = newobj;
471 }
472 }
473 [self selectClassWithObject: obj];
474 }
475 }
476 break;
477 }
478 }
479
480 - (GormClassManager*) classManager
481 {
482 return classManager;
483 }
484
485 /*
486 * A Gorm document is encoded in the archive as a GSNibContainer ...
487 * A class that the gnustep gui library knbows about and can unarchive.
488 */
489 - (Class) classForCoder
490 {
491 return [GSNibContainer class];
492 }
493
494 - (NSArray*) connectorsForDestination: (id)destination
495 {
496 return [self connectorsForDestination: destination ofClass: 0];
497 }
498
499 - (NSArray*) connectorsForDestination: (id)destination
500 ofClass: (Class)aConnectorClass
501 {
502 NSMutableArray *array = [NSMutableArray arrayWithCapacity: 16];
503 NSEnumerator *enumerator = [connections objectEnumerator];
504 id<IBConnectors> c;
505
506 while ((c = [enumerator nextObject]) != nil)
507 {
508 if ([c destination] == destination
509 && (aConnectorClass == 0 || aConnectorClass == [c class]))
510 {
511 [array addObject: c];
512 }
513 }
514 return array;
515 }
516
517 - (NSArray*) connectorsForSource: (id)source
518 {
519 return [self connectorsForSource: source ofClass: 0];
520 }
521
522 - (NSArray*) connectorsForSource: (id)source
523 ofClass: (Class)aConnectorClass
524 {
525 NSMutableArray *array = [NSMutableArray arrayWithCapacity: 16];
526 NSEnumerator *enumerator = [connections objectEnumerator];
527 id<IBConnectors> c;
528
529 while ((c = [enumerator nextObject]) != nil)
530 {
531 if ([c source] == source
532 && (aConnectorClass == 0 || aConnectorClass == [c class]))
533 {
534 [array addObject: c];
535 }
536 }
537 return array;
538 }
539
540 - (BOOL) containsObject: (id)anObject
541 {
542 if ([self nameForObject: anObject] == nil)
543 {
544 return NO;
545 }
546 return YES;
547 }
548
549 - (BOOL) containsObjectWithName: (NSString*)aName forParent: (id)parent
550 {
551 id obj = [nameTable objectForKey: aName];
552
553 if (obj == nil)
554 {
555 return NO;
556 }
557 return YES;
558 }
559
560 - (BOOL) copyObject: (id)anObject
561 type: (NSString*)aType
562 toPasteboard: (NSPasteboard*)aPasteboard
563 {
564 return [self copyObjects: [NSArray arrayWithObject: anObject]
565 type: aType
566 toPasteboard: aPasteboard];
567 }
568
569 - (BOOL) copyObjects: (NSArray*)anArray
570 type: (NSString*)aType
571 toPasteboard: (NSPasteboard*)aPasteboard
572 {
573 NSEnumerator *enumerator;
574 NSMutableSet *editors;
575 id obj;
576 NSData *data;
577
578 /*
579 * Remove all editors from the selected objects before archiving
580 * and restore them afterwards.
581 */
582 editors = [NSMutableSet new];
583 enumerator = [anArray objectEnumerator];
584 while ((obj = [enumerator nextObject]) != nil)
585 {
586 id editor = [self editorForObject: obj create: NO];
587
588 if (editor != nil)
589 {
590 [editors addObject: editor];
591 [editor deactivate];
592 }
593 }
594 data = [NSArchiver archivedDataWithRootObject: anArray];
595 enumerator = [editors objectEnumerator];
596 while ((obj = [enumerator nextObject]) != nil)
597 {
598 [obj activate];
599 }
600 RELEASE(editors);
601
602 [aPasteboard declareTypes: [NSArray arrayWithObject: aType]
603 owner: self];
604 return [aPasteboard setData: data forType: aType];
605 }
606
607 - (id) createSubclass: (id)sender
608 {
609 int i = [classesView selectedRow];
610
611 if (i >= 0 && ![classesView isEditing])
612 {
613 NSString *newClassName;
614 id itemSelected = [classesView itemAtRow: i];
615
616 if(![itemSelected isEqualToString: @"FirstResponder"])
617 {
618 newClassName = [classManager addClassWithSuperClassName:
619 itemSelected];
620 RETAIN(newClassName);
621 [classesView reloadData];
622 [classesView expandItem: itemSelected];
623 i = [classesView rowForItem: newClassName];
624 [classesView selectRow: i byExtendingSelection: NO];
625 [classesView scrollRowToVisible: i];
626 [self editClass: self];
627 }
628 else
629 {
630 // beep to inform the user of this error.
631 NSBeep();
632 }
633 }
634
635 return self;
636 }
637
638 - (void) pasteboardChangedOwner: (NSPasteboard*)sender
639 {
640 NSDebugLog(@"Owner changed for %@", sender);
641 }
642
643 - (void) dealloc
644 {
645 [[NSNotificationCenter defaultCenter] removeObserver: self];
646 [window setDelegate: nil];
647 [window performClose: self];
648 RELEASE(classManager);
649 RELEASE(classEditor);
650 RELEASE(hidden);
651 RELEASE(filesOwner);
652 RELEASE(firstResponder);
653 RELEASE(fontManager);
654 if (objToName != 0)
655 {
656 NSFreeMapTable(objToName);
657 }
658 RELEASE(documentPath);
659 RELEASE(savedEditors);
660 RELEASE(scrollView);
661 RELEASE(classesScrollView);
662 [super dealloc];
663 }
664
665 - (void) detachObject: (id)anObject
666 {
667 NSString *name = RETAIN([self nameForObject: anObject]);
668 GormClassManager *cm = [self classManager];
669 unsigned count;
670
671 [[self editorForObject: anObject create: NO] close];
672
673 count = [connections count];
674 while (count-- > 0)
675 {
676 id<IBConnectors> con = [connections objectAtIndex: count];
677
678 if ([con destination] == anObject || [con source] == anObject)
679 {
680 [connections removeObjectAtIndex: count];
681 }
682 }
683 NSMapRemove(objToName, (void*)anObject);
684 if ([anObject isKindOfClass: [NSWindow class]] == YES
685 || [anObject isKindOfClass: [NSMenu class]] == YES)
686 {
687 [objectsView removeObject: anObject];
688 }
689 /*
690 * Make sure this object isn't in the list of objects to be made visible
691 * on nib loading.
692 */
693 [self setObject: anObject isVisibleAtLaunch: NO];
694
695 // remove from custom class map...
696 NSDebugLog(@"Delete from custom class map -> %@",name);
697 [cm removeCustomClassForObject: name];
698 if([anObject isKindOfClass: [NSScrollView class]] == YES)
699 {
700 NSView *subview = [anObject documentView];
701 NSString *objName = [self nameForObject: subview];
702 NSDebugLog(@"Delete from custom class map -> %@",objName);
703 [cm removeCustomClassForObject: objName];
704 }
705
706 // remove from name table...
707 [nameTable removeObjectForKey: name];
708
709 // free...
710 RELEASE(name);
711 }
712
713 - (void) detachObjects: (NSArray*)anArray
714 {
715 NSEnumerator *enumerator = [anArray objectEnumerator];
716 NSObject *obj;
717
718 while ((obj = [enumerator nextObject]) != nil)
719 {
720 [self detachObject: obj];
721 }
722 }
723
724 - (NSString*) documentPath
725 {
726 return documentPath;
727 }
728
729 - (id) parseHeader: (NSString *)headerPath
730 {
731 NSString *headerFile = [NSString stringWithContentsOfFile: headerPath];
732 NSScanner *headerScanner = [NSScanner scannerWithString: headerFile];
733 GormClassManager *cm = [self classManager];
734 NSCharacterSet *superClassStopSet = [NSCharacterSet characterSetWithCharactersInString: @" \n"];
735 NSCharacterSet *classStopSet = [NSCharacterSet characterSetWithCharactersInString: @" :"];
736 NSCharacterSet *categoryStopSet = [NSCharacterSet characterSetWithCharactersInString: @" ("];
737 NSCharacterSet *typeStopSet = [NSCharacterSet characterSetWithCharactersInString: @" "];
738 NSCharacterSet *actionStopSet = [NSCharacterSet characterSetWithCharactersInString: @";:"];
739 NSCharacterSet *outletStopSet = [NSCharacterSet characterSetWithCharactersInString: @";,"];
740 NSCharacterSet *illegalOutletSet = [NSCharacterSet characterSetWithCharactersInString: @"~`@#$%^&*()+={}|[]\\:;'<>?,./"];
741 NSCharacterSet *illegalActionSet = [NSCharacterSet characterSetWithCharactersInString: @"~`@#$%^&*()+={}|[]\\;'<>?,./"];
742 NSArray *outletTokens = [NSArray arrayWithObjects: @"id", @"IBOutlet", nil];
743 NSArray *actionTokens = [NSArray arrayWithObjects: @"(void)", @"(IBAction)", @"(id)", nil];
744 NSRange notFoundRange = NSMakeRange(NSNotFound,0);
745 // NSCharacterSet *commentStopSet = [NSCharacterSet characterSetWithCharactersInString: @"\n"];
746
747 while (![headerScanner isAtEnd])
748 {
749 NSString *classString = nil;
750 BOOL classfound = NO, result = NO, category = NO;
751 NSEnumerator *outletEnum = [outletTokens objectEnumerator];
752 NSEnumerator *actionEnum = [actionTokens objectEnumerator];
753 NSString *outletToken = nil;
754 NSString *actionToken = nil;
755 int alert;
756
757 classfound = [headerScanner scanUpToString: @"@interface"
758 intoString: NULL];
759
760 [headerScanner scanUpToString: @"@end"
761 intoString: &classString];
762
763 if (classfound && ![headerScanner isAtEnd])
764 {
765 NSString
766 *className = nil,
767 *superClassName = nil,
768 *ivarString = nil,
769 *methodString = nil;
770 NSScanner
771 *classScanner = [NSScanner scannerWithString: classString],
772 *ivarScanner = nil,
773 *methodScanner = nil;
774 NSMutableArray
775 *actions = [NSMutableArray array],
776 *outlets = [NSMutableArray array];
777
778 [classScanner scanString: @"@interface"
779 intoString: NULL];
780 [classScanner scanUpToCharactersFromSet: classStopSet
781 intoString: &className];
782 [classScanner scanString: @":"
783 intoString: NULL];
784 [classScanner scanUpToCharactersFromSet: superClassStopSet
785 intoString: &superClassName];
786 [classScanner scanUpToString: @"{"
787 intoString: NULL];
788 [classScanner scanUpToString: @"}"
789 intoString: &ivarString];
790
791 category = (ivarString == nil);
792 if(!category)
793 {
794 [classScanner scanUpToString: @"@end"
795 intoString: &methodString];
796 NSDebugLog(@"Found a class \"%@\" with super class \"%@\"", className,
797 superClassName);
798 }
799 else
800 {
801 NSDebugLog(@"A CATEGORY");
802 classScanner = [NSScanner scannerWithString: classString];
803 [classScanner scanString: @"@interface"
804 intoString: NULL];
805 [classScanner scanUpToCharactersFromSet: categoryStopSet
806 intoString: &className];
807 [classScanner scanString: @"("
808 intoString: NULL];
809 [classScanner scanUpToCharactersFromSet: superClassStopSet
810 intoString: &superClassName];
811 [classScanner scanString: @")"
812 intoString: NULL];
813 [classScanner scanUpToString: @"@end"
814 intoString: &methodString];
815 NSDebugLog(@"method String %@",methodString);
816 }
817
818
819 // if its' not a category and it's known, ask before proceeding...
820 if([cm isKnownClass: className] && !category)
821 {
822 NSString *message = [NSString stringWithFormat:
823 _(@"The class %@ already exists. Replace it?"),
824 className];
825 alert = NSRunAlertPanel(_(@"Problem adding class from header"),
826 message,
827 _(@"Yes"),
828 _(@"No"),
829 nil);
830 if (alert != NSAlertDefaultReturn)
831 return self;
832 }
833
834 // if it's not a category go through the ivars...
835 if(!category)
836 {
837 NSDebugLog(@"Ivar string is not nil");
838 // Interate over the possible tokens which can make an
839 // ivar an outlet.
840 while ((outletToken = [outletEnum nextObject]) != nil)
841 {
842 NSString *delimiter = nil;
843 NSDebugLog(@"outlet Token = %@", outletToken);
844 // Scan the variables of the class...
845 ivarScanner = [NSScanner scannerWithString: ivarString];
846 while (![ivarScanner isAtEnd])
847 {
848 NSString *outlet = nil;
849 NSString *type = nil;
850
851 if (delimiter == nil || [delimiter isEqualToString: @";"])
852 {
853 [ivarScanner scanUpToString: outletToken
854 intoString: NULL];
855 [ivarScanner scanString: outletToken
856 intoString: NULL];
857 }
858
859 // if using the IBOutlet token in the header, scan in the outlet type
860 // as well.
861 if([outletToken isEqualToString: @"IBOutlet"])
862 {
863 [ivarScanner scanUpToCharactersFromSet: typeStopSet
864 intoString: NULL];
865 [ivarScanner scanCharactersFromSet: typeStopSet
866 intoString: NULL];
867 [ivarScanner scanUpToCharactersFromSet: typeStopSet
868 intoString: &type];
869 NSDebugLog(@"outlet type = %@",type);
870 }
871
872 [ivarScanner scanUpToCharactersFromSet: outletStopSet
873 intoString: &outlet];
874 [ivarScanner scanCharactersFromSet: outletStopSet
875 intoString: &delimiter];
876 if ([ivarScanner isAtEnd] == NO
877 && [outlets indexOfObject: outlet] == NSNotFound)
878 {
879 NSDebugLog(@"outlet = %@", outlet);
880 if(NSEqualRanges([outlet rangeOfCharacterFromSet: illegalOutletSet],notFoundRange))
881 {
882 [outlets addObject: outlet];
883 }
884 }
885 }
886 }
887 }
888
889 while ((actionToken = [actionEnum nextObject]) != nil)
890 {
891 NSDebugLog(@"Action token %@", actionToken);
892 methodScanner = [NSScanner scannerWithString: methodString];
893 while (![methodScanner isAtEnd])
894 {
895 NSString *action = nil;
896 BOOL hasArguments = NO;
897
898 // Scan the method name
899 [methodScanner scanUpToString: actionToken
900 intoString: NULL];
901 [methodScanner scanString: actionToken
902 intoString: NULL];
903 [methodScanner scanUpToCharactersFromSet: actionStopSet
904 intoString: &action];
905
906 // This will return true if the method has args.
907 hasArguments = [methodScanner scanString: @":"
908 intoString: NULL];
909
910 if (hasArguments)
911 {
912 BOOL isAction = NO;
913 NSString *argType = nil;
914
915 // If the argument is (id) then the method can
916 // be considered an action and we add it to the list.
917 isAction = [methodScanner scanString: @"(id)"
918 intoString: &argType];
919
920 if (![methodScanner isAtEnd])
921 {
922 if (isAction)
923 {
924 /* Add the ':' back */
925 action = [action stringByAppendingString: @":"];
926 NSDebugLog(@"action = %@", action);
927 if(NSEqualRanges([action rangeOfCharacterFromSet: illegalActionSet],notFoundRange))
928 {
929 [actions addObject: action];
930 }
931 }
932 else
933 {
934 NSDebugLog(@"Not an action");
935 }
936 }
937 }
938 } // end while
939 } // end while
940
941 if([cm isKnownClass: className] &&
942 [cm isCustomClass: className] && category)
943 {
944 [cm addActions: actions forClassNamed: className];
945 [cm addOutlets: outlets forClassNamed: className];
946 result = YES;
947 }
948 else if(!category)
949 {
950 result = [cm addClassNamed: className
951 withSuperClassNamed: superClassName
952 withActions: actions
953 withOutlets: outlets];
954 }
955
956 if (result)
957 {
958 NSDebugLog(@"Class %@ added", className);
959 [classesView reloadData];
960 }
961 else
962 if (alert == NSAlertDefaultReturn)
963 {
964 [cm removeClassNamed: className];
965 result = [cm addClassNamed: className
966 withSuperClassNamed: superClassName
967 withActions: actions
968 withOutlets: outlets];
969 if (!result)
970 {
971 NSString *message = [NSString stringWithFormat:
972 _(@"Could not replace class %@."), className];
973 NSRunAlertPanel(_(@"Problem adding class from header"),
974 message,
975 nil,
976 nil,
977 nil);
978 NSDebugLog(@"Class %@ failed to add", className);
979 }
980 else
981 {
982 NSDebugLog(@"Class %@ replaced.", className);
983 [classesView reloadData];
984 }
985 }
986
987 if (result)
988 {
989 // go to the class which was just loaded in the classes view...
990 [selectionBox setContentView: classesScrollView];
991 [self _selectClass: className];
992 }
993 } // if we found a class
994 }
995 return self;
996 }
997
998 - (id) addAttributeToClass: (id)sender
999 {
1000 [classesView addAttributeToClass];
1001 return self;
1002 }
1003
1004 - (id) remove: (id)sender
1005 {
1006 id anitem;
1007 int i = [classesView selectedRow];
1008
1009 // if no selection, then return.
1010 if (i == -1)
1011 {
1012 return self;
1013 }
1014
1015 anitem = [classesView itemAtRow: i];
1016 if ([anitem isKindOfClass: [GormOutletActionHolder class]])
1017 {
1018 id itemBeingEdited = [classesView itemBeingEdited];
1019
1020 // if the class being edited is a custom class, then allow the deletion...
1021 if ([classManager isCustomClass: itemBeingEdited])
1022 {
1023 NSString *name = [anitem getName];
1024
1025 if ([classesView editType] == Actions)
1026 {
1027 // if this action is an action on the class, not it's superclass
1028 // allow the deletion...
1029 if ([classManager isAction: name
1030 ofClass: itemBeingEdited])
1031 {
1032 BOOL removed = [self removeConnectionsWithLabel: name
1033 forClassNamed: itemBeingEdited
1034 isAction: YES];
1035 if (removed)
1036 {
1037 [classManager removeAction: name
1038 fromClassNamed: itemBeingEdited];
1039 [classesView removeItemAtRow: i];
1040 }
1041 }
1042 }
1043 else if ([classesView editType] == Outlets)
1044 {
1045 // if this outlet is an outlet on the class, not it's superclass
1046 // allow the deletion...
1047 if ([classManager isOutlet: name
1048 ofClass: itemBeingEdited])
1049 {
1050 BOOL removed = [self removeConnectionsWithLabel: name
1051 forClassNamed: itemBeingEdited
1052 isAction: NO];
1053 if (removed)
1054 {
1055 [classManager removeOutlet: name
1056 fromClassNamed: itemBeingEdited];
1057 [classesView removeItemAtRow: i];
1058 }
1059 }
1060 }
1061 }
1062 }
1063 else
1064 {
1065 NSArray *subclasses = [classManager subClassesOf: anitem];
1066 // if the class has no subclasses, then delete.
1067 if ([subclasses count] == 0)
1068 {
1069 // if the class being edited is a custom class, then allow the deletion...
1070 if ([classManager isCustomClass: anitem])
1071 {
1072 BOOL removed = [self removeConnectionsForClassNamed: anitem];
1073 if (removed)
1074 {
1075 [classManager removeClassNamed: anitem];
1076 [classesView reloadData];
1077 }
1078 }
1079 }
1080 else
1081 {
1082 NSString *message = [NSString stringWithFormat:
1083 _(@"The class %@ has subclasses which must be removed"), anitem];
1084 NSRunAlertPanel(_(@"Problem removing class"),
1085 message,
1086 nil, nil, nil);
1087 }
1088 }
1089 return self;
1090 }
1091
1092 - (id) loadClass: (id)sender
1093 {
1094 NSArray *fileTypes = [NSArray arrayWithObjects: @"h", @"H", nil];
1095 NSOpenPanel *oPanel = [NSOpenPanel openPanel];
1096 int result;
1097
1098 [oPanel setAllowsMultipleSelection: NO];
1099 [oPanel setCanChooseFiles: YES];
1100 [oPanel setCanChooseDirectories: NO];
1101 result = [oPanel runModalForDirectory: nil
1102 file: nil
1103 types: fileTypes];
1104 if (result == NSOKButton)
1105 {
1106 return [self parseHeader: [oPanel filename]];
1107 }
1108
1109 return nil;
1110 }
1111
1112 - (id) editClass: (id)sender
1113 {
1114 [self changeCurrentClass: sender];
1115
1116 return self;
1117 }
1118
1119 - (id) createClassFiles: (id)sender
1120 {
1121 NSSavePanel *sp;
1122 int row = [classesView selectedRow];
1123 id className = [classesView itemAtRow: row];
1124 int result;
1125
1126 if ([className isKindOfClass: [GormOutletActionHolder class]])
1127 {
1128 className = [classesView itemBeingEdited];
1129 }
1130
1131 sp = [NSSavePanel savePanel];
1132 [sp setRequiredFileType: @"m"];
1133 [sp setTitle: _(@"Save source file as...")];
1134 if (documentPath == nil)
1135 {
1136 result = [sp runModalForDirectory: NSHomeDirectory()
1137 file: [className stringByAppendingPathExtension: @"m"]];
1138 }
1139 else
1140 {
1141 result = [sp runModalForDirectory:
1142 [documentPath stringByDeletingLastPathComponent]
1143 file: [className stringByAppendingPathExtension: @"m"]];
1144 }
1145
1146 if (result == NSOKButton)
1147 {
1148 NSString *sourceName = [sp filename];
1149 NSString *headerName;
1150
1151 [sp setRequiredFileType: @"h"];
1152 [sp setTitle: _(@"Save header file as...")];
1153 result = [sp runModalForDirectory:
1154 [sourceName stringByDeletingLastPathComponent]
1155 file:
1156 [[[sourceName lastPathComponent]
1157 stringByDeletingPathExtension]
1158 stringByAppendingString: @".h"]];
1159 if (result == NSOKButton)
1160 {
1161 headerName = [sp filename];
1162 NSDebugLog(@"Saving %@", className);
1163 if (![classManager makeSourceAndHeaderFilesForClass: className
1164 withName: sourceName
1165 and: headerName])
1166 {
1167 NSRunAlertPanel(_(@"Alert"),
1168 _(@"Could not create the class's file"),
1169 nil, nil, nil);
1170 }
1171
1172 return self;
1173 }
1174 }
1175 return nil;
1176 }
1177
1178 - (void) editor: (id<IBEditors,IBSelectionOwners>)anEditor didCloseForObject: (id)anObject
1179 {
1180 NSArray *links;
1181
1182 /*
1183 * If there is a link from this editor to a parent, remove it.
1184 */
1185 links = [self connectorsForSource: anEditor
1186 ofClass: [GormEditorToParent class]];
1187 NSAssert([links count] < 2, NSInternalInconsistencyException);
1188 if ([links count] == 1)
1189 {
1190 [connections removeObjectIdenticalTo: [links objectAtIndex: 0]];
1191 }
1192
1193 /*
1194 * Remove the connection linking the object to this editor
1195 */
1196 links = [self connectorsForSource: anObject
1197 ofClass: [GormObjectToEditor class]];
1198 NSAssert([links count] < 2, NSInternalInconsistencyException);
1199 if ([links count] == 1)
1200 {
1201 [connections removeObjectIdenticalTo: [links objectAtIndex: 0]];
1202 }
1203
1204 /*
1205 * Make sure that this editor is not the selection owner.
1206 */
1207 if ([(id<IB>)NSApp selectionOwner] ==
1208 anEditor)
1209 {
1210 [self resignSelectionForEditor: anEditor];
1211 }
1212 }
1213
1214 - (id<IBEditors>) editorForObject: (id)anObject
1215 create: (BOOL)flag
1216 {
1217 return [self editorForObject: anObject inEditor: nil create: flag];
1218 }
1219
1220 - (id<IBEditors>) editorForObject: (id)anObject
1221 inEditor: (id<IBEditors>)anEditor
1222 create: (BOOL)flag
1223 {
1224 NSArray *links;
1225
1226 /*
1227 * Look up the editor links for the object to see if it already has an
1228 * editor. If it does return it, otherwise create a new editor and a
1229 * link to it if the flag is set.
1230 */
1231 links = [self connectorsForSource: anObject
1232 ofClass: [GormObjectToEditor class]];
1233 if ([links count] == 0 && flag == YES)
1234 {
1235 Class eClass;
1236 id<IBEditors> editor;
1237 id<IBConnectors> link;
1238
1239 eClass = NSClassFromString([anObject editorClassName]);
1240 editor = [[eClass alloc] initWithObject: anObject inDocument: self];
1241 link = [GormObjectToEditor new];
1242 [link setSource: anObject];
1243 [link setDestination: editor];
1244 [connections addObject: link];
1245 RELEASE(link);
1246 if (anEditor == nil)
1247 {
1248 /*
1249 * By default all editors are owned by the top-level editor of
1250 * the document.
1251 */
1252 anEditor = objectsView;
1253 }
1254 if (anEditor != editor)
1255 {
1256 /*
1257 * Link to the parent of the editor.
1258 */
1259 link = [GormEditorToParent new];
1260 [link setSource: editor];
1261 [link setDestination: anEditor];
1262 [connections addObject: link];
1263 RELEASE(link);
1264 }
1265 else
1266 {
1267 NSDebugLog(@"WARNING anEditor = editor");
1268 }
1269 [editor activate];
1270 RELEASE((NSObject *)editor);
1271 return editor;
1272 }
1273 else if ([links count] == 0)
1274 {
1275 return nil;
1276 }
1277 else
1278 {
1279 [[[links lastObject] destination] activate];
1280 return [[links lastObject] destination];
1281 }
1282 }
1283
1284 - (void) endArchiving
1285 {
1286 NSEnumerator *enumerator;
1287 id<IBConnectors> con;
1288 id obj;
1289
1290 /*
1291 * Restore removed objects.
1292 */
1293 [nameTable setObject: filesOwner forKey: @"NSOwner"];
1294 NSMapInsert(objToName, (void*)filesOwner, (void*)@"NSOwner");
1295
1296 [nameTable setObject: firstResponder forKey: @"NSFirst"];
1297 NSMapInsert(objToName, (void*)firstResponder, (void*)@"NSFirst");
1298
1299 if (fontManager != nil)
1300 {
1301 [nameTable setObject: fontManager forKey: @"NSFont"];
1302 NSMapInsert(objToName, (void*)fontManager, (void*)@"NSFont");
1303 }
1304
1305 /*
1306 * Map all connector source and destination names to their objects.
1307 */
1308 enumerator = [connections objectEnumerator];
1309 while ((con = [enumerator nextObject]) != nil)
1310 {
1311 NSString *name;
1312 name = (NSString*)[con source];
1313 obj = [self objectForName: name];
1314 [con setSource: obj];
1315 name = (NSString*)[con destination];
1316 obj = [self objectForName: name];
1317 [con setDestination: obj];
1318 }
1319
1320 /*
1321 * Restore editor links and reactivate the editors.
1322 */
1323 [connections addObjectsFromArray: savedEditors];
1324 enumerator = [savedEditors objectEnumerator];
1325 while ((con = [enumerator nextObject]) != nil)
1326 {
1327 if ([[con source] isKindOfClass: [NSView class]] == NO)
1328 [[con destination] activate];
1329 }
1330 [savedEditors removeAllObjects];
1331 }
1332
1333 - (void) handleNotification: (NSNotification*)aNotification
1334 {
1335 NSString *name = [aNotification name];
1336
1337 if ([name isEqual: NSWindowWillCloseNotification] == YES)
1338 {
1339 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
1340 NSEnumerator *enumerator;
1341 id obj;
1342
1343 [nc postNotificationName: IBWillCloseDocumentNotification
1344 object: self];
1345
1346 enumerator = [nameTable objectEnumerator];
1347 while ((obj = [enumerator nextObject]) != nil)
1348 {
1349 if ([obj isKindOfClass: [NSMenu class]] == YES)
1350 {
1351 if ([[obj window] isVisible] == YES)
1352 {
1353 [hidden addObject: obj];
1354 [obj close];
1355 }
1356 }
1357 else if ([obj isKindOfClass: [NSWindow class]] == YES)
1358 {
1359 if ([obj isVisible] == YES)
1360 {
1361 [hidden addObject: obj];
1362 [obj orderOut: self];
1363 }
1364 }
1365 }
1366
1367 [self setDocumentActive: NO];
1368 }
1369 else if ([name isEqual: NSWindowDidBecomeKeyNotification] == YES)
1370 {
1371 [self setDocumentActive: YES];
1372 }
1373 else if ([name isEqual: NSWindowWillMiniaturizeNotification] == YES)
1374 {
1375 [self setDocumentActive: NO];
1376 }
1377 else if ([name isEqual: NSWindowDidDeminiaturizeNotification] == YES)
1378 {
1379 [self setDocumentActive: YES];
1380 }
1381 else if ([name isEqual: IBWillBeginTestingInterfaceNotification] == YES)
1382 {
1383 if ([window isVisible] == YES)
1384 {
1385 [hidden addObject: window];
1386 [window setExcludedFromWindowsMenu: YES];
1387 [window orderOut: self];
1388 }
1389 if ([(id<IB>)NSApp activeDocument] == self)
1390 {
1391 NSEnumerator *enumerator;
1392 id obj;
1393
1394 enumerator = [nameTable objectEnumerator];
1395 while ((obj = [enumerator nextObject]) != nil)
1396 {
1397 if ([obj isKindOfClass: [NSMenu class]] == YES)
1398 {
1399 if ([[obj window] isVisible] == YES)
1400 {
1401 [hidden addObject: obj];
1402 [obj close];
1403 }
1404 }
1405 else if ([obj isKindOfClass: [NSWindow class]] == YES)
1406 {
1407 if ([obj isVisible] == YES)
1408 {
1409 [hidden addObject: obj];
1410 [obj orderOut: self];
1411 }
1412 }
1413 }
1414 }
1415 }
1416 else if ([name isEqual: IBWillEndTestingInterfaceNotification] == YES)
1417 {
1418 if ([hidden count] > 0)
1419 {
1420 NSEnumerator *enumerator;
1421 id obj;
1422
1423 enumerator = [hidden objectEnumerator];
1424 while ((obj = [enumerator nextObject]) != nil)
1425 {
1426 if ([obj isKindOfClass: [NSMenu class]] == YES)
1427 {
1428 [obj display];
1429 }
1430 else if ([obj isKindOfClass: [NSWindow class]] == YES)
1431 {
1432 [obj orderFront: self];
1433 }
1434 }
1435 [hidden removeAllObjects];
1436 [window setExcludedFromWindowsMenu: NO];
1437 }
1438 }
1439 else if ([name isEqual: IBClassNameChangedNotification] == YES)
1440 {
1441 if ([aNotification object] == classManager) [classesView reloadData];
1442 }
1443 else if ([name isEqual: IBInspectorDidModifyObjectNotification] == YES)
1444 {
1445 if ([aNotification object] == classManager) [classesView reloadData];
1446 }
1447 }
1448
1449 - (id) init
1450 {
1451 self = [super init];
1452 if (self != nil)
1453 {
1454 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
1455 NSRect winrect = NSMakeRect(100,100,342,256);
1456 NSRect selectionRect = {{6, 188}, {234, 64}};
1457 NSRect scrollRect = {{0, 0}, {340, 188}};
1458 NSRect mainRect = {{20, 0}, {320, 188}};
1459 NSImage *image;
1460 GormDisplayCell *cell;
1461 NSTableColumn *tableColumn;
1462 unsigned style;
1463 NSColor *salmonColor =
1464 [NSColor colorWithCalibratedRed: 0.850980
1465 green: 0.737255
1466 blue: 0.576471
1467 alpha: 1.0 ];
1468 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
1469
1470 classManager = [[GormClassManager alloc] init];
1471 classEditor = [[GormClassEditor alloc] initWithDocument: self];
1472 /*
1473 * NB. We must retain the map values (object names) as the nameTable
1474 * may not hold identical name objects, but merely equal strings.
1475 */
1476 objToName = NSCreateMapTableWithZone(NSObjectMapKeyCallBacks,
1477 NSObjectMapValueCallBacks, 128, [self zone]);
1478
1479 // for saving the editors when the gorm file is persisted.
1480 savedEditors = [NSMutableArray new];
1481
1482 // sounds & images
1483 sounds = [NSMutableSet new];
1484 images = [NSMutableSet new];
1485
1486 style = NSTitledWindowMask | NSClosableWindowMask
1487 | NSResizableWindowMask | NSMiniaturizableWindowMask;
1488 window = [[NSWindow alloc] initWithContentRect: winrect
1489 styleMask: style
1490 backing: NSBackingStoreRetained
1491 defer: NO];
1492 [window setMinSize: [window frame].size];
1493 [window setTitle: _(@"UNTITLED")];
1494
1495 [window setDelegate: self];
1496
1497 [nc addObserver: self
1498 selector: @selector(handleNotification:)
1499 name: NSWindowWillCloseNotification
1500 object: window];
1501 [nc addObserver: self
1502 selector: @selector(handleNotification:)
1503 name: NSWindowDidBecomeKeyNotification
1504 object: window];
1505 [nc addObserver: self
1506 selector: @selector(handleNotification:)
1507 name: NSWindowWillMiniaturizeNotification
1508 object: window];
1509 [nc addObserver: self
1510 selector: @selector(handleNotification:)
1511 name: NSWindowDidDeminiaturizeNotification
1512 object: window];
1513 [nc addObserver: self
1514 selector: @selector(handleNotification:)
1515 name: IBClassNameChangedNotification
1516 object: nil];
1517 [nc addObserver: self
1518 selector: @selector(handleNotification:)
1519 name: IBInspectorDidModifyObjectNotification
1520 object: nil];
1521 /*
1522 [nc addObserver: self
1523 selector: @selector(handleNotification:)
1524 name: GormDidModifyClassNotification
1525 object: nil];
1526 */
1527 selectionView = [[NSMatrix alloc] initWithFrame: selectionRect
1528 mode: NSRadioModeMatrix
1529 cellClass: [GormDisplayCell class]
1530 numberOfRows: 1
1531 numberOfColumns: 4];
1532 [selectionView setTarget: self];
1533 [selectionView setAction: @selector(changeView:)];
1534 [selectionView setAutosizesCells: NO];
1535 [selectionView setCellSize: NSMakeSize(64,64)];
1536 [selectionView setIntercellSpacing: NSMakeSize(24,0)];
1537 [selectionView setAutoresizingMask: NSViewMinYMargin|NSViewWidthSizable];
1538
1539 if ((image = objectsImage) != nil)
1540 {
1541 cell = [selectionView cellAtRow: 0 column: 0];
1542 [cell setTag: 0];
1543 [cell setImage: image];
1544 [cell setTitle: _(@"Objects")];
1545 [cell setBordered: NO];
1546 [cell setAlignment: NSCenterTextAlignment];
1547 [cell setImagePosition: NSImageAbove];
1548 [cell setButtonType: NSOnOffButton];
1549 }
1550
1551 if ((image = imagesImage) != nil)
1552 {
1553 cell = [selectionView cellAtRow: 0 column: 1];
1554 [cell setTag: 1];
1555 [cell setImage: image];
1556 [cell setTitle: _(@"Images")];
1557 [cell setBordered: NO];
1558 [cell setAlignment: NSCenterTextAlignment];
1559 [cell setImagePosition: NSImageAbove];
1560 [cell setButtonType: NSOnOffButton];
1561 }
1562
1563 if ((image = soundsImage) != nil)
1564 {
1565 cell = [selectionView cellAtRow: 0 column: 2];
1566 [cell setTag: 2];
1567 [cell setImage: image];
1568 [cell setTitle: _(@"Sounds")];
1569 [cell setBordered: NO];
1570 [cell setAlignment: NSCenterTextAlignment];
1571 [cell setImagePosition: NSImageAbove];
1572 [cell setButtonType: NSOnOffButton];
1573 }
1574
1575 if ((image = classesImage) != nil)
1576 {
1577 cell = [selectionView cellAtRow: 0 column: 3];
1578 [cell setTag: 3];
1579 [cell setImage: image];
1580 [cell setTitle: _(@"Classes")];
1581 [cell setBordered: NO];
1582 [cell setAlignment: NSCenterTextAlignment];
1583 [cell setImagePosition: NSImageAbove];
1584 [cell setButtonType: NSOnOffButton];
1585 }
1586
1587 [[window contentView] addSubview: selectionView];
1588 RELEASE(selectionView);
1589
1590 selectionBox = [[NSBox alloc] initWithFrame: scrollRect];
1591 [selectionBox setTitlePosition: NSNoTitle];
1592 [selectionBox setBorderType: NSNoBorder];
1593 [selectionBox setAutoresizingMask:
1594 NSViewHeightSizable|NSViewWidthSizable];
1595 [[window contentView] addSubview: selectionBox];
1596 RELEASE(selectionBox);
1597
1598 // objects...
1599 mainRect.origin = NSMakePoint(0,0);
1600 scrollView = [[NSScrollView alloc] initWithFrame: scrollRect];
1601 [scrollView setHasVerticalScroller: YES];
1602 [scrollView setHasHorizontalScroller: NO];
1603 [scrollView setAutoresizingMask:
1604 NSViewHeightSizable|NSViewWidthSizable];
1605 [scrollView setBorderType: NSBezelBorder];
1606
1607 objectsView = [[GormObjectEditor alloc] initWithObject: nil
1608 inDocument: self];
1609 AUTORELEASE(objectsView);
1610 [objectsView setFrame: mainRect];
1611 [objectsView setAutoresizingMask:
1612 NSViewHeightSizable|NSViewWidthSizable];
1613 [scrollView setDocumentView: objectsView];
1614 RELEASE(objectsView);
1615
1616 // images...
1617 mainRect.origin = NSMakePoint(0,0);
1618 imagesScrollView = [[NSScrollView alloc] initWithFrame: scrollRect];
1619 [imagesScrollView setHasVerticalScroller: YES];
1620 [imagesScrollView setHasHorizontalScroller: NO];
1621 [imagesScrollView setAutoresizingMask:
1622 NSViewHeightSizable|NSViewWidthSizable];
1623 [imagesScrollView setBorderType: NSBezelBorder];
1624
1625 imagesView = [[GormImageEditor alloc] initWithObject: nil
1626 inDocument: self];
1627 AUTORELEASE(imagesView);
1628 [imagesView setFrame: mainRect];
1629 [imagesView setAutoresizingMask: NSViewHeightSizable|NSViewWidthSizable];
1630 [imagesScrollView setDocumentView: imagesView];
1631 RELEASE(imagesView);
1632
1633 // sounds...
1634 mainRect.origin = NSMakePoint(0,0);
1635 soundsScrollView = [[NSScrollView alloc] initWithFrame: scrollRect];
1636 [soundsScrollView setHasVerticalScroller: YES];
1637 [soundsScrollView setHasHorizontalScroller: NO];
1638 [soundsScrollView setAutoresizingMask:
1639 NSViewHeightSizable|NSViewWidthSizable];
1640 [soundsScrollView setBorderType: NSBezelBorder];
1641
1642 soundsView = [[GormSoundEditor alloc] initWithObject: nil
1643 inDocument: self];
1644 AUTORELEASE(soundsView);
1645 [soundsView setFrame: mainRect];
1646 [soundsView setAutoresizingMask: NSViewHeightSizable|NSViewWidthSizable];
1647 [soundsScrollView setDocumentView: soundsView];
1648 RELEASE(soundsView);
1649
1650 // classes...
1651 classesScrollView = [[NSScrollView alloc] initWithFrame: scrollRect];
1652 [classesScrollView setHasVerticalScroller: YES];
1653 [classesScrollView setHasHorizontalScroller: NO];
1654 [classesScrollView setAutoresizingMask:
1655 NSViewHeightSizable|NSViewWidthSizable];
1656 [classesScrollView setBorderType: NSBezelBorder];
1657
1658 mainRect.origin = NSMakePoint(0,0);
1659 classesView = [[GormOutlineView alloc] initWithFrame: mainRect];
1660 [classesView setMenu: [(Gorm*)NSApp classMenu]];
1661 [classesView setDataSource: self];
1662 [classesView setDelegate: self];
1663 [classesView setAutoresizesAllColumnsToFit: YES];
1664 [classesView setAllowsColumnResizing: NO];
1665 [classesView setDrawsGrid: NO];
1666 [classesView setIndentationMarkerFollowsCell: YES];
1667 [classesView setAutoresizesOutlineColumn: YES];
1668 [classesView setIndentationPerLevel: 10];
1669 [classesView setAttributeOffset: 30];
1670 [classesView setBackgroundColor: salmonColor ];
1671 [classesView setRowHeight: 18];
1672 [classesScrollView setDocumentView: classesView];
1673 RELEASE(classesView);
1674
1675 tableColumn = [[NSTableColumn alloc] initWithIdentifier: @"classes"];
1676 [[tableColumn headerCell] setStringValue: _(@"Classes")];
1677 [tableColumn setMinWidth: 190];
1678 [tableColumn setResizable: YES];
1679 [tableColumn setEditable: YES];
1680 [classesView addTableColumn: tableColumn];
1681 [classesView setOutlineTableColumn: tableColumn];
1682 RELEASE(tableColumn);
1683
1684 tableColumn = [[NSTableColumn alloc] initWithIdentifier: @"outlets"];
1685 [[tableColumn headerCell] setStringValue: _(@"Outlet")];
1686 [tableColumn setWidth: 50];
1687 [tableColumn setResizable: NO];
1688 [tableColumn setEditable: NO];
1689 [classesView addTableColumn: tableColumn];
1690 [classesView setOutletColumn: tableColumn];
1691 RELEASE(tableColumn);
1692
1693 tableColumn = [[NSTableColumn alloc] initWithIdentifier: @"actions"];
1694 [[tableColumn headerCell] setStringValue: _(@"Action")];
1695 [tableColumn setWidth: 50];
1696 [tableColumn setResizable: NO];
1697 [tableColumn setEditable: NO];
1698 [classesView addTableColumn: tableColumn];
1699 [classesView setActionColumn: tableColumn];
1700 RELEASE(tableColumn);
1701
1702 [classesView sizeToFit];
1703
1704 // expand all of the items in the classesView...
1705 [classesView expandItem: @"NSObject"];
1706
1707 [selectionBox setContentView: scrollView];
1708
1709 /*
1710 * Set up special-case dummy objects and add them to the objects view.
1711 */
1712 filesOwner = [GormFilesOwner new];
1713 [self setName: @"NSOwner" forObject: filesOwner];
1714 [objectsView addObject: filesOwner];
1715 firstResponder = [GormFirstResponder new];
1716 [self setName: @"NSFirst" forObject: firstResponder];
1717 [objectsView addObject: firstResponder];
1718 fontManager = [GormFontManager new];
1719 [self setName: @"NSFont" forObject: fontManager];
1720 // [objectsView addObject: fontManager];
1721
1722 /*
1723 * Set image for this miniwindow.
1724 */
1725 [window setMiniwindowImage: [(id)filesOwner imageForViewer]];
1726
1727 hidden = [NSMutableArray new];
1728 /*
1729 * Watch to see when we are starting/ending testing.
1730 */
1731 [nc addObserver: self
1732 selector: @selector(handleNotification:)
1733 name: IBWillBeginTestingInterfaceNotification
1734 object: nil];
1735 [nc addObserver: self
1736 selector: @selector(handleNotification:)
1737 name: IBWillEndTestingInterfaceNotification
1738 object: nil];
1739
1740 // preload headers...
1741 if ([defaults boolForKey: @"PreloadHeaders"])
1742 {
1743 NSArray *headerList = [defaults arrayForKey: @"HeaderList"];
1744 NSEnumerator *en = [headerList objectEnumerator];
1745 id obj = nil;
1746
1747 while ((obj = [en nextObject]) != nil)
1748 {
1749 NSDebugLog(@"Preloading %@", obj);
1750 [self parseHeader: (NSString *)obj];
1751 }
1752 }
1753
1754 }
1755 return self;
1756 }
1757
1758 - (id) instantiateClass: (id)sender
1759 {
1760 NSDebugLog(@"document -> instantiateClass: ");
1761
1762 if ([[selectionView selectedCell] tag] == 3)
1763 {
1764 int i = [classesView selectedRow];
1765
1766 if (i >= 0)
1767 {
1768 id className = [classesView itemAtRow: i];
1769 GSNibItem *item = nil;
1770
1771 if([className isEqualToString: @"FirstResponder"])
1772 return nil;
1773
1774 item = [[GormObjectProxy alloc] initWithClassName: className
1775 frame: NSMakeRect(0,0,0,0)];
1776
1777 [self setName: nil forObject: item];
1778 [self attachObject: item toParent: nil];
1779 RELEASE(item);
1780
1781 [selectionView selectCellWithTag: 0];
1782 [selectionBox setContentView: scrollView];
1783 }
1784
1785 }
1786
1787 return self;
1788 }
1789
1790 - (BOOL) isActive
1791 {
1792 return isActive;
1793 }
1794
1795 - (NSString*) nameForObject: (id)anObject
1796 {
1797 return (NSString*)NSMapGet(objToName, (void*)anObject);
1798 }
1799
1800 - (id) objectForName: (NSString*)name
1801 {
1802 return [nameTable objectForKey: name];
1803 }
1804
1805 - (BOOL) objectIsVisibleAtLaunch: (id)anObject
1806 {
1807 return [[nameTable objectForKey: @"NSVisible"] containsObject: anObject];
1808 }
1809
1810 - (NSArray*) objects
1811 {
1812 return [nameTable allValues];
1813 }
1814
1815 /*
1816 * NB. This assumes we have an empty document to start with - the loaded
1817 * document is merged in to it.
1818 */
1819 - (id) loadDocument: (NSString*)aFile
1820 {
1821 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
1822 NSMutableDictionary *nt;
1823 NSMutableDictionary *cc;
1824 NSData *data;
1825 NSUnarchiver *u;
1826 GSNibContainer *c;
1827 NSEnumerator *enumerator;
1828 id <IBConnectors> con;
1829 NSString *ownerClass, *key;
1830 NSFileManager *mgr = [NSFileManager defaultManager];
1831 BOOL isDir = NO;
1832 NSDirectoryEnumerator *dirEnumerator;
1833
1834 if ([mgr fileExistsAtPath: aFile isDirectory: &isDir])
1835 {
1836 // if the data is in a directory, then load from objects.gorm
1837 if (isDir == NO)
1838 {
1839 data = [NSData dataWithContentsOfFile: aFile];
1840 NSDebugLog(@"Loaded data from file...");
1841 }
1842 else
1843 {
1844 NSString *newFileName;
1845
1846 newFileName = [aFile stringByAppendingPathComponent: @"objects.gorm"];
1847 data = [NSData dataWithContentsOfFile: newFileName];
1848 NSDebugLog(@"Loaded data from %@...", newFileName);
1849 }
1850 }
1851 else
1852 {
1853 // no file exists...
1854 data = nil;
1855 }
1856
1857 // check the data...
1858 if (data == nil)
1859 {
1860 NSRunAlertPanel(NULL,
1861 [NSString stringWithFormat: @"Could not read '%@' data", aFile],
1862 @"OK", NULL, NULL);
1863 return nil;
1864 }
1865
1866 /*
1867 * Create an unarchiver, and use it to unarchive the nib file while
1868 * handling class replacement so that standard objects understood
1869 * by the gui library are converted to their Gorm internal equivalents.
1870 */
1871 u = AUTORELEASE([[NSUnarchiver alloc] initForReadingWithData: data]);
1872 [u decodeClassName: @"GSNibContainer"
1873 asClassName: @"GormDocument"];
1874 [u decodeClassName: @"GSNibItem"
1875 asClassName: @"GormObjectProxy"];
1876 [u decodeClassName: @"GSCustomView"
1877 asClassName: @"GormCustomView"];
1878
1879 // classes
1880 [u decodeClassName: @"NSMenu"
1881 asClassName: @"GormNSMenu"];
1882 [u decodeClassName: @"NSWindow"
1883 asClassName: @"GormNSWindow"];
1884 [u decodeClassName: @"NSPanel"
1885 asClassName: @"GormNSPanel"];
1886 [u decodeClassName: @"NSPopUpButton"
1887 asClassName: @"GormNSPopUpButton"];
1888 [u decodeClassName: @"NSPopUpButtonCell"
1889 asClassName: @"GormNSPopUpButtonCell"];
1890 [u decodeClassName: @"NSBrowser"
1891 asClassName: @"GormNSBrowser"];
1892 [u decodeClassName: @"NSTableView"
1893 asClassName: @"GormNSTableView"];
1894 [u decodeClassName: @"NSOutlineView"
1895 asClassName: @"GormNSOutlineView"];
1896
1897 c = [u decodeObject];
1898 if (c == nil || [c isKindOfClass: [GSNibContainer class]] == NO)
1899 {
1900 NSRunAlertPanel(NULL, _(@"Could not unarchive document data"),
1901 _(@"OK"), NULL, NULL);
1902 return nil;
1903 }
1904
1905 // retrieve the custom class data...
1906 cc = [[c nameTable] objectForKey: GSCustomClassMap];
1907 if (cc == nil)
1908 {
1909 cc = [NSMutableDictionary dictionary]; // create an empty one.
1910 [[c nameTable] setObject: cc forKey: GSCustomClassMap];
1911 }
1912 [classManager setCustomClassMap: cc];
1913 NSDebugLog(@"cc = %@", cc);
1914 NSDebugLog(@"customClasses = %@", [classManager customClassMap]);
1915
1916 // convert from old file format...
1917 if (isDir == NO)
1918 {
1919 NSString *s;
1920
1921 s = [aFile stringByDeletingPathExtension];
1922 s = [s stringByAppendingPathExtension: @"classes"];
1923 if (![classManager loadCustomClasses: s])
1924 {
1925 NSRunAlertPanel(NULL, _(@"Could not open the associated classes file.\n"
1926 @"You won't be able to edit connections on custom classes"),
1927 _(@"OK"), NULL, NULL);
1928 }
1929 }
1930 else
1931 {
1932 NSString *s;
1933
1934 s = [aFile stringByAppendingPathComponent: @"data.classes"];
1935 if (![classManager loadCustomClasses: s])
1936 {
1937 NSRunAlertPanel(NULL, _(@"Could not open the associated classes file.\n"
1938 @"You won't be able to edit connections on custom classes"),
1939 _(@"OK"), NULL, NULL);
1940 }
1941 }
1942
1943 [classesView reloadData];
1944
1945 /*
1946 * In the newly loaded nib container, we change all the connectors
1947 * to hold the objects rather than their names (using our own dummy
1948 * object as the 'NSOwner'.
1949 */
1950 ownerClass = [[c nameTable] objectForKey: @"NSOwner"];
1951 if (ownerClass)
1952 [filesOwner setClassName: ownerClass];
1953 [[c nameTable] setObject: filesOwner forKey: @"NSOwner"];
1954 [[c nameTable] setObject: firstResponder forKey: @"NSFirst"];
1955
1956 /* Iterate over the contents of nameTable and create the connections */
1957 nt = [c nameTable];
1958 enumerator = [[c connections] objectEnumerator];
1959 while ((con = [enumerator nextObject]) != nil)
1960 {
1961 NSString *name;
1962 id obj;
1963
1964 name = (NSString*)[con source];
1965 obj = [nt objectForKey: name];
1966 [con setSource: obj];
1967 name = (NSString*)[con destination];
1968 obj = [nt objectForKey: name];
1969 [con setDestination: obj];
1970 }
1971
1972 /*
1973 * Now we merge the objects from the nib container into our own data
1974 * structures, taking care not to overwrite our NSOwner and NSFirst.
1975 */
1976 [nt removeObjectForKey: @"NSOwner"];
1977 [nt removeObjectForKey: @"NSFirst"];
1978 [connections addObjectsFromArray: [c connections]];
1979 [nameTable addEntriesFromDictionary: nt];
1980
1981 [self rebuildObjToNameMapping];
1982
1983 /*
1984 * set our new file name
1985 */
1986 ASSIGN(documentPath, aFile);
1987 [window setTitleWithRepresentedFilename: documentPath];
1988 [nc postNotificationName: IBDidOpenDocumentNotification
1989 object: self];
1990
1991 /*
1992 * read in all of the sounds in the .gorm wrapper and
1993 * load them into the editor.
1994 */
1995 dirEnumerator = [mgr enumeratorAtPath: documentPath];
1996 if (dirEnumerator)
1997 {
1998 NSString *file = nil;
1999 NSArray *fileTypes = [NSSound soundUnfilteredFileTypes];
2000 while ((file = [dirEnumerator nextObject]))
2001 {
2002 if ([fileTypes containsObject: [file pathExtension]])
2003 {
2004 NSString *soundPath;
2005
2006 NSDebugLog(@"Add the sound %@", file);
2007 soundPath = [documentPath stringByAppendingPathComponent: file];
2008 [soundsView addObject: [self _createSoundPlaceHolder: soundPath]];
2009 [sounds addObject: soundPath];
2010 }
2011 }
2012 }
2013
2014 /*
2015 * read in all of the images in the .gorm wrapper and
2016 * load them into the editor.
2017 */
2018 dirEnumerator = [mgr enumeratorAtPath: documentPath];
2019 if (dirEnumerator)
2020 {
2021 NSString *file = nil;
2022 NSArray *fileTypes = [NSImage imageFileTypes];
2023 while ((file = [dirEnumerator nextObject]))
2024 {
2025 if ([fileTypes containsObject: [file pathExtension]])
2026 {
2027 NSString *imagePath;
2028 id placeHolder;
2029
2030 imagePath = [documentPath stringByAppendingPathComponent: file];
2031 placeHolder = [self _createImagePlaceHolder: imagePath];
2032 if (placeHolder)
2033 {
2034 NSDebugLog(@"Add the image %@", file);
2035 [imagesView addObject: placeHolder];
2036 [images addObject: imagePath];
2037 }
2038 }
2039 }
2040 }
2041
2042 // get the custom class map and set it into the class manager...
2043 // NSLog(@"customClasses = %@", customClasses);
2044 // [classManager setCustomClassMap: customClasses];
2045
2046 NSDebugLog(@"nameTable = %@",[c nameTable]);
2047
2048 enumerator = [[c nameTable] keyEnumerator];
2049 while ((key = [enumerator nextObject]) != nil)
2050 {
2051 id o = [[c nameTable] objectForKey: key];
2052 if ([o respondsToSelector: @selector(awakeFromDocument:)])
2053 {
2054 [o awakeFromDocument: self];
2055 }
2056 }
2057
2058 return self;
2059 }
2060
2061 /*
2062 * Build our reverse mapping information and other initialisation
2063 */
2064 - (void) rebuildObjToNameMapping
2065 {
2066 NSEnumerator *enumerator;
2067 NSString *name;
2068
2069 NSResetMapTable(objToName);
2070 NSMapInsert(objToName, (void*)filesOwner, (void*)@"NSOwner");
2071 NSMapInsert(objToName, (void*)firstResponder, (void*)@"NSFirst");
2072 enumerator = [nameTable keyEnumerator];
2073 while ((name = [enumerator nextObject]) != nil)
2074 {
2075 id obj = [nameTable objectForKey: name];
2076
2077 NSMapInsert(objToName, (void*)obj, (void*)name);
2078 if ([obj isKindOfClass: [NSMenu class]] == YES)
2079 {
2080 if ([name isEqual: @"NSMenu"] == YES)
2081 {
2082 NSRect frame = [[NSScreen mainScreen] frame];
2083
2084 [[obj window] setFrameTopLeftPoint:
2085 NSMakePoint(1, frame.size.height-200)];
2086 [[self openEditorForObject: obj] activate];
2087 [objectsView addObject: obj];
2088 }
2089 }
2090 else if ([obj isKindOfClass: [NSWindow class]] == YES)
2091 {
2092 [objectsView addObject: obj];
2093 [[self openEditorForObject: obj] activate];
2094 }
2095 else if ([obj isKindOfClass: [GSNibItem class]] == YES
2096 && [obj isKindOfClass: [GormCustomView class]] == NO)
2097 {
2098 [objectsView addObject: obj];
2099 //[[self openEditorForObject: obj] activate];
2100 }
2101 }
2102 }
2103
2104 /*
2105 * NB. This assumes we have an empty document to start with - the loaded
2106 * document is merged in to it.
2107 */
2108 - (id) openDocument: (id)sender
2109 {
2110 NSArray *fileTypes;
2111 NSOpenPanel *oPanel = [NSOpenPanel openPanel];
2112 int result;
2113 NSString *pth = [[NSUserDefaults standardUserDefaults]
2114 objectForKey:@"OpenDir"];
2115
2116 fileTypes = [NSArray arrayWithObjects: @"gorm", @"gmodel", nil];
2117 [oPanel setAllowsMultipleSelection: NO];
2118 [oPanel setCanChooseFiles: YES];
2119 [oPanel setCanChooseDirectories: NO];
2120 result = [oPanel runModalForDirectory: pth
2121 file: nil
2122 types: fileTypes];
2123 if (result == NSOKButton)
2124 {
2125 NSString *filename = [oPanel filename];
2126 NSString *ext = [filename pathExtension];
2127
2128 [[NSUserDefaults standardUserDefaults] setObject: [oPanel directory]
2129 forKey:@"OpenDir"];
2130 if ([ext isEqualToString:@"gorm"] || [ext isEqualToString:@"nib"])
2131 {
2132 return [self loadDocument: filename];
2133 }
2134 else if ([ext isEqualToString:@"gmodel"])
2135 {
2136 return [self openGModel: filename];
2137 }
2138 }
2139 return nil; /* Failed */
2140 }
2141
2142 - (id<IBEditors>) openEditorForObject: (id)anObject
2143 {
2144 id<IBEditors> e = [self editorForObject: anObject create: YES];
2145 id<IBEditors, IBSelectionOwners> p = [self parentEditorForEditor: e];
2146
2147 if (p != nil && p != objectsView)
2148 {
2149 [self openEditorForObject: [p editedObject]];
2150 }
2151 [e orderFront];
2152 [[e window] makeKeyAndOrderFront: self];
2153 return e;
2154 }
2155
2156 - (id<IBEditors, IBSelectionOwners>) parentEditorForEditor: (id<IBEditors>)anEditor
2157 {
2158 NSArray *links;
2159 GormObjectToEditor *con;
2160
2161 links = [self connectorsForSource: anEditor
2162 ofClass: [GormEditorToParent class]];
2163 con = [links lastObject];
2164 return [con destination];
2165 }
2166
2167 - (id) parentOfObject: (id)anObject
2168 {
2169 NSArray *old;
2170 id<IBConnectors> con;
2171
2172 old = [self connectorsForSource: anObject ofClass: [NSNibConnector class]];
2173 con = [old lastObject];
2174 if ([con destination] != filesOwner && [con destination] != firstResponder)
2175 {
2176 return [con destination];
2177 }
2178 return nil;
2179 }
2180
2181 - (NSArray*) pasteType: (NSString*)aType
2182 fromPasteboard: (NSPasteboard*)aPasteboard
2183 parent: (id)parent
2184 {
2185 NSData *data;
2186 NSArray *objects;
2187 NSEnumerator *enumerator;
2188 NSPoint filePoint;
2189 NSPoint screenPoint;
2190 NSUnarchiver *u;
2191
2192 data = [aPasteboard dataForType: aType];
2193 if (data == nil)
2194 {
2195 NSLog(@"Pasteboard %@ doesn't contain data of %@", aPasteboard, aType);
2196 return nil;
2197 }
2198 u = AUTORELEASE([[NSUnarchiver alloc] initForReadingWithData: data]);
2199 [u decodeClassName: @"GSCustomView"
2200 asClassName: @"GormCustomView"];
2201 objects = [u decodeObject];
2202 enumerator = [objects objectEnumerator];
2203 filePoint = [window mouseLocationOutsideOfEventStream];
2204 screenPoint = [window convertBaseToScreen: filePoint];
2205
2206 /*
2207 * Windows and panels are a special case - for a multiple window paste,
2208 * the windows need to be positioned so they are not on top of each other.
2209 */
2210 if ([aType isEqualToString: IBWindowPboardType] == YES)
2211 {
2212 NSWindow *win;
2213
2214 while ((win = [enumerator nextObject]) != nil)
2215 {
2216 [win setFrameTopLeftPoint: screenPoint];
2217 screenPoint.x += 10;
2218 screenPoint.y -= 10;
2219 }
2220 }
2221 else
2222 {
2223 NSEnumerator *enumerator = [objects objectEnumerator];
2224 id obj;
2225 NSRect frame;
2226 while ((obj = [enumerator nextObject]) != nil)
2227 {
2228 // check to see if the object has a frame. If so, then
2229 // modify it. If not, simply iterate to the next object
2230 if([obj respondsToSelector: @selector(frame)])
2231 {
2232 frame = [obj frame];
2233 frame.origin.x -= 6;
2234 frame.origin.y -= 6;
2235 [obj setFrame: frame];
2236 }
2237 }
2238 }
2239
2240 [self attachObjects: objects toParent: parent];
2241 [self touch];
2242 return objects;
2243 }
2244
2245 - (void) removeConnector: (id<IBConnectors>)aConnector
2246 {
2247 // issue pre notification..
2248 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
2249 [nc postNotificationName: IBWillRemoveConnectorNotification
2250 object: self];
2251 // mark the document as changed.
2252 [self touch];
2253 // issue port notification..
2254 [connections removeObjectIdenticalTo: aConnector];
2255 [nc postNotificationName: IBDidRemoveConnectorNotification
2256 object: self];
2257 }
2258
2259 - (void) resignSelectionForEditor: (id<IBEditors>)editor
2260 {
2261 NSEnumerator *enumerator = [connections objectEnumerator];
2262 Class editClass = [GormObjectToEditor class];
2263 id<IBConnectors> c;
2264
2265 /*
2266 * This editor wants to give up the selection. Go through all the known
2267 * editors (with links in the connections array) and try to find one
2268 * that wants to take over the selection. Activate whatever editor we
2269 * find (if any).
2270 */
2271 while ((c = [enumerator nextObject]) != nil)
2272 {
2273 if ([c class] == editClass)
2274 {
2275 id<IBEditors> e = [c destination];
2276
2277 if (e != editor && [e wantsSelection] == YES)
2278 {
2279 [e activate];
2280 [self setSelectionFromEditor: e];
2281 return;
2282 }
2283 }
2284 }
2285 /*
2286 * No editor available to take the selection - set a nil owner.
2287 */
2288 [self setSelectionFromEditor: nil];
2289 }
2290
2291 - (void) setupDefaults: (NSString*)type
2292 {
2293 if (hasSetDefaults == YES)
2294 {
2295 return;
2296 }
2297 hasSetDefaults = YES;
2298 if ([type isEqual: @"Application"] == YES)
2299 {
2300 NSMenu *aMenu;
2301 NSWindow *aWindow;
2302 NSRect frame = [[NSScreen mainScreen] frame];
2303 unsigned style = NSTitledWindowMask | NSClosableWindowMask
2304 | NSResizableWindowMask | NSMiniaturizableWindowMask;
2305
2306 if ([NSMenu respondsToSelector: @selector(allocSubstitute)])
2307 {
2308 aMenu = [[NSMenu allocSubstitute] init];
2309 }
2310 else
2311 {
2312 aMenu = [[NSMenu alloc] init];
2313 }
2314
2315 if ([NSWindow respondsToSelector: @selector(allocSubstitute)])
2316 {
2317 aWindow = [[NSWindow allocSubstitute]
2318 initWithContentRect: NSMakeRect(0,0,600, 400)
2319 styleMask: style
2320 backing: NSBackingStoreRetained
2321 defer: NO];
2322 }
2323 else
2324 {
2325 aWindow = [[NSWindow alloc]
2326 initWithContentRect: NSMakeRect(0,0,600, 400)
2327 styleMask: style
2328 backing: NSBackingStoreRetained
2329 defer: NO];
2330 }
2331 [aWindow setFrameTopLeftPoint:
2332 NSMakePoint(220, frame.size.height-100)];
2333 [aWindow setTitle: _(@"My Window")];
2334 [self setName: @"My Window" forObject: aWindow];
2335 [self attachObject: aWindow toParent: nil];
2336 [self setObject: aWindow isVisibleAtLaunch: YES];
2337 RELEASE(aWindow);
2338
2339 [aMenu setTitle: _(@"Main Menu")];
2340 [aMenu addItemWithTitle: _(@"Hide")
2341 action: @selector(hide:)
2342 keyEquivalent: @"h"];
2343 [aMenu addItemWithTitle: _(@"Quit")
2344 action: @selector(terminate:)
2345 keyEquivalent: @"q"];
2346 [self setName: @"NSMenu" forObject: aMenu];
2347 [self attachObject: aMenu toParent: nil];
2348 [objectsView addObject: aMenu];
2349 // RETAIN(aMenu);
2350
2351 [[aMenu window] setFrameTopLeftPoint:
2352 NSMakePoint(1, frame.size.height-200)];
2353 RELEASE(aMenu);
2354 }
2355 else if ([type isEqual: @"Inspector"] == YES)
2356 {
2357 NSWindow *aWindow;
2358 NSRect frame = [[NSScreen mainScreen] frame];
2359 unsigned style = NSTitledWindowMask | NSClosableWindowMask;
2360
2361 aWindow = [[NSWindow alloc] initWithContentRect: NSMakeRect(0,0, IVW, IVH)
2362 styleMask: style
2363 backing: NSBackingStoreRetained
2364 defer: NO];
2365 [aWindow setFrameTopLeftPoint:
2366 NSMakePoint(220, frame.size.height-100)];
2367 [aWindow setTitle: _(@"Inspector Window")];
2368 [self setName: @"InspectorWin" forObject: aWindow];
2369 [self attachObject: aWindow toParent: nil];
2370 RELEASE(aWindow);
2371 }
2372 else if ([type isEqual: @"Palette"] == YES)
2373 {
2374 NSWindow *aWindow;
2375 NSRect frame = [[NSScreen mainScreen] frame];
2376 unsigned style = NSTitledWindowMask | NSClosableWindowMask;
2377
2378 aWindow = [[NSWindow alloc] initWithContentRect: NSMakeRect(0,0,272,192)
2379 styleMask: style
2380 backing: NSBackingStoreRetained
2381 defer: NO];
2382 [aWindow setFrameTopLeftPoint:
2383 NSMakePoint(220, frame.size.height-100)];
2384 [aWindow setTitle: _(@"Palette Window")];
2385 [self setName: @"PaletteWin" forObject: aWindow];
2386 [self attachObject: aWindow toParent: nil];
2387 RELEASE(aWindow);
2388 }
2389 }
2390
2391 - (void) setName: (NSString*)aName forObject: (id)object
2392 {
2393 id oldObject;
2394 NSString *oldName;
2395
2396 if (object == nil)
2397 {
2398 NSLog(@"Attempt to set name for nil object");
2399 return;
2400 }
2401 if (aName == nil)
2402 {
2403 /*
2404 * No name given - so we must generate one unless we already have one.
2405 */
2406 oldName = [self nameForObject: object];
2407 if (oldName == nil)
2408 {
2409 NSString *base;
2410 unsigned i = 0;
2411
2412 /*
2413 * Generate a sensible name for the object based on its class.
2414 */
2415 if ([object isKindOfClass: [GSNibItem class]])
2416 {
2417 // use the actual class name for proxies
2418 base = [(id)object className];
2419 }
2420 else
2421 {
2422 base = NSStringFromClass([object class]);
2423 }
2424 if ([base hasPrefix: @"NS"] || [base hasPrefix: @"GS"])
2425 {
2426 base = [base substringFromIndex: 2];
2427 }
2428 aName = base;
2429 while ([nameTable objectForKey: aName] != nil)
2430 {
2431 aName = [base stringByAppendingFormat: @"%u", ++i];
2432 }
2433 }
2434 else
2435 {
2436 return; /* Already named ... nothing to do */
2437 }
2438 }
2439 else
2440 {
2441 oldObject = [nameTable objectForKey: aName];
2442 if (oldObject != nil)
2443 {
2444 NSLog(@"Attempt to re-use name '%@'", aName);
2445 return;
2446 }
2447 oldName = [self nameForObject: object];
2448 if (oldName != nil)
2449 {
2450 if ([oldName isEqual: aName] == YES)
2451 {
2452 return; /* Already have this name ... nothing to do */
2453 }
2454 RETAIN(object);
2455 AUTORELEASE(object);
2456 [nameTable removeObjectForKey: oldName];
2457 NSMapRemove(objToName, (void*)object);
2458 }
2459 }
2460 aName = [aName copy]; /* Make sure it's immutable */
2461 [nameTable setObject: object forKey: aName];
2462 NSMapInsert(objToName, (void*)object, (void*)aName);
2463 RELEASE(aName);
2464 if (oldName != nil)
2465 {
2466 [nameTable removeObjectForKey: oldName];
2467 }
2468 if ([objectsView containsObject: object] == YES)
2469 {
2470 [objectsView refreshCells];
2471 }
2472 }
2473
2474 - (void) setObject: (id)anObject isVisibleAtLaunch: (BOOL)flag
2475 {
2476 NSMutableArray *a = [nameTable objectForKey: @"NSVisible"];
2477
2478 if (flag == YES)
2479 {
2480 if (a == nil)
2481 {
2482 a = [NSMutableArray new];
2483 [nameTable setObject: a forKey: @"NSVisible"];
2484 RELEASE(a);
2485 }
2486 if ([a containsObject: anObject] == NO)
2487 {
2488 [a addObject: anObject];
2489 }
2490 }
2491 else
2492 {
2493 [a removeObject: anObject];
2494 }
2495 }
2496
2497 - (void) setObject: (id)anObject isDeferred: (BOOL)flag
2498 {
2499 NSMutableArray *a = [nameTable objectForKey: @"NSDeferred"];
2500
2501 if (flag == YES)
2502 {
2503 if (a == nil)
2504 {
2505 a = [NSMutableArray new];
2506 [nameTable setObject: a forKey: @"NSDeferred"];
2507 RELEASE(a);
2508 }
2509 if ([a containsObject: anObject] == NO)
2510 {
2511 [a addObject: anObject];
2512 }
2513 }
2514 else
2515 {
2516 [a removeObject: anObject];
2517 }
2518 }
2519
2520 - (BOOL) objectIsDeferred: (id)anObject
2521 {
2522 return [[nameTable objectForKey: @"NSDeferred"] containsObject: anObject];
2523 }
2524
2525 // windows / services menus...
2526 - (void) setWindowsMenu: (NSMenu *)anObject
2527 {
2528 if(anObject != nil)
2529 {
2530 [nameTable setObject: anObject forKey: @"NSWindowsMenu"];
2531 }
2532 else
2533 {
2534 [nameTable removeObjectForKey: @"NSWindowsMenu"];
2535 }
2536 }
2537
2538 - (NSMenu *) windowsMenu
2539 {
2540 return [nameTable objectForKey: @"NSWindowsMenu"];
2541 }
2542
2543 - (void) setServicesMenu: (NSMenu *)anObject
2544 {
2545 if(anObject != nil)
2546 {
2547 [nameTable setObject: anObject forKey: @"NSServicesMenu"];
2548 }
2549 else
2550 {
2551 [nameTable removeObjectForKey: @"NSServicesMenu"];
2552 }
2553 }
2554
2555 - (NSMenu *) servicesMenu
2556 {
2557 return [nameTable objectForKey: @"NSServicesMenu"];
2558 }
2559
2560 /*
2561 * To revert to a saved version, we actually load a new document and
2562 * close the original document, returning the id of the new document.
2563 */
2564 - (id) revertDocument: (id)sender
2565 {
2566 GormDocument *reverted = AUTORELEASE([GormDocument new]);
2567
2568 if ([reverted loadDocument: documentPath] != nil)
2569 {
2570 NSRect frame = [window frame];
2571
2572 [window setReleasedWhenClosed: YES];
2573 [window close];
2574 [[reverted window] setFrame: frame display: YES];
2575 return reverted;
2576 }
2577 return nil;
2578 }
2579
2580 - (BOOL) saveAsDocument: (id)sender
2581 {
2582 NSSavePanel *sp;
2583 int result;
2584
2585 sp = [NSSavePanel savePanel];
2586 [sp setRequiredFileType: @"gorm"];
2587 result = [sp runModalForDirectory: NSHomeDirectory() file: @""];
2588 if (result == NSOKButton)
2589 {
2590 NSFileManager *mgr = [NSFileManager defaultManager];
2591 NSString *path = [sp filename];
2592 NSString *old = documentPath;
2593
2594 if ([path isEqual: documentPath] == NO
2595 && [mgr fileExistsAtPath: path] == YES)
2596 {
2597 /* NSSavePanel has already asked if it's ok to replace */
2598 NSString *bPath = [path stringByAppendingString: @"~"];
2599
2600 [mgr removeFileAtPath: bPath handler: nil];
2601 [mgr movePath: path toPath: bPath handler: nil];
2602 }
2603 documentPath = RETAIN(path);
2604 [self saveGormDocument: sender];
2605 RELEASE(old);
2606
2607 return YES;
2608 /* FIXME - need to update files window title etc */
2609 }
2610 return NO;
2611 }
2612
2613 //
2614 // Private method which iterates through the list of custom classes and instructs
2615 // the archiver to replace the actual object with template during the archiving
2616 // process.
2617 //
2618 - (void) _replaceObjectsWithTemplates: (NSArchiver *)archiver
2619 {
2620 GormClassManager *cm = [self classManager];
2621 NSEnumerator *en = [[cm customClassMap] keyEnumerator];
2622 id key = nil;
2623 NSDebugLog(@"Called ");
2624 while((key = [en nextObject]) != nil)
2625 {
2626 id customClass = [cm customClassForName: key];
2627 id object = [self objectForName: key]; // put code here to repair old .gorm files if necessary.
2628 NSString *superClass = [cm nonCustomSuperClassOf: customClass];
2629 id <GSTemplate> template = [GSTemplateFactory templateForObject: RETAIN(object)
2630 withClassName: RETAIN([customClass copy])
2631 withSuperClassName: superClass];
2632 NSDebugLog(@"customClass = %@",customClass);
2633 NSDebugLog(@"object = %@, key = %@, className = %@", object, key, customClass);
2634 [archiver replaceObject: object withObject: template];
2635 }
2636 }
2637
2638 - (BOOL) saveGormDocument: (id)sender
2639 {
2640 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
2641 BOOL archiveResult;
2642 NSArchiver *archiver;
2643 NSMutableData *archiverData;
2644 NSString *gormPath;
2645 NSString *classesPath;
2646 NSFileManager *mgr = [NSFileManager defaultManager];
2647 BOOL isDir;
2648 BOOL fileExists;
2649
2650 if (documentPath == nil)
2651 {
2652 if (! [self saveAsDocument: sender] )
2653 return NO;
2654 }
2655
2656 [nc postNotificationName: IBWillSaveDocumentNotification
2657 object: self];
2658
2659 [self beginArchiving];
2660
2661 // set up the necessary paths...
2662 gormPath = [documentPath stringByAppendingPathComponent: @"objects.gorm"];
2663 classesPath = [documentPath stringByAppendingPathComponent: @"data.classes"];
2664
2665 archiverData = [NSMutableData dataWithCapacity: 0];
2666 archiver = [[NSArchiver alloc] initForWritingWithMutableData: archiverData];
2667
2668 /* Special gorm classes to their archive equivalents. */
2669 [archiver encodeClassName: @"GormObjectProxy"
2670 intoClassName: @"GSNibItem"];
2671 [archiver encodeClassName: @"GormCustomView"
2672 intoClassName: @"GSCustomView"];
2673 [archiver encodeClassName: @"GormNSMenu"
2674 intoClassName: @"NSMenu"];
2675 [archiver encodeClassName: @"GormNSWindow"
2676 intoClassName: @"NSWindow"];
2677 [archiver encodeClassName: @"GormNSPanel"
2678 intoClassName: @"NSPanel"];
2679 [archiver encodeClassName: @"GormNSPopUpButton"
2680 intoClassName: @"NSPopUpButton"];
2681 [archiver encodeClassName: @"GormNSPopUpButtonCell"
2682 intoClassName: @"NSPopUpButtonCell"];
2683 [archiver encodeClassName: @"GormNSBrowser"
2684 intoClassName: @"NSBrowser"];
2685 [archiver encodeClassName: @"GormNSTableView"
2686 intoClassName: @"NSTableView"];
2687 [archiver encodeClassName: @"GormNSOutlineView"
2688 intoClassName: @"NSOutlineView"];
2689
2690 [self _replaceObjectsWithTemplates: archiver];
2691
2692 [archiver encodeRootObject: self];
2693 NSDebugLog(@"nameTable = %@",nameTable);
2694
2695 NSDebugLog(@"customClasses = %@", [classManager customClassMap]);
2696
2697 fileExists = [mgr fileExistsAtPath: documentPath isDirectory: &isDir];
2698 if (fileExists)
2699 {
2700 if (isDir == NO)
2701 {
2702 NSString *saveFilePath;
2703
2704 saveFilePath = [documentPath stringByAppendingPathExtension: @"save"];
2705 // move the old file to something...
2706 if (![mgr movePath: documentPath toPath: saveFilePath handler: nil])
2707 {
2708 NSLog(@"Error moving old %@ file to %@",
2709 documentPath, saveFilePath);
2710 }
2711
2712 // create the new directory..
2713 archiveResult = [mgr createDirectoryAtPath: documentPath
2714 attributes: nil];
2715 }
2716 else
2717 {
2718 // set to yes since the directory is already present.
2719 archiveResult = YES;
2720 }
2721 }
2722 else
2723 {
2724 // create the directory...
2725 archiveResult = [mgr createDirectoryAtPath: documentPath attributes: nil];
2726 }
2727
2728 RELEASE(archiver); // We're done with the archiver here..
2729
2730 if (archiveResult)
2731 {
2732 // save the data...
2733 archiveResult = [archiverData writeToFile: gormPath atomically: YES];
2734 if (archiveResult)
2735 {
2736 // save the custom classes.. and we're done...
2737 archiveResult = [classManager saveToFile: classesPath];
2738
2739 // copy sounds into the new folder...
2740 if (archiveResult)
2741 {
2742 NSEnumerator *en = [sounds objectEnumerator];
2743 id object = nil;
2744
2745 while ((object = [en nextObject]) != nil)
2746 {
2747 NSString *soundPath;
2748 BOOL copied = NO;
2749
2750 soundPath = [documentPath stringByAppendingPathComponent:
2751 [object lastPathComponent]];
2752 if(![object isEqualToString: soundPath])
2753 {
2754 copied = [mgr copyPath: object
2755 toPath: soundPath
2756 handler: nil];
2757 }
2758 else
2759 {
2760 // mark as copied if paths are equal...
2761 copied = YES;
2762 }
2763
2764 if (!copied)
2765 {
2766 NSLog(@"Could not find sound at path %@", object);
2767 }
2768 }
2769
2770 en = [images objectEnumerator];
2771
2772 while ((object = [en nextObject]) != nil)
2773 {
2774 NSString *imagePath;
2775 BOOL copied = NO;
2776
2777 imagePath = [documentPath stringByAppendingPathComponent:
2778 [object lastPathComponent]];
2779
2780 if(![object isEqualToString: imagePath])
2781 {
2782 copied = [mgr copyPath: object
2783 toPath: imagePath
2784 handler: nil];
2785 }
2786 else
2787 {
2788 // mark it as copied if paths are equal.
2789 copied = YES;
2790 }
2791
2792 if (!copied)
2793 {
2794 NSLog(@"Could not find image at path %@", object);
2795 }
2796 }
2797
2798 }
2799 }
2800 }
2801
2802 [self endArchiving];
2803
2804 if (archiveResult == NO)
2805 {
2806 NSRunAlertPanel(NULL,_( @"Could not save document"),
2807 _(@"OK"), NULL, NULL);
2808 }
2809 else
2810 {
2811 [window setDocumentEdited: NO];
2812 [window setTitleWithRepresentedFilename: documentPath];
2813
2814 [nc postNotificationName: IBDidSaveDocumentNotification
2815 object: self];
2816 }
2817 return YES;
2818 }
2819
2820 - (void) setDocumentActive: (BOOL)flag
2821 {
2822 if (flag != isActive)
2823 {
2824 NSEnumerator *enumerator;
2825 id obj;
2826
2827 enumerator = [nameTable objectEnumerator];
2828 if (flag == YES)
2829 {
2830 [(GormDocument*)[(id<IB>)NSApp activeDocument] setDocumentActive: NO];
2831 isActive = YES;
2832 while ((obj = [enumerator nextObject]) != nil)
2833 {
2834 if ([obj isKindOfClass: [NSWindow class]] == YES)
2835 {
2836 [obj orderFront: self];
2837 }
2838 else if ([obj isKindOfClass: [NSMenu class]] == YES)
2839 {
2840 [obj display];
2841 }
2842 }
2843 }
2844 else
2845 {
2846 isActive = NO;
2847 while ((obj = [enumerator nextObject]) != nil)
2848 {
2849 if ([obj isKindOfClass: [NSWindow class]] == YES)
2850 {
2851 [obj orderOut: self];
2852 }
2853 else if ([obj isKindOfClass: [NSMenu class]] == YES)
2854 {
2855 [obj close];
2856 }
2857 }
2858 }
2859 }
2860 }
2861
2862 - (void) setSelectionFromEditor: (id<IBEditors>)anEditor
2863 {
2864 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
2865 NSDebugLog(@"setSelectionFromEditor %@", anEditor);
2866 if ([(NSObject *)anEditor respondsToSelector: @selector(window)])
2867 {
2868 [[anEditor window] makeFirstResponder: anEditor];
2869 }
2870 [nc postNotificationName: IBSelectionChangedNotification
2871 object: anEditor];
2872 }
2873
2874 - (void) touch
2875 {
2876 [window setDocumentEdited: YES];
2877 }
2878
2879 - (NSWindow*) windowAndRect: (NSRect*)r forObject: (id)object
2880 {
2881 /*
2882 * Get the window and rectangle for which link markup should be drawn.
2883 */
2884 if ([objectsView containsObject: object] == YES)
2885 {
2886 /*
2887 * objects that exist in the document objects view must have their link
2888 * markup drawn there, so we ask the view for the required rectangle.
2889 */
2890 *r = [objectsView rectForObject: object];
2891 return [objectsView window];
2892 }
2893 else if ([object isKindOfClass: [NSMenuItem class]] == YES)
2894 {
2895 NSArray *links;
2896 NSMenu *menu;
2897 id editor;
2898
2899 /*
2900 * Menu items must have their markup drawn in the window of the
2901 * editor of the parent menu.
2902 */
2903 links = [self connectorsForSource: object
2904 ofClass: [NSNibConnector class]];
2905 menu = [[links lastObject] destination];
2906 editor = [self editorForObject: menu create: NO];
2907 *r = [editor rectForObject: object];
2908 return [editor window];
2909 }
2910 else if ([object isKindOfClass: [NSView class]] == YES)
2911 {
2912 /*
2913 * Normal view objects just get link markup drawn on them.
2914 */
2915 id temp = object;
2916 id editor = [self editorForObject: temp create: NO];
2917
2918 while ((temp != nil) && (editor == nil))
2919 {
2920 temp = [temp superview];
2921 editor = [self editorForObject: temp create: NO];
2922 }
2923
2924 if (temp == nil)
2925 {
2926 *r = [object convertRect: [object bounds] toView: nil];
2927 }
2928 else if ([editor respondsToSelector:
2929 @selector(windowAndRect:forObject:)])
2930 {
2931 return [editor windowAndRect: r forObject: object];
2932 }
2933 }
2934 else if ([object isKindOfClass: [NSTableColumn class]] == YES)
2935 {
2936 NSTableView *tv = [[(NSTableColumn*)object dataCell] controlView];
2937 NSTableHeaderView *th = [tv headerView];
2938 int index;
2939
2940 if (th == nil || tv == nil)
2941 {
2942 NSLog(@"fail 1 %@ %@ %@", [(NSTableColumn*)object headerCell], th, tv);
2943 *r = NSZeroRect;
2944 return nil;
2945 }
2946
2947 index = [[tv tableColumns] indexOfObject: object];
2948
2949 if (index == NSNotFound)
2950 {
2951 NSLog(@"fail 2");
2952 *r = NSZeroRect;
2953 return nil;
2954 }
2955
2956 *r = [th convertRect: [th headerRectOfColumn: index]
2957 toView: nil];
2958 return [th window];
2959 }
2960 else
2961 {
2962 *r = NSZeroRect;
2963 return nil;
2964 }
2965
2966 // never reached, keeps gcc happy
2967 return nil;
2968 }
2969
2970 - (NSWindow*) window
2971 {
2972 return window;
2973 }
2974
2975 - (BOOL) couldCloseDocument
2976 {
2977 if ([window isDocumentEdited] == YES)
2978 {
2979 NSString *msg;
2980 int result;
2981
2982 if (documentPath == nil)
2983 {
2984 msg = _(@"Document 'UNTITLED' has been modified");
2985 }
2986 else
2987 {
2988 msg = [NSString stringWithFormat: _(@"Document '%@' has been modified"),
2989 [documentPath lastPathComponent]];
2990 }
2991 result = NSRunAlertPanel(NULL, msg, _(@"Save"), _(@"Don't Save"), _(@"Cancel"));
2992
2993 if (result == NSAlertDefaultReturn)
2994 {
2995 //Save
2996 if (! [self saveGormDocument: self] )
2997 return NO;
2998 }
2999
3000 //Cancel
3001 else if (result == NSAlertOtherReturn)
3002 return NO;
3003 }
3004
3005 return YES;
3006
3007 }
3008 - (BOOL) windowShouldClose: (id)sender
3009 {
3010 return [self couldCloseDocument];
3011 }
3012
3013 // convenience methods for formatting outlets/actions
3014 - (NSString*) _identifierString: (NSString*)str
3015 {
3016 static NSCharacterSet *illegal = nil;
3017 static NSCharacterSet *numeric = nil;
3018 NSRange r;
3019 NSMutableString *m;
3020
3021 if (illegal == nil)
3022 {
3023 illegal = [[NSCharacterSet characterSetWithCharactersInString:
3024 @"_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"]
3025 invertedSet];
3026 numeric = [NSCharacterSet characterSetWithCharactersInString:
3027 @"0123456789"];
3028 RETAIN(illegal);
3029 RETAIN(numeric);
3030 }
3031 if (str == nil)
3032 {
3033 return nil;
3034 }
3035 m = [str mutableCopy];
3036 r = [str rangeOfCharacterFromSet: illegal];
3037 while (r.length > 0)
3038 {
3039 [m deleteCharactersInRange: r];
3040 r = [m rangeOfCharacterFromSet: illegal];
3041 }
3042 r = [str rangeOfCharacterFromSet: numeric];
3043 while (r.length > 0 && r.location == 0)
3044 {
3045 [m deleteCharactersInRange: r];
3046 r = [m rangeOfCharacterFromSet: numeric];
3047 }
3048 str = [m copy];
3049 RELEASE(m);
3050 AUTORELEASE(str);
3051
3052 return str;
3053 }
3054
3055 - (NSString *)_formatAction: (NSString *)action
3056 {
3057 NSString *identifier;
3058
3059 identifier = [[self _identifierString: action] stringByAppendingString: @":"];
3060 return identifier;
3061 }
3062
3063 - (NSString *)_formatOutlet: (NSString *)outlet
3064 {
3065 NSString *identifier = [self _identifierString: outlet];
3066 return identifier;
3067 }
3068
3069 - (BOOL) removeConnectionsWithLabel: (NSString *)name
3070 forClassNamed: (NSString *)className
3071 isAction: (BOOL)action
3072 {
3073 NSEnumerator *en = [connections objectEnumerator];
3074 id<IBConnectors> c = nil;
3075 BOOL removed = YES;
3076
3077 // remove all.
3078 while ((c = [en nextObject]) != nil)
3079 {
3080 id proxy = nil;
3081 NSString *label = [c label];
3082
3083 if (action)
3084 {
3085 if (![label hasSuffix: @":"])
3086 continue;
3087 proxy = [c destination];
3088 }
3089 else
3090 {
3091 if ([label hasSuffix: @":"])
3092 continue;
3093 proxy = [c source];
3094 }
3095
3096 if ([label isEqualToString: name] &&
3097 [[proxy className] isEqualToString: className])
3098 {
3099 NSString *title;
3100 NSString *msg;
3101 int retval;
3102
3103 title = [NSString stringWithFormat:
3104 @"Modifying %@",(action==YES?@"Action":@"Outlet")];
3105 msg = [NSString stringWithFormat:
3106 _(@"This will break all connections to '%@'. Continue?"), name];
3107 retval = NSRunAlertPanel(title, msg,_(@"OK"),_(@"Cancel"), nil, nil);
3108
3109 if (retval == NSAlertDefaultReturn)
3110 {
3111 removed = YES;
3112 [self removeConnector: c];
3113 }
3114 else
3115 {
3116 removed = NO;
3117 }
3118 }
3119 }
3120
3121 // done...
3122 NSDebugLog(@"Removed references to %@ on %@", name, className);
3123 return removed;
3124 }
3125
3126 - (BOOL) removeConnectionsForClassNamed: (NSString *)className
3127 {
3128 NSEnumerator *en = [connections objectEnumerator];
3129 id<IBConnectors> c = nil;
3130 BOOL removed = YES;
3131 int retval = -1;
3132 NSString *title = [NSString stringWithFormat: _(@"Modifying Class")];
3133 NSString *msg;
3134
3135 msg = [NSString stringWithFormat: _(@"This will break all connections to "
3136 @"actions/outlets to instances of class '%@'. Continue?"), className];
3137
3138 // ask the user if he/she wants to continue...
3139 retval = NSRunAlertPanel(title, msg,_(@"OK"),_(@"Cancel"), nil, nil);
3140 if (retval == NSAlertDefaultReturn)
3141 {
3142 removed = YES;
3143 }
3144 else
3145 {
3146 removed = NO;
3147 }
3148
3149 // remove all.
3150 while ((c = [en nextObject]) != nil)
3151 {
3152 // check both...
3153 if ([[[c source] className] isEqualToString: className]
3154 || [[[c destination] className] isEqualToString: className])
3155 {
3156 [self removeConnector: c];
3157 }
3158 }
3159
3160 // done...
3161 NSDebugLog(@"Removed references to actions/outlets for objects of %@",
3162 className);
3163 return removed;
3164 }
3165
3166 - (BOOL) renameConnectionsForClassNamed: (NSString *)className
3167 toName: (NSString *)newName
3168 {
3169 NSEnumerator *en = [connections objectEnumerator];
3170 id<IBConnectors> c = nil;
3171 BOOL removed = YES;
3172 int retval = -1;
3173 NSString *title = [NSString stringWithFormat: _(@"Modifying Class")];
3174 NSString *msg = [NSString stringWithFormat:
3175 _(@"Change class name '%@' to '%@'. Continue?"),
3176 className, newName];
3177
3178 // ask the user if he/she wants to continue...
3179 retval = NSRunAlertPanel(title, msg,_(@"OK"),_(@"Cancel"), nil, nil);
3180 if (retval == NSAlertDefaultReturn)
3181 {
3182 removed = YES;
3183 }
3184 else
3185 {
3186 removed = NO;
3187 }
3188
3189 // remove all.
3190 while ((c = [en nextObject]) != nil)
3191 {
3192 id source = [c source];
3193 id destination = [c destination];
3194
3195 // check both...
3196 if ([[[c source] className] isEqualToString: className])
3197 {
3198 [source setClassName: newName];
3199 NSDebugLog(@"Found matching source");
3200 }
3201 else if ([[[c destination] className] isEqualToString: className])
3202 {
3203 [destination setClassName: newName];
3204 NSDebugLog(@"Found matching destination");
3205 }
3206 }
3207
3208 // Get the object from the object editor so that we can change the name
3209 // there too.
3210
3211 // done...
3212 NSDebugLog(@"Changed references to actions/outlets for objects of %@", className);
3213 return removed;
3214 }
3215
3216 // --- NSOutlineView dataSource ---
3217 - (id) outlineView: (NSOutlineView *)anOutlineView
3218 objectValueForTableColumn: (NSTableColumn *)aTableColumn
3219 byItem: item
3220 {
3221 if (anOutlineView == classesView)
3222 {
3223 id identifier = [aTableColumn identifier];
3224 id className = item;
3225
3226 if ([identifier isEqualToString: @"classes"])
3227 {
3228 return className;
3229 }
3230 else if ([identifier isEqualToString: @"outlets"])
3231 {
3232 return [NSString stringWithFormat: @"%d",
3233 [[classManager allOutletsForClassNamed: className] count]];
3234 }
3235 else if ([identifier isEqualToString: @"actions"])
3236 {
3237 return [NSString stringWithFormat: @"%d",
3238 [[classManager allActionsForClassNamed: className] count]];
3239 }
3240
3241 }
3242 return @"";
3243 }
3244
3245 - (void) outlineView: (NSOutlineView *)anOutlineView
3246 setObjectValue: (id)anObject
3247 forTableColumn: (NSTableColumn *)aTableColumn
3248 byItem: (id)item
3249 {
3250 GormOutlineView *gov = (GormOutlineView *)anOutlineView;
3251
3252 if ([item isKindOfClass: [GormOutletActionHolder class]])
3253 {
3254 if (![anObject isEqualToString: @""])
3255 {
3256 NSString *name = [item getName];
3257
3258 // retain the name and add the action/outlet...
3259 RETAIN(name);
3260 if ([gov editType] == Actions)
3261 {
3262 NSString *formattedAction = [self _formatAction: anObject];
3263 if (![classManager isAction: formattedAction
3264 ofClass: [gov itemBeingEdited]])
3265 {
3266 BOOL removed;
3267
3268 removed = [self removeConnectionsWithLabel: name
3269 forClassNamed: [gov itemBeingEdited] isAction: YES];
3270 if (removed)
3271 {
3272 [classManager replaceAction: name
3273 withAction: formattedAction
3274 forClassNamed: [gov itemBeingEdited]];
3275 [(GormOutletActionHolder *)item setName: formattedAction];
3276 }
3277 }
3278 else
3279 {
3280 NSString *message;
3281
3282 message = [NSString stringWithFormat:
3283 _(@"The class %@ already has an action named %@"),
3284 [gov itemBeingEdited], formattedAction];
3285
3286 NSRunAlertPanel(_(@"Problem Adding Action"),
3287 message, nil, nil, nil);
3288
3289 }
3290 }
3291 else if ([gov editType] == Outlets)
3292 {
3293 NSString *formattedOutlet = [self _formatOutlet: anObject];
3294
3295 if (![classManager isOutlet: formattedOutlet
3296 ofClass: [gov itemBeingEdited]])
3297 {
3298 BOOL removed;
3299
3300 removed = [self removeConnectionsWithLabel: name
3301 forClassNamed: [gov itemBeingEdited] isAction: NO];
3302 if (removed)
3303 {
3304 [classManager replaceOutlet: name
3305 withOutlet: formattedOutlet
3306 forClassNamed: [gov itemBeingEdited]];
3307 [(GormOutletActionHolder *)item setName: formattedOutlet];
3308 }
3309 }
3310 else
3311 {
3312 NSString *message;
3313
3314 message = [NSString stringWithFormat:
3315 _(@"The class %@ already has an outlet named %@"),
3316 [gov itemBeingEdited], formattedOutlet];
3317 NSRunAlertPanel(_(@"Problem Adding Outlet"),
3318 message, nil, nil, nil);
3319
3320 }
3321 }
3322 }
3323 }
3324 else
3325 {
3326 if ( ( ![anObject isEqualToString: @""] ) && ( ! [anObject isEqualToString:item] ) )
3327 {
3328 BOOL rename;
3329
3330 rename = [self renameConnectionsForClassNamed: item toName: anObject];
3331 if (rename)
3332 {
3333 int row = 0;
3334
3335 RETAIN(item); // retain the new name
3336 [classManager renameClassNamed: item newName: anObject];
3337 [gov reloadData];
3338 row = [gov rowForItem: anObject];
3339 [gov scrollRowToVisible: row];
3340 }
3341 }
3342 }
3343
3344 [gov setNeedsDisplay: YES];
3345 }
3346
3347 - (int) outlineView: (NSOutlineView *)anOutlineView
3348 numberOfChildrenOfItem: (id)item
3349 {
3350 if (item == nil)
3351 {
3352 return 1;
3353 }
3354 else
3355 {
3356 NSArray *subclasses = [classManager subClassesOf: item];
3357 return [subclasses count];
3358 }
3359
3360 return 0;
3361 }
3362
3363 - (BOOL) outlineView: (NSOutlineView *)anOutlineView
3364 isItemExpandable: (id)item
3365 {
3366 NSArray *subclasses = nil;
3367 if (item == nil)
3368 return YES;
3369
3370 subclasses = [classManager subClassesOf: item];
3371 if ([subclasses count] > 0)
3372 return YES;
3373
3374 return NO;
3375 }
3376
3377 - (id) outlineView: (NSOutlineView *)anOutlineView
3378 child: (int)index
3379 ofItem: (id)item
3380 {
3381 if (item == nil && index == 0)
3382 {
3383 return @"NSObject";
3384 }
3385 else
3386 {
3387 NSArray *subclasses = [classManager subClassesOf: item];
3388 return [subclasses objectAtIndex: index];
3389 }
3390
3391 return nil;
3392 }
3393
3394 // GormOutlineView data source methods...
3395 - (NSArray *)outlineView: (NSOutlineView *)anOutlineView
3396 actionsForItem: (id)item
3397 {
3398 NSArray *actions = [classManager allActionsForClassNamed: item];
3399 return actions;
3400 }
3401
3402 - (NSArray *)outlineView: (NSOutlineView *)anOutlineView
3403 outletsForItem: (id)item
3404 {
3405 NSArray *outlets = [classManager allOutletsForClassNamed: item];
3406 return outlets;
3407 }
3408
3409 - (NSString *)outlineView: (NSOutlineView *)anOutlineView
3410 addNewActionForClass: (id)item
3411 {
3412 GormOutlineView *gov = (GormOutlineView *)anOutlineView;
3413 if (![classManager isCustomClass: [gov itemBeingEdited]])
3414 {
3415 return nil;
3416 }
3417
3418 return [classManager addNewActionToClassNamed: item];
3419 }
3420
3421 - (NSString *)outlineView: (NSOutlineView *)anOutlineView
3422 addNewOutletForClass: (id)item
3423 {
3424 GormOutlineView *gov = (GormOutlineView *)anOutlineView;
3425 if (![classManager isCustomClass: [gov itemBeingEdited]])
3426 {
3427 return nil;
3428 }
3429
3430 if([item isEqualToString: @"FirstResponder"])
3431 return nil;
3432
3433 return [classManager addNewOutletToClassNamed: item];
3434 }
3435
3436 // Delegate methods
3437 - (BOOL) outlineView: (NSOutlineView *)outlineView
3438 shouldEditTableColumn: (NSTableColumn *)tableColumn
3439 item: (id)item
3440 {
3441 BOOL result = NO;
3442 GormOutlineView *gov = (GormOutlineView *)outlineView;
3443
3444 NSDebugLog(@"in the delegate %@", [tableColumn identifier]);
3445 if (tableColumn == [gov outlineTableColumn])
3446 {
3447 NSDebugLog(@"outline table col");
3448 if (![item isKindOfClass: [GormOutletActionHolder class]])
3449 {
3450 result = [classManager isCustomClass: item];
3451 [self editClass: item];
3452 }
3453 else
3454 {
3455 id itemBeingEdited = [gov itemBeingEdited];
3456 if ([classManager isCustomClass: itemBeingEdited])
3457 {
3458 if ([gov editType] == Actions)
3459 {
3460 result = [classManager isAction: [item getName]
3461 ofClass: itemBeingEdited];
3462 }
3463 else if ([gov editType] == Outlets)
3464 {
3465 result = [classManager isOutlet: [item getName]
3466 ofClass: itemBeingEdited];
3467 }
3468 }
3469 }
3470 }
3471
3472 return result;
3473 }
3474
3475 - (void) outlineViewSelectionDidChange: (NSNotification *)notification
3476 {
3477 id object = [notification object];
3478 int row = [object selectedRow];
3479
3480 if(row != -1)
3481 {
3482 id item = [object itemAtRow: [object selectedRow]];
3483 if (![item isKindOfClass: [GormOutletActionHolder class]])
3484 {
3485 [self editClass: item];
3486 }
3487 }
3488 }
3489
3490 // for debuging purpose
3491 - (void) printAllEditors
3492 {
3493 NSMutableSet *set = [NSMutableSet setWithCapacity: 16];
3494 NSEnumerator *enumerator = [connections objectEnumerator];
3495 id<IBConnectors> c;
3496
3497 while ((c = [enumerator nextObject]) != nil)
3498 {
3499 if ([GormObjectToEditor class] == [c class])
3500 {
3501 [set addObject: [c destination]];
3502 }
3503 }
3504
3505 NSLog(@"all editors %@", set);
3506 }
3507
3508 // sound support...
3509 - (id) openSound: (id)sender
3510 {
3511 NSArray *fileTypes = [NSSound soundUnfilteredFileTypes];
3512 NSArray *filenames;
3513 NSString *filename;
3514 NSOpenPanel *oPanel = [NSOpenPanel openPanel];
3515 int result;
3516 int i;
3517
3518 [oPanel setAllowsMultipleSelection: YES];
3519 [oPanel setCanChooseFiles: YES];
3520 [oPanel setCanChooseDirectories: NO];
3521 result = [oPanel runModalForDirectory: nil
3522 file: nil
3523 types: fileTypes];
3524 if (result == NSOKButton)
3525 {
3526 filenames = [oPanel filenames];
3527 for (i=0; i<[filenames count]; i++)
3528 {
3529 filename = [filenames objectAtIndex:i];
3530 NSDebugLog(@"Loading sound file: %@",filenames);
3531 [soundsView addObject: [self _createSoundPlaceHolder: filename]];
3532 [sounds addObject: filename];
3533 }
3534 return self;
3535 }
3536
3537 return nil;
3538 }
3539
3540 // image support...
3541 - (id) openImage: (id)sender
3542 {
3543 NSArray *fileTypes = [NSImage imageFileTypes];
3544 NSArray *filenames;
3545 NSOpenPanel *oPanel = [NSOpenPanel openPanel];
3546 NSString *filename;
3547 int result;
3548 int i;
3549
3550 [oPanel setAllowsMultipleSelection: YES];
3551 [oPanel setCanChooseFiles: YES];
3552 [oPanel setCanChooseDirectories: NO];
3553 result = [oPanel runModalForDirectory: nil
3554 file: nil
3555 types: fileTypes];
3556 if (result == NSOKButton)
3557 {
3558 filenames = [oPanel filenames];
3559 for (i=0; i<[filenames count]; i++)
3560 {
3561 filename = [filenames objectAtIndex:i];
3562 NSDebugLog(@"Loading image file: %@",filename);
3563 [imagesView addObject: [self _createImagePlaceHolder: filename]];
3564 [images addObject: filename];
3565 }
3566 return self;
3567 }
3568
3569 return nil;
3570 }
3571
3572 - (void) addImage: (NSString*) path
3573 {
3574 [images addObject: path];
3575 }
3576 @end
3577
3578 @implementation GormDocument (MenuValidation)
3579 - (BOOL) isEditingObjects
3580 {
3581 return ([selectionBox contentView] == scrollView);
3582 }
3583
3584 - (BOOL) isEditingImages
3585 {
3586 return ([selectionBox contentView] == imagesScrollView);
3587 }
3588
3589 - (BOOL) isEditingSounds
3590 {
3591 return ([selectionBox contentView] == soundsScrollView);
3592 }
3593
3594 - (BOOL) isEditingClasses
3595 {
3596 return ([selectionBox contentView] == classesScrollView);
3597 }
3598 @end

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26