//
//  MPMainWindowController.m
//  MacPass
//
//  Created by Michael Starke on 24.07.12.
//  Copyright (c) 2012 HicknHack Software GmbH. All rights reserved.
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#import "MPDocumentWindowController.h"
#import "MPActionHelper.h"
#import "MPAppDelegate.h"
#import "MPAutotypeDaemon.h"
#import "MPConstants.h"
#import "MPContextButton.h"
#import "MPDatabaseSettingsWindowController.h"
#import "MPDocument.h"
#import "MPDocumentWindowDelegate.h"
#import "MPDuplicateEntryOptionsWindowController.h"
#import "MPEntryViewController.h"
#import "MPFixAutotypeWindowController.h"
#import "MPInspectorViewController.h"
#import "MPOutlineViewController.h"
#import "MPPasswordEditWindowController.h"
#import "MPPasswordInputController.h"
#import "MPSettingsHelper.h"
#import "MPToolbarDelegate.h"
#import "MPTitlebarColorAccessoryViewController.h"

#import "MPPluginHost.h"
#import "MPPlugin.h"

#import "KeePassKit/KeePassKit.h"

typedef NS_ENUM(NSUInteger, MPAlertContext) {
  MPAlertLossySaveWarning,
};

typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword);

@interface MPDocumentWindowController () {
@private
  id _firstResponder;
}

@property (strong) IBOutlet NSSplitView *splitView;

@property (strong) NSToolbar *toolbar;

@property (strong) MPPasswordInputController *passwordInputController;
@property (strong) MPEntryViewController *entryViewController;
@property (strong) MPOutlineViewController *outlineViewController;
@property (strong) MPInspectorViewController *inspectorViewController;
@property (strong) MPDatabaseSettingsWindowController *documentSettingsWindowController;
@property (strong) MPDocumentWindowDelegate *documentWindowDelegate;
@property (strong) MPPasswordEditWindowController *passwordEditWindowController;
@property (strong) MPToolbarDelegate *toolbarDelegate;
@property (strong) MPFixAutotypeWindowController *fixAutotypeWindowController;

//@property (nonatomic, copy) MPPasswordChangedBlock passwordChangedBlock;

@end

@implementation MPDocumentWindowController

- (NSString *)windowNibName {
  return @"DocumentWindow";
}

-(id)init {
  self = [super initWithWindow:nil];
  if( self ) {
    _firstResponder = nil;
    _toolbarDelegate = [[MPToolbarDelegate alloc] init];
    _outlineViewController = [[MPOutlineViewController alloc] init];
    _entryViewController = [[MPEntryViewController alloc] init];
    _inspectorViewController = [[MPInspectorViewController alloc] init];
    _documentWindowDelegate = [[MPDocumentWindowDelegate alloc] init];
  }
  return self;
}

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark View Handling
- (void)windowDidLoad {
  [super windowDidLoad];
  
  self.window.delegate = self.documentWindowDelegate;
  [self.window registerForDraggedTypes:@[NSURLPboardType]];
  
  MPDocument *document = self.document;
  
  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didRevertDocument:) name:MPDocumentDidRevertNotifiation object:document];
  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didUnlockDatabase:) name:MPDocumentDidUnlockDatabaseNotification object:document];
  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didAddEntry:) name:MPDocumentDidAddEntryNotification object:document];
  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didAddGroup:) name:MPDocumentDidAddGroupNotification object:document];
  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didLockDatabase:) name:MPDocumentDidLockDatabaseNotification object:document];
  
  [self.entryViewController registerNotificationsForDocument:document];
  [self.inspectorViewController registerNotificationsForDocument:document];
  [self.outlineViewController registerNotificationsForDocument:document];
  [self.toolbarDelegate registerNotificationsForDocument:document];
  
  self.toolbar = [[NSToolbar alloc] initWithIdentifier:@"MainWindowToolbar"];
  self.toolbar.autosavesConfiguration = YES;
  self.toolbar.allowsUserCustomization = YES;
  self.toolbar.delegate = self.toolbarDelegate;
  self.window.toolbar = self.toolbar;
  self.toolbarDelegate.toolbar = self.toolbar;
  
  self.splitView.translatesAutoresizingMaskIntoConstraints = NO;
  
  NSView *outlineView = self.outlineViewController.view;
  NSView *inspectorView = self.inspectorViewController.view;
  NSView *entryView = self.entryViewController.view;
  [self.splitView addSubview:outlineView];
  [self.splitView addSubview:entryView];
  [self.splitView addSubview:inspectorView];
  
  [self.splitView setHoldingPriority:NSLayoutPriorityDefaultLow+2 forSubviewAtIndex:0];
  [self.splitView setHoldingPriority:NSLayoutPriorityDefaultLow+1 forSubviewAtIndex:2];
  
  BOOL showInspector = [NSUserDefaults.standardUserDefaults boolForKey:kMPSettingsKeyShowInspector];
  if(!showInspector) {
    [inspectorView removeFromSuperview];
  }
  
  if(document.encrypted) {
    [self showPasswordInput];
  }
  else {
    [self showEntries];
  }
  
  self.splitView.autosaveName = @"SplitView";
  
  /*
   TODO: Add display for database color?
   NSTitlebarAccessoryViewController *tbc = [[MPTitlebarColorAccessoryViewController alloc] init];
   tbc.layoutAttribute = NSLayoutAttributeRight;
   [self.window addTitlebarAccessoryViewController:tbc];
   */
}

