PageRenderTime 100ms CodeModel.GetById 17ms app.highlight 76ms RepoModel.GetById 2ms app.codeStats 0ms

/core/externals/update-engine/externals/google-toolbox-for-mac/UnitTesting/GTMAppKit+UnitTesting.m

http://macfuse.googlecode.com/
Objective C | 583 lines | 334 code | 85 blank | 164 comment | 21 complexity | 61cdd799ac722957b60ef51b6fd7fed5 MD5 | raw file
  1//
  2//  GTMAppKit+UnitTesting.m
  3//
  4//  Categories for making unit testing of graphics/UI easier.
  5//
  6//  Copyright 2006-2008 Google Inc.
  7//
  8//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  9//  use this file except in compliance with the License.  You may obtain a copy
 10//  of the License at
 11//
 12//  http://www.apache.org/licenses/LICENSE-2.0
 13//
 14//  Unless required by applicable law or agreed to in writing, software
 15//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 16//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 17//  License for the specific language governing permissions and limitations under
 18//  the License.
 19//
 20
 21#import "GTMDefines.h"
 22#import "GTMAppKit+UnitTesting.h"
 23#import "GTMGeometryUtils.h"
 24#import "GTMMethodCheck.h"
 25
 26#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
 27 #define ENCODE_NSINTEGER(coder, i, key) [(coder) encodeInt:(i) forKey:(key)]
 28#else
 29 #define ENCODE_NSINTEGER(coder, i, key) [(coder) encodeInteger:(i) forKey:(key)]
 30#endif
 31
 32@implementation NSApplication (GTMUnitTestingAdditions)
 33GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:);
 34
 35- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
 36  [super gtm_unitTestEncodeState:inCoder];
 37  ENCODE_NSINTEGER(inCoder, [[self mainWindow] windowNumber], @"ApplicationMainWindow");
 38
 39  // Descend down into the windows allowing them to store their state
 40  NSWindow *window = nil;
 41  int i = 0;
 42  GTM_FOREACH_OBJECT(window, [self windows]) {
 43    if ([window isVisible]) {
 44      // Only record visible windows because invisible windows may be closing on us
 45      // This appears to happen differently in 64 bit vs 32 bit, and items
 46      // in the window may hold an extra retain count for a while until the
 47      // event loop is spun. To avoid all this, we just don't record non
 48      // visible windows.
 49      // See rdar://5851458 for details.
 50      [inCoder encodeObject:window forKey:[NSString stringWithFormat:@"Window %d", i]];
 51      i = i + 1;
 52    }
 53  }
 54
 55  // and encode the menu bar
 56  NSMenu *mainMenu = [self mainMenu];
 57  if (mainMenu) {
 58    [inCoder encodeObject:mainMenu forKey:@"MenuBar"];
 59  }
 60}
 61@end
 62
 63@implementation NSWindow (GTMUnitTestingAdditions)
 64
 65- (CGImageRef)gtm_unitTestImage {
 66  return [[[self contentView] superview] gtm_unitTestImage];
 67}
 68
 69- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
 70  [super gtm_unitTestEncodeState:inCoder];
 71  [inCoder encodeObject:[self title] forKey:@"WindowTitle"];
 72  [inCoder encodeBool:[self isVisible] forKey:@"WindowIsVisible"];
 73  // Do not record if window is key, because users running unit tests
 74  // and clicking around to other apps, could change this mid test causing
 75  // issues.
 76  // [inCoder encodeBool:[self isKeyWindow] forKey:@"WindowIsKey"];
 77  [inCoder encodeBool:[self isMainWindow] forKey:@"WindowIsMain"];
 78  [inCoder encodeObject:[self contentView] forKey:@"WindowContent"];
 79  if ([self toolbar]) {
 80    [inCoder encodeObject:[self toolbar] forKey:@"WindowToolbar"];
 81  }
 82}
 83
 84@end
 85
 86@implementation NSControl (GTMUnitTestingAdditions)
 87
 88//  Encodes the state of an object in a manner suitable for comparing
 89//  against a master state file so we can determine whether the
 90//  object is in a suitable state.
 91//
 92//  Arguments:
 93//    inCoder - the coder to encode our state into
 94- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
 95  [super gtm_unitTestEncodeState:inCoder];
 96  [inCoder encodeObject:[self class] forKey:@"ControlType"];
 97  [inCoder encodeObject:[self objectValue] forKey:@"ControlValue"];
 98  [inCoder encodeObject:[self selectedCell] forKey:@"ControlSelectedCell"];
 99  ENCODE_NSINTEGER(inCoder, [self tag], @"ControlTag");
