SKPreferenceController.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. //
  2. // SKPreferenceController.m
  3. // Skim
  4. //
  5. // Created by Christiaan Hofman on 2/10/07.
  6. /*
  7. This software is Copyright (c) 2007-2018
  8. Christiaan Hofman. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions
  11. are met:
  12. - Redistributions of source code must retain the above copyright
  13. notice, this list of conditions and the following disclaimer.
  14. - Redistributions in binary form must reproduce the above copyright
  15. notice, this list of conditions and the following disclaimer in
  16. the documentation and/or other materials provided with the
  17. distribution.
  18. - Neither the name of Christiaan Hofman nor the names of any
  19. contributors may be used to endorse or promote products derived
  20. from this software without specific prior written permission.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. #import "SKPreferenceController.h"
  34. //#import "SKGeneralPreferences.h"
  35. //#import "SKDisplayPreferences.h"
  36. //#import "SKNotesPreferences.h"
  37. //#import "SKSyncPreferences.h"
  38. //#import "NSUserDefaultsController_SKExtensions.h"
  39. //#import "SKFontWell.h"
  40. //#import "SKStringConstants.h"
  41. //#import "NSView_SKExtensions.h"
  42. //#import "NSGraphics_SKExtensions.h"
  43. //#import "NSGeometry_SKExtensions.h"
  44. //#import "NSAnimationContext_SKExtensions.h"
  45. //#import "DropboxPreferences.h"
  46. /*#import "KMOCRPreferences.h" //恢复OCR架上版本样式*/
  47. #define SKPreferencesToolbarIdentifier @"SKPreferencesToolbarIdentifier"
  48. #define SKPreferenceWindowFrameAutosaveName @"SKPreferenceWindow"
  49. #define SKLastSelectedPreferencePaneKey @"SKLastSelectedPreferencePane"
  50. #define NIBNAME_KEY @"nibName"
  51. #define BOTTOM_MARGIN 27.0
  52. #define INITIALUSERDEFAULTS_KEY @"InitialUserDefaults"
  53. #define RESETTABLEKEYS_KEY @"ResettableKeys"
  54. #import <PDF_Master-Swift.h>
  55. static inline NSRect SKShrinkRect(NSRect rect, CGFloat amount, NSRectEdge edge) {
  56. NSRect ignored;
  57. NSDivideRect(rect, &ignored, &rect, amount, edge);
  58. return rect;
  59. }
  60. @implementation SKPreferenceController
  61. //@synthesize resetButtons;
  62. static SKPreferenceController *sharedPrefenceController = nil;
  63. + (id)sharedPrefenceController {
  64. if (sharedPrefenceController == nil)
  65. [[self alloc] init];
  66. return sharedPrefenceController;
  67. }
  68. + (id)allocWithZone:(NSZone *)zone {
  69. return sharedPrefenceController ?: [super allocWithZone:zone];
  70. }
  71. - (id)init {
  72. if (sharedPrefenceController == nil) {
  73. self = [super initWithWindowNibName:@"PreferenceWindow"];
  74. if (self) {
  75. preferencePanes = [[NSArray alloc] initWithObjects:
  76. [[SKGeneralPreferences alloc] init],
  77. [[KMDisplayPreferences alloc] init],
  78. [[KMNotesPreferences alloc] init],
  79. // [[[SKSyncPreferences alloc] init] autorelease],
  80. // [[[DropboxPreferences alloc] init] autorelease],
  81. // [[[KMOCRPreferences alloc] init] autorelease], nil]; //恢复OCR架上版本样式
  82. // [[[DropboxPreferences alloc] init] autorelease],
  83. nil];
  84. history = [[NSMutableArray alloc] init];
  85. historyIndex = 0;
  86. }
  87. sharedPrefenceController = self;
  88. } else if (self != sharedPrefenceController) {
  89. NSLog(@"Attempt to allocate second instance of %@", [self class]);
  90. self = sharedPrefenceController;
  91. }
  92. return self;
  93. }
  94. - (void)dealloc {
  95. currentPane = nil;
  96. // SKDESTROY(preferencePanes);
  97. // SKDESTROY(resetButtons);
  98. // SKDESTROY(history);
  99. // [super dealloc];
  100. }
  101. - (NSViewController<SKPreferencePane> *)preferencePaneForItemIdentifier:(NSString *)itemIdent {
  102. [self panelControlSelectColor:itemIdent];
  103. for (NSViewController<SKPreferencePane> *pane in preferencePanes)
  104. if ([[pane nibName] isEqualToString:itemIdent])
  105. return pane;
  106. return nil;
  107. }
  108. - (void)selectPane:(NSViewController<SKPreferencePane> *)pane {
  109. if ([pane isEqual:currentPane] == NO) {
  110. if (pane) {
  111. historyIndex++;
  112. if ([history count] > historyIndex)
  113. [history removeObjectsInRange:NSMakeRange(historyIndex, [history count] - historyIndex)];
  114. [history addObject:pane];
  115. } else {
  116. pane = [history objectAtIndex:historyIndex];
  117. }
  118. NSWindow *window = [self window];
  119. NSView *contentView = [window contentView];
  120. NSView *oldView = [currentPane view];
  121. NSView *view = [pane view];
  122. NSRect frame = SKShrinkRect([window frame], NSHeight([contentView frame]) - NSMaxY([view frame]), NSMinYEdge);
  123. // make sure edits are committed
  124. [currentPane commitEditing];
  125. [[NSUserDefaultsController sharedUserDefaultsController] commitEditing];
  126. currentPane = pane;
  127. [window setTitle:[currentPane title]];
  128. [[NSUserDefaults standardUserDefaults] setObject:[currentPane nibName] forKey:SKLastSelectedPreferencePaneKey];
  129. [[window toolbar] setSelectedItemIdentifier:[currentPane nibName]];
  130. if ([[NSUserDefaults standardUserDefaults] boolForKey:SKStringConstants.disableAnimationsKey]) {
  131. [contentView replaceSubview:oldView with:view];
  132. [window setFrame:frame display:YES];
  133. } else {
  134. NSTimeInterval duration = [window animationResizeTime:frame];
  135. [contentView displayIfNeeded];
  136. [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
  137. [context setDuration:duration];
  138. [[contentView animator] replaceSubview:oldView with:view];
  139. [[window animator] setFrame:frame display:YES];
  140. }
  141. completionHandler:^{}];
  142. }
  143. }
  144. }
  145. - (void)windowDidLoad {
  146. if (@available(macOS 11.0,*)) {
  147. self.window.toolbarStyle = NSWindowToolbarStyleExpanded;
  148. }
  149. NSWindow *window = [self window];
  150. NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:SKPreferencesToolbarIdentifier];
  151. [toolbar setAllowsUserCustomization:NO];
  152. [toolbar setAutosavesConfiguration:NO];
  153. [toolbar setVisible:YES];
  154. [toolbar setDelegate:self];
  155. [window setToolbar:toolbar];
  156. [window setShowsToolbarButton:NO];
  157. [[window contentView] setWantsLayer:YES];
  158. // we want to restore the top of the window, while without the force it restores the bottom position without the size
  159. [window setFrameUsingName:SKPreferenceWindowFrameAutosaveName force:YES];
  160. [self setWindowFrameAutosaveName:SKPreferenceWindowFrameAutosaveName];
  161. // [NSGraphicsContext SKAutoSizeButtons:resetButtons rightAlign:false];
  162. CGFloat width = 0.0;
  163. NSRect frame;
  164. NSViewController<SKPreferencePane> *pane;
  165. NSView *view;
  166. for (pane in preferencePanes)
  167. width = fmax(width, NSWidth([[pane view] frame]));
  168. for (pane in preferencePanes) {
  169. view = [pane view];
  170. frame = [view frame];
  171. if (([view autoresizingMask] & NSViewWidthSizable))
  172. frame.size.width = width;
  173. else
  174. frame.origin.x = floor(0.5 * (width - NSWidth(frame)));
  175. frame.origin.y = BOTTOM_MARGIN;
  176. [view setFrame:frame];
  177. }
  178. currentPane = [self preferencePaneForItemIdentifier:[[NSUserDefaults standardUserDefaults] stringForKey:SKLastSelectedPreferencePaneKey]] ?: [preferencePanes objectAtIndex:0];
  179. [toolbar setSelectedItemIdentifier:[currentPane nibName]];
  180. // [window setTitle:[currentPane title]];
  181. // [history addObject:currentPane];
  182. view = [currentPane view];
  183. frame = [window frame];
  184. frame.size.width = width;
  185. frame = SKShrinkRect(frame, NSHeight([[window contentView] frame]) - NSMaxY([view frame]), NSMinYEdge);
  186. [window setFrame:frame display:NO];
  187. [[window contentView] addSubview:view];
  188. }
  189. - (void)windowDidResignMain:(NSNotification *)notification {
  190. [[[self window] contentView] deactivateWellSubcontrols];
  191. }
  192. - (void)windowWillClose:(NSNotification *)notification {
  193. // make sure edits are committed
  194. [currentPane commitEditing];
  195. [[NSUserDefaultsController sharedUserDefaultsController] commitEditing];
  196. }
  197. - (void)selectPaneWithIdentifier:(NSString *)itemIdentifier {
  198. [self selectPane:[self preferencePaneForItemIdentifier:itemIdentifier]];
  199. }
  200. - (void)panelControlSelectColor:(NSString *)identifier {
  201. for (NSToolbarItem *item in self.window.toolbar.items) {
  202. if ([item.itemIdentifier isEqualToString:identifier]) {
  203. if ([item.itemIdentifier isEqualToString:@"GeneralPreferences"]) {
  204. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsGeneralSel"];
  205. } else if ([item.itemIdentifier isEqualToString:@"DisplayPreferences"]){
  206. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsViewSel"];
  207. } else if ([item.itemIdentifier isEqualToString:@"NotesPreferences"]){
  208. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsAnnotationSel"];
  209. } else if ([item.itemIdentifier isEqualToString:@"SyncPreferences"]){
  210. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsSynchronizeSel"];
  211. } else if ([item.itemIdentifier isEqualToString:@"DropboxPreferences"]){
  212. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsDropboxSel"];
  213. }
  214. } else {
  215. if ([item.itemIdentifier isEqualToString:@"GeneralPreferences"]) {
  216. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsGeneralNor"];
  217. } else if ([item.itemIdentifier isEqualToString:@"DisplayPreferences"]){
  218. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsViewNor"];
  219. } else if ([item.itemIdentifier isEqualToString:@"NotesPreferences"]){
  220. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsAnnotationNor"];
  221. } else if ([item.itemIdentifier isEqualToString:@"SyncPreferences"]){
  222. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsSynchronizeNor"];
  223. } else if ([item.itemIdentifier isEqualToString:@"DropboxPreferences"]){
  224. item.image = [NSImage imageNamed:@"KMImageNameElseSettingsDropboxNor"];
  225. }
  226. }
  227. }
  228. }
  229. #pragma mark Actions
  230. - (void)selectPaneAction:(id)sender {
  231. [self panelControlSelectColor:[sender itemIdentifier]];
  232. [self selectPane:[self preferencePaneForItemIdentifier:[sender itemIdentifier]]];
  233. }
  234. - (void)resetAllSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
  235. if (returnCode == NSAlertFirstButtonReturn) {
  236. [[NSUserDefaultsController sharedUserDefaultsController] revertToInitialValues:nil];
  237. for (NSViewController<SKPreferencePane> *pane in preferencePanes) {
  238. if ([pane respondsToSelector:@selector(defaultsDidRevert)])
  239. [pane defaultsDidRevert];
  240. }
  241. //重置OCR后,记录切换值
  242. [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"KMStatusPlanPopUpBtnKey"];
  243. [[NSUserDefaults standardUserDefaults] synchronize];
  244. }
  245. }
  246. - (IBAction)resetAll:(id)sender {
  247. NSAlert *alert = [[NSAlert alloc] init];
  248. [alert setMessageText:NSLocalizedString(@"Reset all preferences to their original values?", @"Message in alert dialog when pressing Reset All button")];
  249. [alert setInformativeText:NSLocalizedString(@"Choosing Reset will restore all settings to the state they were in when PDF Reader Pro Edition was first installed.", @"Informative text in alert dialog when pressing Reset All button")];
  250. [alert addButtonWithTitle:NSLocalizedString(@"Reset", @"Button title")];
  251. [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Button title")];
  252. [alert beginSheetModalForWindow:[self window]
  253. modalDelegate:self
  254. didEndSelector:@selector(resetAllSheetDidEnd:returnCode:contextInfo:)
  255. contextInfo:NULL];
  256. }
  257. - (void)resetCurrentSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
  258. if (returnCode == NSAlertFirstButtonReturn) {
  259. NSURL *initialUserDefaultsURL = [[NSBundle mainBundle] URLForResource:INITIALUSERDEFAULTS_KEY withExtension:@"plist"];
  260. NSArray *resettableKeys = [[[NSDictionary dictionaryWithContentsOfURL:initialUserDefaultsURL] objectForKey:RESETTABLEKEYS_KEY] objectForKey:[currentPane nibName]];
  261. [[NSUserDefaultsController sharedUserDefaultsController] revertToInitialValuesForKeys:resettableKeys];
  262. if ([currentPane respondsToSelector:@selector(defaultsDidRevert)])
  263. [currentPane defaultsDidRevert];
  264. if ([[currentPane title] isEqualToString:@"OCR"]) { //重置OCR后,记录切换值
  265. [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"KMStatusPlanPopUpBtnKey"];
  266. [[NSUserDefaults standardUserDefaults] synchronize];
  267. }
  268. }
  269. }
  270. - (IBAction)resetCurrent:(id)sender {
  271. if (currentPane == nil) {
  272. NSBeep();
  273. return;
  274. }
  275. NSString *label = [currentPane title];
  276. NSAlert *alert = [[NSAlert alloc] init];
  277. [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Reset %@ preferences to their original values?", @"Message in alert dialog when pressing Reset All button"), label]];
  278. [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Choosing Reset will restore all settings in this pane to the state they were in when PDF Reader Pro Edition was first installed.", @"Informative text in alert dialog when pressing Reset All button"), label]];
  279. [alert addButtonWithTitle:NSLocalizedString(@"Reset", @"Button title")];
  280. [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Button title")];
  281. [alert beginSheetModalForWindow:[self window]
  282. modalDelegate:self
  283. didEndSelector:@selector(resetCurrentSheetDidEnd:returnCode:contextInfo:)
  284. contextInfo:NULL];
  285. }
  286. - (IBAction)doGoToNextPage:(id)sender {
  287. NSUInteger itemIndex = [preferencePanes indexOfObject:currentPane];
  288. if (itemIndex != NSNotFound && ++itemIndex < [preferencePanes count])
  289. [self selectPane:[preferencePanes objectAtIndex:itemIndex]];
  290. }
  291. - (IBAction)doGoToPreviousPage:(id)sender {
  292. NSUInteger itemIndex = [preferencePanes indexOfObject:currentPane];
  293. if (itemIndex != NSNotFound && itemIndex-- > 0)
  294. [self selectPane:[preferencePanes objectAtIndex:itemIndex]];
  295. }
  296. - (IBAction)doGoToFirstPage:(id)sender {
  297. [self selectPane:[preferencePanes objectAtIndex:0]];
  298. }
  299. - (IBAction)doGoToLastPage:(id)sender {
  300. [self selectPane:[preferencePanes lastObject]];
  301. }
  302. - (IBAction)doGoBack:(id)sender {
  303. if (historyIndex > 0) {
  304. historyIndex--;
  305. [self selectPane:nil];
  306. }
  307. }
  308. - (IBAction)doGoForward:(id)sender {
  309. if (historyIndex + 1 < [history count]) {
  310. historyIndex++;
  311. [self selectPane:nil];
  312. }
  313. }
  314. - (IBAction)changeFont:(id)sender {
  315. // [[[[self window] contentView] activeFontWell] changeFontFromFontManager:sender];
  316. }
  317. - (IBAction)changeAttributes:(id)sender {
  318. // [[[[self window] contentView] activeFontWell] changeAttributesFromFontManager:sender];
  319. }
  320. - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
  321. if ([menuItem action] == @selector(doGoToNextPage:) || [menuItem action] == @selector(doGoToLastPage:))
  322. return [currentPane isEqual:[preferencePanes lastObject]] == NO;
  323. else if ([menuItem action] == @selector(doGoToPreviousPage:) || [menuItem action] == @selector(doGoToFirstPage:))
  324. return [currentPane isEqual:[preferencePanes objectAtIndex:0]] == NO;
  325. else if ([menuItem action] == @selector(doGoBack:))
  326. return historyIndex > 0;
  327. else if ([menuItem action] == @selector(doGoForward:))
  328. return historyIndex + 1 < [history count];
  329. return YES;
  330. }
  331. #pragma mark Toolbar
  332. - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdent willBeInsertedIntoToolbar:(BOOL)willBeInserted {
  333. NSViewController<SKPreferencePane> *pane = [self preferencePaneForItemIdentifier:itemIdent];
  334. NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdent];
  335. [item setLabel:[pane title]];
  336. NSImage * ima = nil;
  337. if ([itemIdent isEqualToString:@"GeneralPreferences"]) {
  338. ima = [NSImage imageNamed:@"KMImageNameElseSettingsGeneralNor"];//generalPreferences
  339. } else if ([itemIdent isEqualToString:@"DisplayPreferences"]){
  340. ima = [NSImage imageNamed:@"KMImageNameElseSettingsViewNor"];//displayPreferences
  341. } else if ([itemIdent isEqualToString:@"NotesPreferences"]){
  342. ima = [NSImage imageNamed:@"KMImageNameElseSettingsAnnotationNor"];//notesPreferences
  343. } else if ([itemIdent isEqualToString:@"SyncPreferences"]){
  344. ima = [NSImage imageNamed:@"KMImageNameElseSettingsSynchronizeNor"];//syncPreferences
  345. } else if ([itemIdent isEqualToString:@"DropboxPreferences"]){
  346. ima = [NSImage imageNamed:@"KMImageNameElseSettingsDropboxNor"];//dropboxPreferences
  347. }
  348. //恢复OCR架上版本样式
  349. // else if ([itemIdent isEqualToString:@"KMOCRPreferences"]) {
  350. // ima = [NSImage imageNamed:@"ocrPreferences"];
  351. // }
  352. [item setImage:ima];
  353. [item setTarget:self];
  354. [item setAction:@selector(selectPaneAction:)];
  355. return item;
  356. }
  357. - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
  358. return [preferencePanes valueForKey:NIBNAME_KEY];
  359. }
  360. - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
  361. return [self toolbarDefaultItemIdentifiers:toolbar];
  362. }
  363. - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar {
  364. return [self toolbarDefaultItemIdentifiers:toolbar];
  365. }
  366. @end
  367. @implementation SKPreferenceWindow
  368. - (BOOL)respondsToSelector:(SEL)aSelector {
  369. return aSelector != @selector(toggleToolbarShow:) && aSelector != @selector(runToolbarCustomizationPalette:) && [super respondsToSelector:aSelector];
  370. }
  371. @end
  372. @implementation SKIBArray
  373. static void setObjectAtIndex(id __strong *object, id obj, NSUInteger i, unsigned long *mutationsPtr) {
  374. if (object[i] != obj) {
  375. // [object[i] release];
  376. object[i] = obj;
  377. (*mutationsPtr)++;
  378. }
  379. }
  380. #define SYNTHESIZE_OBJECT_ACCESSORS(i) \
  381. @dynamic object##i; \
  382. - (id)object##i { return object[i-1]; } \
  383. - (void)setObject##i:(id)obj { setObjectAtIndex(object, obj, i-1, &mutations); }
  384. SYNTHESIZE_OBJECT_ACCESSORS(1)
  385. SYNTHESIZE_OBJECT_ACCESSORS(2)
  386. SYNTHESIZE_OBJECT_ACCESSORS(3)
  387. SYNTHESIZE_OBJECT_ACCESSORS(4)
  388. SYNTHESIZE_OBJECT_ACCESSORS(5)
  389. SYNTHESIZE_OBJECT_ACCESSORS(6)
  390. SYNTHESIZE_OBJECT_ACCESSORS(7)
  391. SYNTHESIZE_OBJECT_ACCESSORS(8)
  392. SYNTHESIZE_OBJECT_ACCESSORS(9)
  393. - (void)dealloc {
  394. NSUInteger i;
  395. for (i = 0; i < 9; i++)
  396. setObjectAtIndex(object, nil, i, &mutations);
  397. // [super dealloc];
  398. }
  399. - (NSUInteger)count {
  400. NSUInteger i;
  401. for (i = 0; i < 9; i++)
  402. if (object[i] == nil) break;
  403. return i;
  404. }
  405. - (id)objectAtIndex:(NSUInteger)anIndex {
  406. return object[anIndex];
  407. }
  408. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len {
  409. if (state->state == 0) {
  410. state->state = 1;
  411. // state->itemsPtr = object;
  412. state->mutationsPtr = &mutations;
  413. return [self count];
  414. }
  415. return 0;
  416. }
  417. @end