- (NSSearchField *)searchField {
  return self.toolbarDelegate.searchField;
}

- (void)_setContentViewController:(MPViewController *)viewController {
  
  NSView *newContentView = nil;
  if(viewController && viewController.view) {
    newContentView = viewController.view;
  }
  NSView *contentView = self.window.contentView;
  NSView *oldSubView = nil;
  if(contentView.subviews.count == 1) {
    oldSubView = contentView.subviews.firstObject;
  }
  if(oldSubView == newContentView) {
    return; // View is already present
  }
  [oldSubView removeFromSuperviewWithoutNeedingDisplay];
  [contentView addSubview:newContentView];
  [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newContentView]|"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(newContentView)]];
  
  NSNumber *border = @([[self window] contentBorderThicknessForEdge:NSMinYEdge]);
  [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[newContentView]-border-|"
                                                                      options:0
                                                                      metrics:NSDictionaryOfVariableBindings(border)
                                                                        views:NSDictionaryOfVariableBindings(newContentView)]];
  
  [contentView layout];
  [self.window makeFirstResponder:viewController.reconmendedFirstResponder];
}

#pragma mark MPDocument notifications
- (void)_didRevertDocument:(NSNotification *)notification {
  [self.outlineViewController clearSelection];
  [self showPasswordInput];
}

- (void)_didAddEntry:(NSNotification *)notification {
  [self showInspector:self];
}

- (void)_didAddGroup:(NSNotification *)notification {
  [self showInspector:self];
}

- (void)_didLockDatabase:(NSNotification *)notification {
  [self showPasswordInput];
}

- (void)_didUnlockDatabase:(NSNotification *)notification {
  [self showEntries];
  /* Show password reminders */
  [self _presentPasswordIntervalAlerts];
}


#pragma mark Actions
- (void)saveDocument:(id)sender {
  MPDocument *document = self.document;
  if(!document.compositeKey) {
    [self editPasswordWithCompetionHandler:^(NSInteger result) {
      if(result == NSModalResponseOK) {
        [self saveDocument:sender];
      }
    }];
    return;
  }
  /* All set and good ready to save */
  [self.document saveDocument:sender];
}
- (void)saveDocumentAs:(id)sender {
  MPDocument *document = self.document;
  if(document.compositeKey) {
    [self.document saveDocumentAs:sender];
    return;
  }
  /* we need to make sure that a password is set */
  [self editPasswordWithCompetionHandler:^(NSInteger result) {
    if(result == NSModalResponseOK) {
      [self saveDocumentAs:sender];
    }
  }];
}