100  [inCoder encodeBool:[self isEnabled] forKey:@"ControlIsEnabled"];
101}
102
103@end
104
105@implementation NSButton (GTMUnitTestingAdditions)
106
107//  Encodes the state of an object in a manner suitable for comparing
108//  against a master state file so we can determine whether the
109//  object is in a suitable state.
110//
111//  Arguments:
112//    inCoder - the coder to encode our state into
113- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
114  [super gtm_unitTestEncodeState:inCoder];
115  NSString *alternateTitle = [self alternateTitle];
116  if (alternateTitle) {
117    [inCoder encodeObject:alternateTitle forKey:@"ButtonAlternateTitle"];
118  }
119}
120
121@end
122
123@implementation NSTextField (GTMUnitTestingAdditions)
124
125- (BOOL)gtm_shouldEncodeStateForSubviews {
126  return NO;
127}
128
129//  Encodes the state of an object in a manner suitable for comparing
130//  against a master state file so we can determine whether the
131//  object is in a suitable state.
132//
133//  Arguments:
134//    inCoder - the coder to encode our state into
135- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
136  [super gtm_unitTestEncodeState:inCoder];
137  id controlCell = [self cell];
138  if ([controlCell isKindOfClass:[NSTextFieldCell class]]) {
139    NSTextFieldCell *textFieldCell = controlCell;
140    [inCoder encodeObject:[textFieldCell placeholderString]
141                   forKey:@"PlaceHolderString"];
142  }
143}
144
145@end
146
147@implementation NSCell (GTMUnitTestingAdditions)
148
149//  Encodes the state of an object in a manner suitable for comparing
150//  against a master state file so we can determine whether the
151//  object is in a suitable state.
152//
153//  Arguments:
154//    inCoder - the coder to encode our state into
155- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
156  [super gtm_unitTestEncodeState:inCoder];
157  BOOL isImageCell = NO;
158  if ([self hasValidObjectValue]) {
159    id val = [self objectValue];
160    [inCoder encodeObject:val forKey:@"CellValue"];
161    isImageCell = [val isKindOfClass:[NSImage class]];
162  }
163  if (!isImageCell) {
164    // Image cells have a title that includes addresses that aren't going
165    // to be constant, so we don't encode them. All the info we need
166    // is going to be in the CellValue encoding.
167    [inCoder encodeObject:[self title] forKey:@"CellTitle"];
168  }
169  ENCODE_NSINTEGER(inCoder, [self state], @"CellState");
170  ENCODE_NSINTEGER(inCoder, [self tag], @"CellTag");
171}
172
173@end
174
175@implementation NSImage (GTMUnitTestingAdditions)
176
177- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
178  [super gtm_unitTestEncodeState:inCoder];
179  [inCoder encodeObject:NSStringFromSize([self size]) forKey:@"ImageSize"];
180  [inCoder encodeObject:[self name] forKey:@"ImageName"];
181}
182
183- (CGImageRef)gtm_unitTestImage {
184  // Create up a context
185  NSSize size = [self size];
186  NSRect rect = GTMNSRectOfSize(size);
187  CGSize cgSize = GTMNSSizeToCGSize(size);
188  CGContextRef contextRef = GTMCreateUnitTestBitmapContextOfSizeWithData(cgSize,
189                                                                         NULL);
190  NSGraphicsContext *bitmapContext
191    = [NSGraphicsContext graphicsContextWithGraphicsPort:contextRef flipped:NO];
192  _GTMDevAssert(bitmapContext, @"Couldn't create ns bitmap context");
193
194  [NSGraphicsContext saveGraphicsState];
195  [NSGraphicsContext setCurrentContext:bitmapContext];
196  [self drawInRect:rect fromRect:rect operation:NSCompositeCopy fraction:1.0];
197
198  CGImageRef image = CGBitmapContextCreateImage(contextRef);
199  CFRelease(contextRef);
200  [NSGraphicsContext restoreGraphicsState];
201  return (CGImageRef)GTMCFAutorelease(image);
202}
203
204@end
205
206@implementation NSMenu (GTMUnitTestingAdditions)
207
208//  Encodes the state of an object in a manner suitable for comparing
209//  against a master state file so we can determine whether the
210//  object is in a suitable state.
211//
212//  Arguments:
213//    inCoder - the coder to encode our state into
214- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
215  [super gtm_unitTestEncodeState:inCoder];
216  // Hack here to work around
217  // rdar://5881796 Application menu item title wrong when accessed programatically
218  // which causes us to have different results on x86_64 vs x386.
219  // Hack is braced intentionally. We don't record the title of the
220  // "application" menu or it's menu title because they are wrong on 32 bit.
221  // They appear to work right on 64bit.
222  {
223    NSMenu *mainMenu = [NSApp mainMenu];
224    NSMenu *appleMenu = [[mainMenu itemAtIndex:0] submenu];
225    if (![self isEqual:appleMenu]) {
226      [inCoder encodeObject:[self title] forKey:@"MenuTitle"];
227    }
228  }
229  // Descend down into the menuitems allowing them to store their state
230  NSMenuItem *menuItem = nil;
231  int i = 0;
232  GTM_FOREACH_OBJECT(menuItem, [self itemArray]) {
233    [inCoder encodeObject:menuItem
234                   forKey:[NSString stringWithFormat:@"MenuItem %d", i]];
235    ++i;
236  }
237}
238
239@end
240
241@implementation NSMenuItem (GTMUnitTestingAdditions)
242
243- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
244  [super gtm_unitTestEncodeState:inCoder];
245  // Hack here to work around
246  // rdar://5881796 Application menu item title wrong when accessed programatically
247  // which causes us to have different results on x86_64 vs x386.
248  // See comment above.
249  {
250    NSMenu *mainMenu = [NSApp mainMenu];
251    NSMenuItem *appleMenuItem = [mainMenu itemAtIndex:0];
252    if (![self isEqual:appleMenuItem]) {
253      [inCoder encodeObject:[self title] forKey:@"MenuItemTitle"];
254    }
255  }
256  [inCoder encodeObject:[self keyEquivalent] forKey:@"MenuItemKeyEquivalent"];
257  [inCoder encodeBool:[self isSeparatorItem] forKey:@"MenuItemIsSeparator"];
258  ENCODE_NSINTEGER(inCoder, [self state], @"MenuItemState");
259  [inCoder encodeBool:[self isEnabled] forKey:@"MenuItemIsEnabled"];
260  [inCoder encodeBool:[self isAlternate] forKey:@"MenuItemIsAlternate"];
261  [inCoder encodeObject:[self toolTip] forKey:@"MenuItemTooltip"];
262  ENCODE_NSINTEGER(inCoder, [self tag], @"MenuItemTag");
263  ENCODE_NSINTEGER(inCoder, [self indentationLevel], @"MenuItemIndentationLevel");
264
265  // Do our submenu if neccessary
266  if ([self hasSubmenu]) {
267    [inCoder encodeObject:[self submenu] forKey:@"MenuItemSubmenu"];
268  }
269}
270
271@end
272
273@implementation NSTabView (GTMUnitTestingAdditions)
274
275//  Encodes the state of an object in a manner suitable for comparing
276//  against a master state file so we can determine whether the
277//  object is in a suitable state.
278//
279//  Arguments:
280//    inCoder - the coder to encode our state into
281- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
282  [super gtm_unitTestEncodeState:inCoder];
283  NSTabViewItem *tab = nil;
284  int i = 0;
285  GTM_FOREACH_OBJECT(tab, [self tabViewItems]) {
286    NSString *key = [NSString stringWithFormat:@"TabItem %d", i];
287    [inCoder encodeObject:tab forKey:key];
288    i = i + 1;
289  }
290}
291
292@end
293
294@implementation NSTabViewItem (GTMUnitTestingAdditions)
295
296//  Encodes the state of an object in a manner suitable for comparing
297//  against a master state file so we can determine whether the
298//  object is in a suitable state.
299//
300//  Arguments:
301//    inCoder - the coder to encode our state into
302- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
303  [super gtm_unitTestEncodeState:inCoder];
304  [inCoder encodeObject:[self label] forKey:@"TabLabel"];
305  [inCoder encodeObject:[self view] forKey:@"TabView"];
306}
307
308@end
309
310@implementation NSToolbar (GTMUnitTestingAdditions)
311
312//  Encodes the state of an object in a manner suitable for comparing
313//  against a master state file so we can determine whether the
314//  object is in a suitable state.
315//
316//  Arguments:
317//    inCoder - the coder to encode our state into
318- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
319  [super gtm_unitTestEncodeState:inCoder];
320  NSToolbarItem *item = nil;
321  NSUInteger i = 0;
322  GTM_FOREACH_OBJECT(item, [self items]) {
323    NSString *key
324      = [NSString stringWithFormat:@"ToolbarItem %lu", (unsigned long)i];
325    [inCoder encodeObject:item forKey:key];
326    i = i + 1;
327  }
328}
329
330@end
331
332@implementation NSToolbarItem (GTMUnitTestingAdditions)
333
334//  Encodes the state of an object in a manner suitable for comparing
335//  against a master state file so we can determine whether the
336//  object is in a suitable state.
337//
338//  Arguments:
339//    inCoder - the coder to encode our state into
340- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
341  [super gtm_unitTestEncodeState:inCoder];
342  [inCoder encodeObject:[self label] forKey:@"Label"];
343  [inCoder encodeObject:[self paletteLabel] forKey:@"PaletteLabel"];
344  [inCoder encodeObject:[self toolTip] forKey:@"ToolTip"];
345  NSView *view = [self view];
346  if (view) {
347    [inCoder encodeObject:view forKey:@"View"];
348  }
349}
350
351@end
352
353@implementation NSMatrix (GTMUnitTestingAdditions)
354
355//  Encodes the state of an object in a manner suitable for comparing
356//  against a master state file so we can determine whether the
357//  object is in a suitable state.
358//
359//  Arguments:
360//    inCoder - the coder to encode our state into
361- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
362  [super gtm_unitTestEncodeState:inCoder];
363
364  ENCODE_NSINTEGER(inCoder, [self mode], @"MatrixMode");
365  ENCODE_NSINTEGER(inCoder, [self numberOfRows], @"MatrixRowCount");
366  ENCODE_NSINTEGER(inCoder, [self numberOfColumns], @"MatrixColumnCount");
367  [inCoder encodeBool:[self allowsEmptySelection]
368               forKey:@"MatrixAllowEmptySelection"];
369  [inCoder encodeBool:[self isSelectionByRect] forKey:@"MatrixSelectionByRect"];
370  [inCoder encodeBool:[self autosizesCells] forKey:@"MatrixAutosizesCells"];
371  [inCoder encodeSize:[self intercellSpacing] forKey:@"MatrixIntercellSpacing"];
372
373  [inCoder encodeObject:[self prototype] forKey:@"MatrixCellPrototype"];
374
375  // Dump the list of cells
376  NSCell *cell;
377  long i = 0;
378  GTM_FOREACH_OBJECT(cell, [self cells]) {
379    [inCoder encodeObject:cell
380                   forKey:[NSString stringWithFormat:@"MatrixCell %ld", i]];
381    ++i;
382  }
383}
384
385@end
386
387@implementation NSBox (GTMUnitTestingAdditions)
388
389//  Encodes the state of an object in a manner suitable for comparing
390//  against a master state file so we can determine whether the
391//  object is in a suitable state.
392//
393//  Arguments:
394//    inCoder - the coder to encode our state into
395- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
396  [super gtm_unitTestEncodeState:inCoder];
397
398  [inCoder encodeObject:[self title] forKey:@"BoxTitle"];
399  ENCODE_NSINTEGER(inCoder, [self titlePosition], @"BoxTitlePosition");
400  ENCODE_NSINTEGER(inCoder, [self boxType], @"BoxType");
401  ENCODE_NSINTEGER(inCoder, [self borderType], @"BoxBorderType");
402  // 10.5+ [inCoder encodeBool:[self isTransparent] forKey:@"BoxIsTransparent"];
403}
404
405@end
406
407@implementation NSSegmentedControl (GTMUnitTestingAdditions)
408
409//  Encodes the state of an NSSegmentedControl and all its segments.
410//
411//  Arguments:
412//    inCoder - the coder to encode state into
413- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
414  [super gtm_unitTestEncodeState:inCoder];
415
416  NSInteger segmentCount = [self segmentCount];
417  ENCODE_NSINTEGER(inCoder, segmentCount, @"SegmentCount");
418
419  for (NSInteger i = 0; i < segmentCount; ++i) {
420    NSString *key = [NSString stringWithFormat:@"Segment %ld", (long)i];
421    [inCoder encodeObject:[self labelForSegment:i] forKey:key];
422  }
423}
424
425@end
426
427@implementation NSComboBox (GTMUnitTestingAdditions)
428
429- (BOOL)gtm_shouldEncodeStateForSubviews {
430  // Subclass of NSTextView, don't want subviews for the same reason.
431  return NO;
432}
433
434//  Encodes the state of an NSSegmentedControl and all its segments.
435//
436//  Arguments:
437//    inCoder - the coder to encode state into
438- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
439  [super gtm_unitTestEncodeState:inCoder];
440
441  NSInteger aCount = [self numberOfItems];
442  ENCODE_NSINTEGER(inCoder, aCount, @"ComboBoxNumberOfItems");
443  aCount = [self numberOfVisibleItems];
444  ENCODE_NSINTEGER(inCoder, aCount, @"ComboBoxNumberOfVisibleItems");
445
446  // Include the objectValues if it doesn't use a data source.
447  if (![self usesDataSource]) {
448    NSArray *objectValues = [self objectValues];
449    for (NSUInteger i = 0; i < [objectValues count]; ++i) {
450      id value = [objectValues objectAtIndex:i];
451      if ([value isKindOfClass:[NSString class]]) {
452        NSString *key = [NSString stringWithFormat:@"ComboBoxObjectValue %lu",
453                         (unsigned long)i];
454        [inCoder encodeObject:value forKey:key];
455      }
456    }
457  }
458}
459
460@end
461
462
463//  A view that allows you to delegate out drawing using the formal
464//  GTMUnitTestViewDelegate protocol above. This is useful when writing up unit
465//  tests for visual elements.
466//  Your test will often end up looking like this:
467//  - (void)testFoo {
468//   GTMAssertDrawingEqualToFile(self, NSMakeSize(200, 200), @"Foo", nil, nil);
469//  }
470//  and your testSuite will also implement the unitTestViewDrawRect method to do
471//  it's actual drawing. The above creates a view of size 200x200 that draws
472//  it's content using |self|'s unitTestViewDrawRect method and compares it to
473//  the contents of the file Foo.tif to make sure it's valid
474@implementation GTMUnitTestView
475
476- (id)initWithFrame:(NSRect)frame
477             drawer:(id<GTMUnitTestViewDrawer>)drawer
478        contextInfo:(void*)contextInfo {
479  self = [super initWithFrame:frame];
480  if (self != nil) {
481    drawer_ = [drawer retain];
482    contextInfo_ = contextInfo;
483  }
484  return self;
485}
486
487- (void)dealloc {
488  [drawer_ release];
489  [super dealloc];
490}
491
492
493- (void)drawRect:(NSRect)rect {
494  [drawer_ gtm_unitTestViewDrawRect:rect contextInfo:contextInfo_];
495}
496
497
498@end
499
500@implementation NSView (GTMUnitTestingAdditions)
501
502//  Returns an image containing a representation of the object
503//  suitable for use in comparing against a master image.
504//  Does all of it's drawing with smoothfonts and antialiasing off
505//  to avoid issues with font smoothing settings and antialias differences
506//  between ppc and x86.
507//
508//  Returns:
509//    an image of the object
510- (CGImageRef)gtm_unitTestImage {
511  // Create up a context
512  NSRect bounds = [self bounds];
513  CGSize cgSize = GTMNSSizeToCGSize(bounds.size);
514  CGContextRef contextRef = GTMCreateUnitTestBitmapContextOfSizeWithData(cgSize,
515                                                                         NULL);
516  NSGraphicsContext *bitmapContext
517    = [NSGraphicsContext graphicsContextWithGraphicsPort:contextRef flipped:NO];
518  _GTMDevAssert(bitmapContext, @"Couldn't create ns bitmap context");
519
520  // Save our state and turn off font smoothing and antialias.
521  CGContextSaveGState(contextRef);
522  CGContextSetShouldSmoothFonts(contextRef, false);
523  CGContextSetShouldAntialias(contextRef, false);
524  [self displayRectIgnoringOpacity:bounds inContext:bitmapContext];
525
526  CGImageRef image = CGBitmapContextCreateImage(contextRef);
527  CFRelease(contextRef);
528  return (CGImageRef)GTMCFAutorelease(image);
529}
530
531//  Returns whether gtm_unitTestEncodeState should recurse into subviews
532//  of a particular view.
533//  If you have "Full keyboard access" in the
534//  Keyboard & Mouse > Keyboard Shortcuts preferences pane set to "Text boxes
535//  and Lists only" that Apple adds a set of subviews to NSTextFields. So in the
536//  case of NSTextFields we don't want to recurse into their subviews. There may
537//  be other cases like this, so instead of specializing gtm_unitTestEncodeState: to
538//  look for NSTextFields, NSTextFields will just not allow us to recurse into
539//  their subviews.
540//
541//  Returns:
542//    should gtm_unitTestEncodeState pick up subview state.
543- (BOOL)gtm_shouldEncodeStateForSubviews {
544  return YES;
545}
546
547//  Encodes the state of an object in a manner suitable for comparing
548//  against a master state file so we can determine whether the
549//  object is in a suitable state.
550//
551//  Arguments:
552//    inCoder - the coder to encode our state into
553- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
554  [super gtm_unitTestEncodeState:inCoder];
555  [inCoder encodeBool:[self isHidden] forKey:@"ViewIsHidden"];
556  [inCoder encodeObject:[self toolTip] forKey:@"ViewToolTip"];
557  NSArray *supportedAttrs = [self accessibilityAttributeNames];
558  if ([supportedAttrs containsObject:NSAccessibilityHelpAttribute]) {
559    NSString *help
560      = [self accessibilityAttributeValue:NSAccessibilityHelpAttribute];
561    [inCoder encodeObject:help forKey:@"ViewAccessibilityHelp"];
562  }
563  if ([supportedAttrs containsObject:NSAccessibilityDescriptionAttribute]) {
564    NSString *description
565      = [self accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
566    [inCoder encodeObject:description forKey:@"ViewAccessibilityDescription"];
567  }
568  NSMenu *menu = [self menu];
569  if (menu) {
570    [inCoder encodeObject:menu forKey:@"ViewMenu"];
571  }
572  if ([self gtm_shouldEncodeStateForSubviews]) {
573    NSView *subview = nil;
574    int i = 0;
575    GTM_FOREACH_OBJECT(subview, [self subviews]) {
576      [inCoder encodeObject:subview forKey:[NSString stringWithFormat:@"ViewSubView %d", i]];
577      i = i + 1;
578    }
579  }
580}
581
582@end
583