/[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.155 - (show annotations) (download)
Sun Aug 31 03:46:46 2003 UTC (20 years, 8 months ago) by gcasa
Branch: MAIN
CVS Tags: Gorm-0_3_1
Changes since 1.154: +5 -0 lines
Override awakeWithContext: to prevent issues when loading .gorm files.

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

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