- (void)exportAsXML:(id)sender {
  NSSavePanel *savePanel = [NSSavePanel savePanel];
  MPDocument *document = self.document;
  savePanel.nameFieldStringValue = document.displayName;
  savePanel.allowsOtherFileTypes = YES;
  savePanel.allowedFileTypes = @[(id)kUTTypeXML];
  savePanel.canSelectHiddenExtension = YES;
  [savePanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
    if(result == NSFileHandlingPanelOKButton) {
      [document writeXMLToURL:savePanel.URL];
    }
  }];
}

- (void)importFromXML:(id)sender {
  NSOpenPanel *openPanel = [NSOpenPanel openPanel];
  MPDocument *document = self.document;
  openPanel.allowsMultipleSelection = NO;
  openPanel.canChooseDirectories = NO;
  openPanel.canChooseFiles = YES;
  openPanel.allowedFileTypes = @[(id)kUTTypeXML];
  openPanel.prompt = NSLocalizedString(@"OPEN_BUTTON_IMPORT_XML_OPEN_PANEL", "Open button in the open panel to import an XML file");
  openPanel.message = NSLocalizedString(@"MESSAGE_XML_OPEN_PANEL", "Message in the open panel to import an XML file");
  [openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
    if(result == NSFileHandlingPanelOKButton) {
      [document readXMLfromURL:openPanel.URL];
      [self.outlineViewController showOutline];
    }
  }];
}

- (void)importFromPlugin:(id)sender {
  if(![sender isKindOfClass:NSMenuItem.class]) {
    return;
  }
  NSMenuItem *menuItem = sender;
  if(![menuItem.representedObject isKindOfClass:NSDictionary.class]) {
    return;
  }
  
  NSWindow *sheetWindow = ((MPDocument *)self.document).windowForSheet;
  if(!sheetWindow) {
    return;
  }
  MPPlugin<MPImportPlugin> *importPlugin;
  NSOpenPanel *openPanel = NSOpenPanel.openPanel;
  [importPlugin prepareOpenPanel:openPanel];
  [openPanel beginSheetModalForWindow:sheetWindow completionHandler:^(NSModalResponse result) {
    KPKTree *importedTree = [importPlugin treeForRunningOpenPanel:openPanel withResponse:result];
    [self.document importTree:importedTree];
  }];
}

- (void)mergeWithOther:(id)sender {
  NSOpenPanel *openPanel = [NSOpenPanel openPanel];
  MPDocument *document = self.document;
  openPanel.allowsMultipleSelection = NO;
  openPanel.canChooseDirectories = NO;
  openPanel.canChooseFiles = YES;
  openPanel.message = NSLocalizedString(@"SELECT_FILE_TO_MERGE", @"Message for the dialog to open a file for merge");
  //openPanel.allowedFileTypes = @[(id)kUTTypeXML];
  [openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
    if(result == NSFileHandlingPanelOKButton) {
      [document mergeWithContentsFromURL:openPanel.URL key:document.compositeKey];
    }
  }];
}

- (void)fixAutotype:(id)sender {
  if(!self.fixAutotypeWindowController) {
    self.fixAutotypeWindowController = [[MPFixAutotypeWindowController alloc] init];
  }
  self.fixAutotypeWindowController.document = self.document;
  [self.fixAutotypeWindowController.window makeKeyAndOrderFront:sender];
}

- (void)showPasswordInput {
  [self showPasswordInputWithMessage:nil];
}
- (void)showPasswordInputWithMessage:(NSString *)message {
  if(!self.passwordInputController) {
    self.passwordInputController = [[MPPasswordInputController alloc] init];
  }
  [self _setContentViewController:self.passwordInputController];
  [self.passwordInputController requestPasswordWithMessage:message cancelLabel:nil completionHandler:^BOOL(NSString *password, NSURL *keyURL, BOOL didCancel, NSError *__autoreleasing *error) {
    if(didCancel) {
      return NO;
    }
    return [((MPDocument *)self.document) unlockWithPassword:password keyFileURL:keyURL error:error];
    
  }];
}

