diff --git a/Plugin/CTFWhitelistWindowController.h b/Plugin/CTFWhitelistWindowController.h index 808bb619..4f5cdbe8 100755 --- a/Plugin/CTFWhitelistWindowController.h +++ b/Plugin/CTFWhitelistWindowController.h @@ -5,9 +5,20 @@ extern NSString* kCTFCheckForUpdates; @interface CTFWhitelistWindowController : NSWindowController { IBOutlet NSArrayController *_controller; IBOutlet NSButton *_checkNowButton; + + IBOutlet NSPanel *confirmUninstallSheet; + IBOutlet NSPanel *successfulUninstallationSheet; + IBOutlet NSPanel *failedUninstallationSheet; } - (IBAction)checkForUpdates:(id)sender; +- (IBAction)uninstallClickToFlash:(id)sender; + +- (IBAction)cancelUninstall:(id)sender; +- (IBAction)approveUninstall:(id)sender; + +- (IBAction)dismissSuccessSheet:(id)sender; +- (IBAction)dismissFailureSheet:(id)sender; @end diff --git a/Plugin/CTFWhitelistWindowController.m b/Plugin/CTFWhitelistWindowController.m index e49841d0..f56ff678 100755 --- a/Plugin/CTFWhitelistWindowController.m +++ b/Plugin/CTFWhitelistWindowController.m @@ -35,4 +35,98 @@ - (NSString *)versionString return [CTFBundle objectForInfoDictionaryKey: @"CFBundleShortVersionString"]; } +- (IBAction)uninstallClickToFlash:(id)sender; +{ + [NSApp beginSheet:confirmUninstallSheet + modalForWindow:[self window] + modalDelegate:self + didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + contextInfo:nil]; +} + +- (IBAction)cancelUninstall:(id)sender; +{ + [confirmUninstallSheet orderOut:sender]; + + [NSApp endSheet:confirmUninstallSheet returnCode:0]; +} + +- (IBAction)approveUninstall:(id)sender; +{ + [confirmUninstallSheet orderOut:sender]; + + [NSApp endSheet:confirmUninstallSheet returnCode:1]; +} + +- (void)sheetDidEnd:(NSWindow *)sheet + returnCode:(int)returnCode + contextInfo:(void *)contextInfo; +{ + if (returnCode == 1) { + NSString *userPluginPath = [@"~/Library/Internet Plug-ins/ClickToFlash.webplugin" stringByExpandingTildeInPath]; + BOOL isDirectory = NO; + BOOL userPluginExists = [[NSFileManager defaultManager] fileExistsAtPath:userPluginPath + isDirectory:&isDirectory]; + BOOL succeeded = NO; + if (userPluginExists && isDirectory) { + // we'll move the plugin to the trash, instead of just obstinately + // deleting it + succeeded = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation + source:[@"~/Library/Internet Plug-ins/" stringByExpandingTildeInPath] + destination:nil + files:[NSArray arrayWithObject:@"ClickToFlash.webplugin"] + tag:nil]; + } + + if (succeeded) { + [NSApp beginSheet:successfulUninstallationSheet + modalForWindow:[self window] + modalDelegate:self + didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + contextInfo:nil]; + } else { + // there are three ways to get here: + + // 1. either userPluginExists equals NO, in which case the plugin is + // installed for all users and we can't guarantee that we can + // uninstall it, so we'll just fail + + // 2. an item exists at the correct path, but it's a file not a + // folder, so it's not ClickToFlash + + // 3. the plugin exists, but for some reason we couldn't move + // it to the trash + + [NSApp beginSheet:failedUninstallationSheet + modalForWindow:[self window] + modalDelegate:self + didEndSelector:@selector(resultSheetDidEnd:returnCode:contextInfo:) + contextInfo:nil]; + } + } else { + // uninstallation was cancelled + } +} + +- (IBAction)dismissSuccessSheet:(id)sender; +{ + [successfulUninstallationSheet orderOut:sender]; + + [NSApp endSheet:successfulUninstallationSheet returnCode:0]; +} + +- (IBAction)dismissFailureSheet:(id)sender; +{ + [failedUninstallationSheet orderOut:sender]; + + [NSApp endSheet:failedUninstallationSheet returnCode:0]; +} + +- (void)returnSheetDidEnd:(NSWindow *)sheet + returnCode:(int)returnCode + contextInfo:(void *)contextInfo; +{ + // nothing to see here! +} + @end diff --git a/Plugin/English.lproj/WhitelistPanel.xib b/Plugin/English.lproj/WhitelistPanel.xib index ff606c66..c6bc7e70 100755 --- a/Plugin/English.lproj/WhitelistPanel.xib +++ b/Plugin/English.lproj/WhitelistPanel.xib @@ -1,7 +1,7 @@ - 1050 + 1040 9J61 677 949.46 @@ -37,13 +37,13 @@ 15 2 - {{80, 265}, {527, 438}} + {{80, 209}, {527, 494}} -534248448 Q2xpY2sgdG8gRmxhc2gg4oCUIFNldHRpbmdzA NSPanel {3.40282e+38, 3.40282e+38} - {420, 350} + {420, 400} 274 @@ -210,7 +210,7 @@ - {{20, 58}, {487, 169}} + {{20, 69}, {487, 169}} @@ -225,7 +225,7 @@ 292 - {{48, 30}, {29, 26}} + {{48, 41}, {29, 26}} YES @@ -250,7 +250,7 @@ 292 - {{20, 30}, {29, 26}} + {{20, 41}, {29, 26}} YES @@ -271,7 +271,7 @@ 289 - {{227, 31}, {283, 17}} + {{227, 42}, {283, 17}} YES @@ -298,7 +298,7 @@ 268 - {{93, 149}, {286, 18}} + {{93, 194}, {286, 18}} YES @@ -330,7 +330,7 @@ 268 - {{93, 129}, {286, 18}} + {{93, 174}, {286, 18}} YES @@ -353,7 +353,7 @@ 268 - {{17, 170}, {67, 17}} + {{17, 215}, {67, 17}} YES @@ -370,7 +370,7 @@ 268 - {{93, 58}, {285, 58}} + {{93, 103}, {285, 58}} YES @@ -642,7 +642,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 268 - {{17, 99}, {67, 17}} + {{17, 144}, {67, 17}} YES @@ -659,7 +659,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 268 - {{17, 28}, {67, 17}} + {{17, 73}, {67, 17}} YES @@ -673,10 +673,27 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + + 268 + {{17, 20}, {67, 17}} + + + YES + + 68288064 + 71304192 + Uninstall: + + + + + + 268 - {{93, 27}, {287, 18}} + {{93, 72}, {287, 18}} YES @@ -699,7 +716,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 268 - {{117, -3}, {86, 28}} + {{117, 42}, {86, 28}} YES @@ -720,7 +737,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 268 - {{93, 169}, {286, 18}} + {{93, 214}, {286, 18}} YES @@ -740,8 +757,29 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 25 + + + 268 + {{88, 13}, {144, 28}} + + + YES + + 67239424 + 134348800 + Uninstall ClickToFlash + + + -2038284033 + 129 + + + 200 + 25 + + - {{65, 235}, {397, 207}} + {{65, 246}, {397, 252}} NSView @@ -749,14 +787,14 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 289 - {{471, 9}, {39, 14}} + {{467, 20}, {43, 14}} YES 68288064 71435264 - 1.4fc6 + version @@ -764,12 +802,12 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA - {527, 438} + {527, 494} {{0, 0}, {1280, 778}} - {420, 372} + {420, 422} {3.40282e+38, 3.40282e+38} ClickToFlash_settingsWindow @@ -803,6 +841,368 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA YES + + 1 + 2 + {{108, 566}, {461, 150}} + 1886912512 + Uninstall ClickToFlash Confirmation + NSPanel + + View + + {3.40282e+38, 3.40282e+38} + {213, 107} + + + 256 + + YES + + + 256 + {{352, 12}, {95, 32}} + + YES + + 67239424 + 137887744 + Uninstall + + + -2038284033 + 1 + + Helvetica + 1.300000e+01 + 16 + + + + + + + + 200 + 25 + + + + + 256 + {{268, 12}, {84, 32}} + + YES + + 67239424 + 137887744 + Cancel + + + -2038284033 + 1 + + + Gw + 200 + 25 + + + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 46}, {84, 84}} + + YES + + 130560 + 33554432 + + NSImage + ctf + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{109, 110}, {335, 20}} + + YES + + 67239424 + 4194304 + Are you sure you want to uninstall ClickToFlash? + + LucidaGrande-Bold + 1.300000e+01 + 2072 + + + + + + + + + 256 + {{109, 60}, {335, 42}} + + YES + + 67239424 + 4194304 + The ClickToFlash plugin will be uninstalled only for this user. If ClickToFlash is installed for all users, this uninstall will fail; contact your system administrator for assistance. + + + + + + + + {461, 150} + + {{0, 0}, {1280, 778}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{108, 580}, {461, 136}} + 1886912512 + ClickToFlash Uninstallation Failed + NSPanel + + View + + {3.40282e+38, 3.40282e+38} + {213, 107} + + + 256 + + YES + + + 256 + {{352, 12}, {95, 32}} + + YES + + 67239424 + 137887744 + OK + + + -2038284033 + 1 + + + + + + + + 200 + 25 + + + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 32}, {84, 84}} + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{109, 96}, {335, 20}} + + YES + + 67239424 + 4194304 + ClickToFlash uninstallation failed. + + + + + + + + + 256 + {{109, 60}, {335, 28}} + + YES + + 67239424 + 4194304 + ClickToFlash may be installed for all users. Contact your system administrator for assistance. + + + + + + + + {461, 136} + + {{0, 0}, {1280, 778}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{108, 592}, {461, 124}} + 1886912512 + ClickToFlash Uninstallation Succeeded + NSPanel + + View + + {3.40282e+38, 3.40282e+38} + {213, 107} + + + 256 + + YES + + + 256 + {{352, 12}, {95, 32}} + + YES + + 67239424 + 137887744 + OK + + + -2038284033 + 1 + + + + + + + + 200 + 25 + + + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 20}, {84, 84}} + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{109, 84}, {335, 20}} + + YES + + 67239424 + 4194304 + ClickToFlash successfully uninstalled. + + + + + + + + + 256 + {{109, 34}, {335, 42}} + + YES + + 67239424 + 4194304 + You must restart this application for the changes to take effect. + + + + + + + + {461, 124} + + {{0, 0}, {1280, 778}} + {213, 129} + {3.40282e+38, 3.40282e+38} + @@ -905,7 +1305,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA versionString NSAllowsEditingMultipleValuesSelection - + 2 @@ -1005,13 +1405,77 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA values.siteInfo NSHandlesContentAsCompoundValue - + 2 183 + + + confirmUninstallSheet + + + + 210 + + + + approveUninstall: + + + + 211 + + + + cancelUninstall: + + + + 212 + + + + failedUninstallationSheet + + + + 235 + + + + successfulUninstallationSheet + + + + 236 + + + + dismissSuccessSheet: + + + + 237 + + + + dismissFailureSheet: + + + + 238 + + + + uninstallClickToFlash: + + + + 239 + @@ -1057,10 +1521,10 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA YES + - @@ -1177,6 +1641,8 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + @@ -1349,6 +1815,283 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA Plugin Defaults + + 186 + + + YES + + + + + + 187 + + + + + 190 + + + YES + + + + + + 191 + + + + + 194 + + + YES + + + + Uninstall Confirm + + + 195 + + + YES + + + + + + + + + + 196 + + + YES + + + + + + 197 + + + YES + + + + + + 199 + + + YES + + + + + + 200 + + + YES + + + + + + 201 + + + YES + + + + + + 202 + + + + + 203 + + + + + 204 + + + + + 206 + + + + + 207 + + + + + 213 + + + YES + + + + Uninstall Failure + + + 214 + + + YES + + + + + + + + + 216 + + + YES + + + + + + 217 + + + YES + + + + + + 218 + + + YES + + + + + + 219 + + + YES + + + + + + 220 + + + + + 221 + + + + + 222 + + + + + 223 + + + + + 225 + + + YES + + + + Uninstall Success + + + 226 + + + YES + + + + + + + + + 227 + + + YES + + + + + + 228 + + + YES + + + + + + 229 + + + YES + + + + + + 230 + + + YES + + + + + + 231 + + + + + 232 + + + + + 233 + + + + + 234 + + + @@ -1359,7 +2102,6 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA -2.IBPluginDependency -3.IBPluginDependency 1.IBEditorWindowLastContentRect - 1.IBPluginDependency 1.IBViewEditorWindowController.showingLayoutRectangles 1.IBWindowTemplateEditedContentRect 1.NSWindowTemplate.visibleAtLaunch @@ -1373,6 +2115,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 102.IBPluginDependency 103.IBPluginDependency 104.IBPluginDependency + 105.IBPluginDependency 106.IBAttributePlaceholdersKey 106.IBPluginDependency 107.IBPluginDependency @@ -1401,9 +2144,76 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 17.IBPluginDependency 170.CustomClassName 170.IBPluginDependency + 186.IBPluginDependency + 187.IBPluginDependency + 190.IBPluginDependency + 191.IBPluginDependency + 194.IBEditorWindowLastContentRect + 194.IBViewEditorWindowController.showingLayoutRectangles + 194.IBWindowTemplateEditedContentRect + 194.ImportedFromIB2 + 194.windowTemplate.hasMinSize + 194.windowTemplate.minSize + 195.IBPluginDependency + 195.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 2.IBPluginDependency 20.IBPluginDependency + 200.IBPluginDependency + 200.ImportedFromIB2 + 201.IBPluginDependency + 201.ImportedFromIB2 + 202.IBPluginDependency + 203.IBPluginDependency + 204.IBPluginDependency + 206.IBPluginDependency + 207.IBPluginDependency 21.IBPluginDependency + 213.IBEditorWindowLastContentRect + 213.IBViewEditorWindowController.showingLayoutRectangles + 213.IBWindowTemplateEditedContentRect + 213.ImportedFromIB2 + 213.windowTemplate.hasMinSize + 213.windowTemplate.minSize + 214.IBPluginDependency + 214.ImportedFromIB2 + 216.IBPluginDependency + 216.ImportedFromIB2 + 217.IBPluginDependency + 217.ImportedFromIB2 + 218.IBPluginDependency + 218.ImportedFromIB2 + 219.IBPluginDependency + 219.ImportedFromIB2 + 220.IBPluginDependency + 221.IBPluginDependency + 222.IBPluginDependency + 223.IBPluginDependency + 225.IBEditorWindowLastContentRect + 225.IBViewEditorWindowController.showingLayoutRectangles + 225.IBWindowTemplateEditedContentRect + 225.ImportedFromIB2 + 225.windowTemplate.hasMinSize + 225.windowTemplate.minSize + 226.IBPluginDependency + 226.ImportedFromIB2 + 227.IBPluginDependency + 227.ImportedFromIB2 + 228.IBPluginDependency + 228.ImportedFromIB2 + 229.IBPluginDependency + 229.ImportedFromIB2 + 230.IBPluginDependency + 230.ImportedFromIB2 + 231.IBPluginDependency + 232.IBPluginDependency + 233.IBPluginDependency + 234.IBPluginDependency 31.IBPluginDependency 32.IBPluginDependency 36.IBAttributePlaceholdersKey @@ -1412,7 +2222,9 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 39.IBPluginDependency 4.IBPluginDependency 41.IBPluginDependency + 42.IBPluginDependency 7.CustomClassName + 7.IBPluginDependency 9.IBPluginDependency @@ -1420,15 +2232,14 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilderKit com.apple.InterfaceBuilderKit - {{40, 279}, {527, 438}} - com.apple.InterfaceBuilder.CocoaPlugin + {{12, 251}, {527, 494}} - {{40, 279}, {527, 438}} + {{12, 251}, {527, 494}} {196, 240} {{202, 428}, {480, 270}} - {420, 350} + {420, 400} com.apple.InterfaceBuilder.CocoaPlugin ToolTip @@ -1442,6 +2253,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin ToolTip @@ -1509,6 +2321,73 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + {{265, 81}, {461, 150}} + + {{265, 81}, {461, 150}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{266, 75}, {461, 136}} + + {{266, 75}, {461, 136}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{281, 226}, {461, 124}} + + {{281, 226}, {461, 124}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin ToolTip @@ -1523,8 +2402,10 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin CTFUserDefaultsController com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -1547,7 +2428,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA - 183 + 239 @@ -1557,15 +2438,32 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA NSUserDefaultsController IBProjectSource - CTFUserDefaultsController.h + Plugin/CTFUserDefaultsController.h CTFWhitelistWindowController NSWindowController - checkForUpdates: - id + YES + + YES + approveUninstall: + cancelUninstall: + checkForUpdates: + dismissFailureSheet: + dismissSuccessSheet: + uninstallClickToFlash: + + + YES + id + id + id + id + id + id + YES @@ -1573,11 +2471,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA YES _checkNowButton _controller + confirmUninstallSheet + failedUninstallationSheet + successfulUninstallationSheet YES NSButton NSArrayController + NSPanel + NSPanel + NSPanel