- (void)editPassword:(id)sender {
  [self editPasswordWithCompetionHandler:nil];
}

- (void)editPasswordWithCompetionHandler:(void (^)(NSInteger result))handler {
  if(!self.passwordEditWindowController) {
    self.passwordEditWindowController = [[MPPasswordEditWindowController alloc] init];
  }
  [self.document addWindowController:self.passwordEditWindowController];
  
  [self.window beginSheet:self.passwordEditWindowController.window completionHandler:^(NSModalResponse returnCode) {
    if(handler) {
      handler(returnCode);
    }
    [self.passwordEditWindowController.document removeWindowController:self.passwordEditWindowController];
    self.passwordEditWindowController = nil;
  }];
}

- (void)showDatabaseSettings:(id)sender {
  [self _showDatabaseSetting:MPDatabaseSettingsTabGeneral];
}

- (void)editTemplateGroup:(id)sender {
  [self _showDatabaseSetting:MPDatabaseSettingsTabAdvanced];
}

- (void)editTrashGroup:(id)sender {
  [self _showDatabaseSetting:MPDatabaseSettingsTabAdvanced];
}

- (IBAction)lock:(id)sender {
  MPDocument *document = self.document;
  if(document.encrypted) {
    return; // Document already locked
  }
  if(!document.compositeKey) {
    [self editPasswordWithCompetionHandler:^(NSInteger result) {
      if(result == NSModalResponseOK) {
        [self lock:sender];
      }
    }];
    return;
  }
  [document lockDatabase:sender];
}

- (void)createGroup:(id)sender {
  id<MPTargetNodeResolving> target = [NSApp targetForAction:@selector(currentTargetGroups)];
  NSArray *groups = [target currentTargetGroups];
  MPDocument *document = self.document;
  if(groups.count == 1) {
    [document createGroup:groups.firstObject];
  }
  else {
    [document createGroup:document.root];
  }
}

- (void)createEntry:(id)sender {
  id<MPTargetNodeResolving> target = [NSApp targetForAction:@selector(currentTargetGroups)];
  NSArray *groups = [target currentTargetGroups];
  if(groups.count == 1) {
    [(MPDocument *)self.document createEntry:groups.firstObject];
  }
}

- (void)delete:(id)sender {
  id<MPTargetNodeResolving> target = [NSApp targetForAction:@selector(currentTargetNodes)];
  NSArray *nodes = [target currentTargetNodes];
  for(KPKNode *node in nodes) {
    [self.document deleteNode:node];
  }
}
- (void)duplicateEntryWithOptions:(id)sender {
  MPDuplicateEntryOptionsWindowController *wc = [[MPDuplicateEntryOptionsWindowController alloc] init];
  
  [self.window beginSheet:wc.window completionHandler:^(NSModalResponse returnCode) {
    if(returnCode == NSModalResponseOK) {
      
      KPKCopyOptions options = kKPKCopyOptionNone;
      if(wc.referenceUsername) {
        options |= kKPKCopyOptionReferenceUsername;
      }
      if(wc.referencePassword) {
        options |= kKPKCopyOptionReferencePassword;
      }
      if(wc.duplicateHistory) {
        options |= kKPKCopyOptionCopyHistory;
      }
      [((MPDocument *)self.document) duplicateEntryWithOptions:options];
    }
  }];
}

- (void)pickExpiryDate:(id)sender {
  [self.inspectorViewController pickExpiryDate:sender];
}

- (void)showPluginData:(id)sender {
  [self.inspectorViewController showPluginData:sender];
}

- (void)toggleInspector:(id)sender {
  NSView *inspectorView = self.inspectorViewController.view;
  BOOL inspectorWasVisible = [self _isInspectorVisible];
  if(inspectorWasVisible) {
    [inspectorView removeFromSuperview];
  }
  else {
    [self.splitView addSubview:inspectorView];
    [self.splitView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[inspectorView(>=200)]"
                                                                           options:0
                                                                           metrics:nil
                                                                             views:NSDictionaryOfVariableBindings(inspectorView)]];
  }
  [[NSUserDefaults standardUserDefaults] setBool:!inspectorWasVisible forKey:kMPSettingsKeyShowInspector];
}

- (void)performAutotypeForEntry:(id)sender {
  id<MPTargetNodeResolving> entryResolver = [NSApp targetForAction:@selector(currentTargetEntries)];
  NSArray *entries = [entryResolver currentTargetEntries];
  if(entries.count == 1) {
    [[MPAutotypeDaemon defaultDaemon] performAutotypeForEntry:entries.firstObject];
  }
}

- (void)showInspector:(id)sender {
  if(![self _isInspectorVisible]) {
    [self toggleInspector:sender];
  }
}

- (void)focusEntries:(id)sender {
  [self.window makeFirstResponder:[self.entryViewController reconmendedFirstResponder]];
}

- (void)focusGroups:(id)sender {
  [self.window makeFirstResponder:[self.outlineViewController reconmendedFirstResponder]];
}

- (void)focusInspector:(id)sender {
  [self.window makeFirstResponder:[self.inspectorViewController reconmendedFirstResponder]];
}

- (void)showEntries {
  NSView *contentView = self.window.contentView;
  if(self.splitView == contentView) {
    return; // We are displaying the entries already
  }
  if(contentView.subviews.count == 1) {
    [contentView.subviews.firstObject removeFromSuperviewWithoutNeedingDisplay];
  }
  [contentView addSubview:self.splitView];
  NSView *outlineView = self.outlineViewController.view;
  NSView *inspectorView = self.inspectorViewController.view;
  NSView *entryView = self.entryViewController.view;
  
  /*
   The current easy way to prevent layout hiccups is to add the inspector
   Add all needed constraints an then remove it again, if it was hidden
   */
  BOOL removeInspector = NO;
  if(!inspectorView.superview) {
    [self.splitView addSubview:inspectorView];
    removeInspector = YES;
  }
  /* Maybe we should consider not double adding constraints */
  NSDictionary *views = NSDictionaryOfVariableBindings(outlineView, inspectorView, entryView, _splitView);
  [self.splitView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[outlineView(>=150)]-1-[entryView(>=350)]-1-[inspectorView(>=200)]|"
                                                                         options:0
                                                                         metrics:nil
                                                                           views:views]];
  [self.splitView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[outlineView]|"
                                                                         options:0
                                                                         metrics:nil
                                                                           views:views]];
  [self.splitView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[entryView(>=300)]|"
                                                                         options:0
                                                                         metrics:nil
                                                                           views:views]];
  [self.splitView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[inspectorView]|"
                                                                         options:0
                                                                         metrics:nil
                                                                           views:views]];
  
  NSNumber *border = @([[self window] contentBorderThicknessForEdge:NSMinYEdge]);
  [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_splitView]-border-|"
                                                                      options:0
                                                                      metrics:NSDictionaryOfVariableBindings(border)
                                                                        views:views]];
  [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_splitView]|"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:views]];
  [self.outlineViewController showOutline];
  
  /* Restore the State the inspector view was in before the view change */
  if(removeInspector) {
    [inspectorView removeFromSuperview];
  }
  [contentView layoutSubtreeIfNeeded];
}

- (void)copyUsername:(id)sender {
  [self.entryViewController copyUsername:sender];
}

- (void)copyPassword:(id)sender {
  [self.entryViewController copyPassword:sender];
}

- (void)copyCustomAttribute:(id)sender {
  [self.entryViewController copyCustomAttribute:sender];
}

- (void)copyURL:(id)sender {
  [self.entryViewController copyURL:sender];
}

- (void)openURL:(id)sender {
  [self.entryViewController openURL:sender];
}



#pragma mark Validation
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
  return ([self.document validateMenuItem:menuItem]);
}

#pragma mark NSAlert handling
- (void)_presentPasswordIntervalAlerts {
  MPDocument *document = self.document;
  if(document.shouldEnforcePasswordChange) {
    NSAlert *alert = [[NSAlert alloc] init];
    
    alert.alertStyle = NSCriticalAlertStyle;
    alert.messageText = NSLocalizedString(@"ENFORCE_PASSWORD_CHANGE_ALERT_TITLE", "Message text for the enforce password change alert");
    alert.informativeText = NSLocalizedString(@"ENFORCE_PASSWORD_CHANGE_ALERT_DESCRIPTION", "Informative text for the enforce password change alert");
    
    [alert addButtonWithTitle:NSLocalizedString(@"CHANGE_PASSWORD_WITH_DOTS", "Single button to show the password change dialog")];
    
    [alert beginSheetModalForWindow:[self.document windowForSheet] completionHandler:^(NSModalResponse returnCode) {
      /* if sheet was stopped any other way, do nothing */
      if(returnCode != NSAlertFirstButtonReturn) {
        return;
      }
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self editPasswordWithCompetionHandler:^(NSInteger result) {
          /* if password was changed, reset change key and dismiss */
          if(result == NSModalResponseOK) {
            document.tree.metaData.enforceMasterKeyChangeOnce = NO;
          }
          else if(result == NSModalResponseCancel) {
            /* password was not changes, so keep nagging the user! */
            [self _presentPasswordIntervalAlerts];
          }
          else {
            // We might have been killed by locking so do nothing!
          }
        }];
      });
    }];
  }
  else if(document.shouldRecommendPasswordChange) {
    NSAlert *alert = [[NSAlert alloc] init];
    
    alert.alertStyle = NSInformationalAlertStyle;
    alert.messageText = NSLocalizedString(@"RECOMMEND_PASSWORD_CHANGE_ALERT_TITLE", "Message text for the recommend password change alert");
    alert.informativeText = NSLocalizedString(@"RECOMMEND_PASSWORD_CHANGE_ALERT_DESCRIPTION", "Informative text for the recommend password change alert");
    
    [alert addButtonWithTitle:NSLocalizedString(@"CHANGE_PASSWORD_WITH_DOTS", "Button to show the password change dialog")];
    [alert addButtonWithTitle:NSLocalizedString(@"CHANGE_LATER", "Button to postpone the password change")];
    alert.buttons[1].keyEquivalent = [NSString stringWithFormat:@"%c", 0x1b];
    
    
    [alert beginSheetModalForWindow:[self.document windowForSheet]completionHandler:^(NSModalResponse returnCode) {
      if(returnCode == NSAlertSecondButtonReturn) {
        return;
      }
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self editPassword:nil];
      });
    }];
  }
}

#pragma mark Sheet handling
- (void)_showDatabaseSetting:(MPDatabaseSettingsTab)tab {
  if(!self.documentSettingsWindowController) {
    self.documentSettingsWindowController = [[MPDatabaseSettingsWindowController alloc] init];
  }
  [self.document addWindowController:self.documentSettingsWindowController];
  [self.documentSettingsWindowController showSettingsTab:tab];
  [self.window beginSheet:self.documentSettingsWindowController.window completionHandler:^(NSModalResponse returnCode) {
    [self.documentSettingsWindowController.document removeWindowController:self.documentSettingsWindowController];
    self.documentSettingsWindowController = nil;
  }];
}

#pragma mark -
#pragma mark UI Helper

- (BOOL)_isInspectorVisible {
  NSView *inspectorView = self.inspectorViewController.view;
  return (nil != inspectorView.superview);
}

@end
