//
//  WBMainWindow.mm
//  MySQLWorkbench
//
//  Created by Alfredo Kojima on 1/Oct/08.
//  Copyright 2008 Sun Microsystems Inc. All rights reserved.
//

#import "WBMainWindow.h"
#import "WBMainController.h"
#import "WBMenuManager.h"
#import "WBToolbarManager.h"
#import "WBOverviewPanel.h"
#import "WBModelDiagramPanel.h"
#import "WBPluginPanel.h"
#import "MCanvasViewer.h"
#import "MCanvasScrollView.h"
#import "GRTShellViewController.h"
#import "WBTabView.h"
#import "WBTabItem.h"
#import "WBModelSidebarController.h"
#import "WBOutputPanelController.h"
#import "WBPaletteContainer.h"
#import "WBPluginBase.h"
#import "WBPluginEditorBase.h"
#import "MCPPUtilities.h"

#include "workbench/wb_context_ui.h"
#include "workbench/wb_model_diagram_form.h"
#include "workbench/wb_overview_physical.h"


#define MIN_SIDEBAR_WIDTH 180

@implementation WBWindow

/** Overrider to catch changes to firstResponders
 *
 * This method is overriden so that we can notify the backend when the 
 * first responder is changed, to know when the active 'form' changes, which 
 * would trigger updates to the menus, toolbars etc.
 */
- (BOOL)makeFirstResponder:(NSResponder *)responder
{
  // NSLog(@"%@", responder);
  BOOL changed= [super makeFirstResponder: responder];
  if (changed)
    [[self windowController] firstResponderChanged: responder];

  return changed;
}

@end





@implementation WBMainWindow


- (void)setOwner:(WBMainController*)owner
{
  _mainController= owner;
}

- (WBMainController*)owner
{
  return _mainController;
}

- (WBToolbarManager*)toolbarManager
{
  return _toolbarManager;
}

- (WBMenuManager*)menuManager
{
  return _menuManager;
}

static void show_message_list(WBMainWindow *self)
{
  [self showOutput:nil];
}


- (void)setWBContext:(wb::WBContextUI*)wbui
{
  _wbui= wbui;
  
  [outputController setupWithGRTManager:_wbui->get_wb()->get_grt_manager()];
  
  _wbui->get_wb()->get_grt_manager()->get_messages_list()->signal_show().
    connect(sigc::bind(sigc::ptr_fun(show_message_list), self));
  
  // setup GRT sidebar here so that output can go to shell
  [shellController setupWithGRTManager: _wbui->get_wb()->get_grt_manager()];
  
  {
    NSRect rect([drawerView frame]);
    
    rect.origin= NSMakePoint(0, 0);
    
    // setup GRT shell sidebar in drawer
    [drawerView addSubview: [shellController sidebar]];
    [[shellController sidebar] setFrame: rect];
    [[shellController sidebar] setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable
     |NSViewMinXMargin|NSViewMinYMargin|NSViewMaxXMargin|NSViewMaxYMargin];
  }
  _menuManager= [[WBMenuManager alloc] initWithMenuBar:[NSApp mainMenu] WBContext:_wbui];
  
#if (APP_MAJOR_NUMBER == 5) && (APP_MINOR_NUMBER > 1)
  _workspace_overview= [[WBOverviewPanel alloc] initWithBE:_wbui->get_main_overview()];
  
#endif
  
  _physical_overview= [[WBOverviewPanel alloc] initWithBE:_wbui->get_physical_overview()];
  [_physical_overview setMenuManager: _menuManager];
  
//  [(WBToolbarTabView*)topTabView setAllowsTabReordering:NO];
  
  [_menuManager rebuildMainMenu];
    
  [_panels setObject:[self outputPanel] forKey:[[self outputPanel] identifier]];
  
  // remove output and shell tabs
  [self closeBottomPanelWithIdentifier: [[self outputPanel] identifier]];
  [self closeBottomPanelWithIdentifier: @"shell"];
  
#if (APP_MAJOR_NUMBER == 5) && (APP_MINOR_NUMBER > 1)  
  [self addTopPanel: _workspace_overview];
  [_workspace_overview rebuildAll];
#endif
  
  [self addTopPanel: _physical_overview];
  [_physical_overview rebuildAll];
}


/** Called after the window is shown on screen
 * The amount of work done here should be minimal, so that there isn't any visible window
 * relayouting.
 */
- (void)setupReady
{
  _toolbarManager= [[WBToolbarManager alloc] initWithToolbar:[[self window] toolbar] 
                                                   WBContext:_wbui];
  
  [_toolbarManager refreshMainToolbar];

  // setup the sidebars (has to be done after window is shown or the navigator view won't work)
  [modelSidebarController setupWithWBContextUI: _wbui];  

  [modelSidebarController resetForNewModel];
  
  // setup model sidebar
  {
    NSTabViewItem *item= [[[NSTabViewItem alloc] initWithIdentifier:@"model"] autorelease];
    
    [item setLabel:@"Model"];
    [item setView:[modelSidebarController sidebar]];
    
    [sideTabView addTabViewItem:item];
    [sideTabView selectLastTabViewItem: self];
  }

  [shellController refresh];
  [modelSidebarController refresh];
}


- (NSSize)minimumSizeForBottomTabView
{
  NSSize minSize= NSMakeSize(0, 0);
  for (NSTabViewItem *tab in [bottomTabView tabViewItems])
  {
    WBBasePanel *panel= [_panels objectForKey: [tab identifier]];
    
    if ([panel respondsToSelector:@selector(minimumSize)])
    {
      NSSize msize= [panel minimumSize];
      
      minSize.width= MAX(minSize.width, msize.width);
      minSize.height= MAX(minSize.height, msize.height);
    }
  }
  
  minSize.height+= NSHeight([bottomTabView frame]) - NSHeight([bottomTabView contentRect]);
  
  return minSize;
}


- (NSTabViewItem*)addTopPanel:(WBBasePanel*)panel
{
  id tabItem= [[[NSTabViewItem alloc] initWithIdentifier:[panel identifier]] autorelease];
    
  [tabItem setView:[panel topView]];
  [tabItem setLabel:[panel title]];
//  NSImage *icon= [panel tabIcon];
//  if (icon)
//    [tabItem setIconImage:icon];
  
  [_panels setObject:panel forKey:[panel identifier]];
  
  [topTabView addTabViewItem:tabItem];
  
  return tabItem;
}


- (NSTabViewItem*)addBottomPanel:(WBBasePanel*)panel
{
  id tabItem= [[[NSTabViewItem alloc] initWithIdentifier:[panel identifier]] autorelease];
  
  [tabItem setView:[panel topView]];
  [tabItem setLabel:[panel title]];
  
  [_panels setObject:panel forKey:[panel identifier]];
  
  // check if bottom tabview has to be enlarged
  float minHeight= [self minimumSizeForBottomTabView].height;
  float minHeightForNewPanel= [panel minimumSize].height + NSHeight([bottomTabView frame]) - NSHeight([bottomTabView contentRect]);
  if (minHeight < minHeightForNewPanel)
    minHeight= minHeightForNewPanel;
  if (minHeight > NSHeight([bottomTabView frame]))
  {
    [mainSplitView setPosition: NSHeight([mainSplitView frame]) - minHeight
              ofDividerAtIndex: 0];
  }
  
  [bottomTabView addTabViewItem:tabItem];
  
  return tabItem;
}




- (void)switchToDiagramWithIdentifier:(const char*)identifier
{
  NSString *ident= [NSString stringWithCString:identifier];
  id form= [_panels objectForKey: ident]; 
  
  // check if the diagram was closed
  for (NSTabViewItem *item in _closedTopTabs)
  {
    if ([[item identifier] isEqual: ident])
    {
      [topTabView addTabViewItem:item];
      [topTabView selectLastTabViewItem:nil];
      [_closedTopTabs removeObject:item];
        
      if ([form respondsToSelector:@selector(didOpen)])
        [form didOpen];
      
      return;
    }
  }
  
  [topTabView selectTabViewItemWithIdentifier:ident];
}


- (GRTShellViewController*)shell
{
  return shellController;
}


- (WBOutputPanelController*)outputPanel
{
  return outputController;
}


- (void)updateBackendTimer
{
  [_backendTimer invalidate];
  _backendTimer= 0;

  double interval= _wbui->get_wb()->get_grt_manager()->delay_for_next_timeout();
  
  if (interval >= 0.0)
    _backendTimer= [NSTimer scheduledTimerWithTimeInterval:interval
                                                    target:self
                                                  selector:@selector(fireBackendTimer:)
                                                  userInfo:nil
                                                   repeats:NO];
}


- (void)fireBackendTimer:(NSTimer*)timer
{
  _wbui->get_wb()->get_grt_manager()->flush_timers();

  [self updateBackendTimer];
}



- (void)refreshGUI:(wb::RefreshType)type
         argument1:(const std::string&)arg1
         argument2:(NativeHandle)arg2
{
  const char* RefreshTypeStr[] = {
    "RefreshNeeded",
    "RefreshNothing",
    "RefreshLayers",
    "RefreshSchemaList",
    "RefreshSchema",
    "RefreshNewView",
    "RefreshSelection",
    "RefreshToolbar",
    "RefreshMessages",
    "RefreshCloseEditor", // argument: object-id (close all if "")
    "RefreshFindOutput",
    "RefreshHistory",
    "RefreshUserTypes",
    "RefreshOverviewNodeInfo",    // argument: node-id
    "RefreshOverviewNodeChildren",// argument: node-id
    "RefreshCatalogTreeNode",
    "RefreshViewName",
    "RefreshDocument",
    "RefreshCloseDocument",
    "RefreshZoom",
    "RefreshMenubar",
    "RefreshTimer"
  };
  
  switch (type)
  {
    case wb::RefreshNeeded:
      [_mainController requestRefresh];
      break;
      
    case wb::RefreshDocument:
      // refresh the title of the document
      [[self window] setTitle:[NSString stringWithUTF8String:_wbui->get_title().c_str()]];
      [[self window] setRepresentedFilename:[NSString stringWithUTF8String:_wbui->get_wb()->get_filename().c_str()]];
      [[self window] setDocumentEdited:_wbui->get_wb()->has_unsaved_changes()];
      break;
      
    case wb::RefreshCloseDocument:
      [self closeEditorsMatching:nil]; // close all editors

      // flush anything waiting to be executed 
      _wbui->get_wb()->flush_idle_tasks();
  
      _wbui->get_wb()->close_document_finish();
      break;
      
    case wb::RefreshOverviewNodeInfo:
      try
      {
        if (!arg2 || arg2 == [_physical_overview formBE])
          [_physical_overview refreshNode: bec::NodeId(arg1)];
        else
          [_workspace_overview refreshNode: bec::NodeId(arg1)]; 
      }
      catch (std::exception &exc)
      {
        NSLog(@"exception caught refreshing overview '%p' node: %s", arg2, exc.what());
      }
      break;

    case wb::RefreshOverviewNodeChildren:
      try
      {
        if (!arg2 || arg2 == [_physical_overview formBE])
        {
          if (arg1.empty())
          {
            NSInteger i= [topTabView indexOfTabViewItemWithIdentifier: [_physical_overview identifier]];
            if (i >= 0)
              [[topTabView tabViewItemAtIndex: i] setLabel: [_physical_overview title]];
          }
          [_physical_overview refreshNodeChildren: bec::NodeId(arg1)];
        }
        else if (arg2 == [_workspace_overview formBE])
        {
          if (arg1.empty())
          {
            NSInteger i= [topTabView indexOfTabViewItemWithIdentifier: [_workspace_overview identifier]];
            if (i >= 0)
              [[topTabView tabViewItemAtIndex: i] setLabel: [_workspace_overview title]];
          }          
          [_workspace_overview refreshNodeChildren: bec::NodeId(arg1)];
        }
        else
          NSLog(@"invalid %p", arg2);
      }
      catch (std::exception &exc)
      {
        NSLog(@"exception caught refreshing overview '%p' node children: %s", arg2, exc.what());
      }
      break;
      
    case wb::RefreshNewView:
    {
      id form= [_panels objectForKey: [NSString stringWithUTF8String: arg1.c_str()]];
      if (![form isClosed])
        [self switchToDiagramWithIdentifier: arg1.c_str()];
      break;
    }
    case wb::RefreshViewName:
    {
      id diagramPanel= (id)arg2;
      id identifier= [diagramPanel identifier];
      for (NSTabViewItem *item in [topTabView tabViewItems])
      {
        if ([item identifier] == identifier)
        {
          [item setLabel: [diagramPanel title]];
          break;
        }
      }
      break;
    }
    case wb::RefreshMenubar:
      [_menuManager rebuildMainMenu];
      break;      
      
    case wb::RefreshToolbar:
      if (arg1.empty() || arg1 == "main")
        [_toolbarManager refreshMainToolbar];
      if (arg1.empty() || arg1 == "tools")
      {
        if ([[self selectedMainPanel] respondsToSelector: @selector(toolsToolbar)])
          [_toolbarManager refreshToolsToolbar: [(id)[self selectedMainPanel] toolsToolbar]];
      }
      if (arg1.empty() || arg1 == "options")
      {
        if ([[self selectedMainPanel] respondsToSelector: @selector(optionsToolbar)])
        {
          BOOL flag;
          flag= [_toolbarManager refreshToolOptionsToolbar: [(id)[self selectedMainPanel] optionsToolbar]];
          [(id)[self selectedMainPanel] showOptionsToolbar:flag];
        }
      }      
      break;
      
    case wb::RefreshSchemaList:
      [modelSidebarController refreshCatalogTree];
      break;

    case wb::RefreshSchema:
      [modelSidebarController refreshCatalogTree];
      break;
      
    case wb::RefreshLayers:
      [modelSidebarController refreshLayerTree];
      break;
      
    case wb::RefreshHistory:
      [modelSidebarController refreshHistory];
      [[self window] setDocumentEdited:_wbui->get_wb()->has_unsaved_changes()];
      break;
      
    case wb::RefreshUserTypes:
      [modelSidebarController refreshUserTypeList];
      break;
      
    case wb::RefreshZoom:
      [modelSidebarController refreshZoom];
      break;
      
    case wb::RefreshSelection:
      [modelSidebarController updateForSelectionChange];
      break;
      
    case wb::RefreshMessages:
      [outputController refreshMessages];
      break;

    case wb::RefreshCloseEditor:
      [self closeEditorsMatching: arg1.empty() ? nil : [NSString stringWithCPPString: arg1]];
      break;
      
    case wb::RefreshTimer:
      [self updateBackendTimer];
      break;
      
    default:
      NSLog(@"unhandled %s", RefreshTypeStr[type]);
      break;
  }

//  NSLog(@"MEG: %s", RefreshTypeStr[type]);
}


- (void)setStatusText:(NSString*)text
{  
  [statusBarText setStringValue:text];
  [statusBarText sizeToFit];
  
  [text release];
}


- (void)blockGUI:(BOOL)lock
{
  //[_menuManager setBlocked: lock];
  //[_toolbarManager setBlocked: lock];
}


- (mdc::CanvasView*)createView:(const char*)oid
                          name:(const char*)name
{
  wb::ModelDiagramForm *dform= _wbui->get_diagram_form_for_diagram_id(oid);
  WBModelDiagramPanel *panel= [[WBModelDiagramPanel alloc] initWithId:[NSString stringWithUTF8String:oid]
                                                               formBE:dform];

  NSTabViewItem* item= [self addTopPanel:panel];
  [panel release];
  
  if (dform->is_closed())
  {
    [topTabView removeTabViewItem: item];
    [_closedTopTabs addObject: item];
  }
  return [panel canvas];
}


- (void)destroyView:(mdc::CanvasView*)view
{
  NSString *identifier= [NSString stringWithUTF8String:view->get_tag().c_str()];
  WBModelDiagramPanel *panel= [_panels objectForKey:identifier];

  if (panel)
  {
    NSUInteger i= [topTabView indexOfTabViewItemWithIdentifier:identifier];
    if (i != NSNotFound)
    {
      NSTabViewItem *item= [topTabView tabViewItemAtIndex:i];    
      [topTabView removeTabViewItem:item];
    }
    [_panels removeObjectForKey:identifier];
  }

  for (NSTabViewItem *item in [_closedTopTabs reverseObjectEnumerator])
  {
    if ([[item identifier] isEqual: identifier])
    {
      [_closedTopTabs removeObject: item];
      break;
    }
  }
}


- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)tabView
{
  if (tabView == bottomTabView)
  {        
    if ([tabView numberOfTabViewItems] == 0)
    {
      // tabview got emptied, collapse the splitview
      
      //[tabView setHidden:YES];
      [mainSplitView setPosition:[mainSplitView maxPossiblePositionOfDividerAtIndex:0] ofDividerAtIndex:0];
    }
    else if ([tabView numberOfTabViewItems] > 0 && [mainSplitView isSubviewCollapsed:tabView])
    {
      // tabview getting filled again, uncollapse the splitview
      
      CGFloat position= [self splitView:mainSplitView
                 constrainMaxCoordinate:0
                            ofSubviewAt:0];
      
      // if the min size is too small, make it a bit bigger so that it won't show up totally collapsed
      // for views that allows small minsizes

      if (position > NSHeight([mainSplitView frame]) - [mainSplitView dividerThickness] - 250)
        position= NSHeight([mainSplitView frame]) - [mainSplitView dividerThickness] - 250;
      
      [mainSplitView setPosition:position
                ofDividerAtIndex:0];
    }
  }
}


- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
{
  // look in the subviews of the tabView for the topone one that can become firstResponder
  // and then make it 
  id view= [tabViewItem view];
  NSMutableArray *viewStack= [NSMutableArray array];
  NSView *topMostView= nil; // the editable view that's at the topmost position
  CGFloat topMostViewY= MAXFLOAT;
  NSView *firstView= nil; // 1st focusable view used as plan B
  
  [viewStack addObject: view];
  
  while ([viewStack count] > 0)
  {
    view= [viewStack objectAtIndex: 0];
    [viewStack removeObjectAtIndex: 0];
    
    if ([view acceptsFirstResponder] && [view class] != [NSView class] && [view respondsToSelector:@selector(isEditable)] && [view isEditable])
    {
      NSPoint pos= [view convertPointToBase: [view frame].origin];
      if (!topMostView || pos.y < topMostViewY)
      {
        topMostView= view;
        topMostViewY= pos.y;
      }
    }
    else if ([view canBecomeKeyView] && !firstView)
      firstView= view;
    
    for (id subview in [view subviews])
    {
      if ([subview acceptsFirstResponder] && 
          [subview class] != [NSView class] && 
          [subview respondsToSelector:@selector(isEditable)] && [subview isEditable])
      {
        NSPoint pos= [subview convertPointToBase: [subview frame].origin];
        if (!topMostView || pos.y < topMostViewY)
        {
          topMostView= subview;
          topMostViewY= pos.y;
        }        
      }
      else if ([subview canBecomeKeyView] && !firstView)
        firstView= subview;
        
      [viewStack addObject: subview];
    }
  }
    
  if (!topMostView)
    [[self window] makeFirstResponder: firstView];
  else
    [[self window] makeFirstResponder: topMostView];
}



- (void)tabView:(NSTabView *)tabView
willCloseTabViewItem:(NSTabViewItem*)tabViewItem
{
  id form= [_panels objectForKey:[tabViewItem identifier]]; 
  
  if ([form respondsToSelector:@selector(willClose)])
    [form willClose];
  
  if (tabView == topTabView)
    [_closedTopTabs addObject:tabViewItem];
  else if (tabView == bottomTabView)
  {
    // retain and autorelease so that the form won't be deallocated
    // before the tabview has a chance to remove it
    [[form retain] autorelease];
    
    if ([form isKindOfClass: [WBPluginPanel class]])
      [_panels removeObjectForKey:[tabViewItem identifier]];
    else
      [_closedBottomTabs addObject:tabViewItem];
  }
}



- (void)closePanel:(WBBasePanel*)panel
{
  id container= [[panel topView] nextResponder];
  
  // one more nextResponder, because of the hack with custom NSTabViews
  container= [container nextResponder];
  
  if (container == topTabView)
  {
    [self closeTopPanelWithIdentifier: [panel identifier]];
  }
  else if (container == bottomTabView)
  {
    BOOL wasActive= [self activePanel] == panel;
    
    [self closeBottomPanelWithIdentifier: [panel identifier]];
    
    // auto-reselect the current toptabview item if bottom tabview is empty
    // and was 1st responder
    if (wasActive && [bottomTabView numberOfTabViewItems] == 0)
    {
      [self tabView: topTabView didSelectTabViewItem: [topTabView selectedTabViewItem]];
    }
  }
}



- (void)closeTopPanelWithIdentifier:(id)identifier
{
  NSUInteger index= [topTabView indexOfTabViewItemWithIdentifier: identifier];
  if (index != NSNotFound)
  {
    NSTabViewItem *item= [topTabView tabViewItemAtIndex: index];
    id form= [_panels objectForKey:[item identifier]];
    
    [form retain];
    
//    if ([form respondsToSelector:@selector(notifyClose)]
//      [form notifyClose];
    
    if ([form isKindOfClass: [WBPluginPanel class]])
    {
      [_panels removeObjectForKey: [item identifier]];
    }
    else
      [_closedTopTabs addObject: item];
    
    [topTabView removeTabViewItem: item];
    
    // need to release now instead of autoreleasing because plugins should be released synchronously
    // now, and not in runloop
    [form release];
  }
}


- (void)closeBottomPanelWithIdentifier:(id)identifier
{
  NSUInteger index= [bottomTabView indexOfTabViewItemWithIdentifier: identifier];
  if (index != NSNotFound)
  {
    NSTabViewItem *item= [bottomTabView tabViewItemAtIndex: index];
    id form= [_panels objectForKey:[item identifier]]; 
    
    [form retain];

//    if ([form respondsToSelector: @selector(notifyClose)])
//      [form notifyClose];
    
    if ([form isKindOfClass: [WBPluginPanel class]])
    {      
      [_panels removeObjectForKey: [item identifier]];
    }
    else
      [_closedBottomTabs addObject: item];

    [bottomTabView removeTabViewItem: item];
    
    [form release];
  }
}



- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
{
  if (tabView == topTabView)
  {
    WBBasePanel *panel= [_panels objectForKey:[tabViewItem identifier]];
    
    NSAssert(![panel isKindOfClass:[WBModelDiagramPanel class]] || [(WBModelDiagramPanel*)panel canvas], @"diagram panel has no canvas at moment of tab switch");
    
    _wbui->set_active_form([panel formBE]);
    
    // not needed in theory, but we refresh here anyway to make the update more immediate
    if ([panel respondsToSelector: @selector(toolsToolbar)])
      [_toolbarManager refreshToolsToolbar: [(id)panel toolsToolbar]];

//    [_toolbarManager refreshMainToolbar];
  }
}


- (void)changedIdentifierOfPanel: (WBBasePanel*)panel
                  fromIdentifier: (id)identifier
{
  [[panel retain] autorelease];
  [_panels removeObjectForKey: identifier];
  [_panels setObject: panel forKey: [panel identifier]];
  
  NSUInteger index= [bottomTabView indexOfTabViewItemWithIdentifier: identifier];
  if (index != NSNotFound)
  {    
    id item= [bottomTabView tabViewItemAtIndex: index];
    [bottomTabView removeTabViewItem: item]; // needed to workaround WBTabView bug
    [item setIdentifier: [panel identifier]];
    [bottomTabView insertTabViewItem: item atIndex: index]; // needed to workaround WBTabView bug
  }
}


- (void)reopenBottomTabViewItem:(id)identifier
{
  for (NSTabViewItem *item in _closedBottomTabs)
  {
    if ([[item identifier] isEqual:identifier])
    {
      id form= [_panels objectForKey: identifier];

      [bottomTabView addTabViewItem:item];
      [_closedBottomTabs removeObject:item];

      if ([form respondsToSelector:@selector(didOpen)])
        [form didOpen];
      break;
    }
  }
  [bottomTabView selectTabViewItemWithIdentifier:identifier];
}


- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset
{
  if (sender == mainSplitView && offset == 0)
  {
    NSSize minSize= [self minimumSizeForBottomTabView];

    return NSHeight([sender frame]) - minSize.height - [mainSplitView dividerThickness];
  }
  else if (sender == sideSplitView)
  {
    return proposedMax - MIN_SIDEBAR_WIDTH;
  }    
  return proposedMax;
}



- (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset
{
  if (sender == mainSplitView && offset == 0) {
    return 100; // Looks nice
  }
  return proposedMax;
}



- (BOOL) splitView: (NSSplitView*) sender
canCollapseSubview: (NSView*) subview;
{
  return (subview == bottomTabView);
}



- (BOOL) splitView: (NSSplitView*) splitView
shouldCollapseSubview: (NSView*) subview
forDoubleClickOnDividerAtIndex: (NSInteger) dividerIndex;
{
  return (subview == bottomTabView);
}



- (IBAction)showMySQLOverview:(id)sender
{
  id identifier= [_physical_overview identifier];
  
  for (NSTabViewItem *item in _closedTopTabs)
  {
    if ([[item identifier] isEqual:identifier])
    {
      [topTabView addTabViewItem:item];
      [_closedTopTabs removeObject:item];
      break;
    }
  }
  [topTabView selectTabViewItemWithIdentifier:identifier];  
}


- (IBAction)showGRTShell:(id)sender
{
  [self reopenBottomTabViewItem:@"shell"];
  [drawer open:nil];
}


- (IBAction)toggleGRTSidebar:(id)sender
{
  [drawer toggle:nil];
}


- (IBAction)showOutput:(id)sender
{
  [self reopenBottomTabViewItem:@"output"];
}


- (void)focusSearchField:(id)sender
{
  [_toolbarManager focusSearchField:sender];
}


- (void)performSearchObject:(id)sender
{
  NSString *search= [_toolbarManager lastSearchString];
  if (search && [search length] > 0)
  {
    bec::UIForm *main_form= _wbui->get_active_main_form();
    if (main_form && main_form->get_frontend_data())
    {
      id form= reinterpret_cast<id>(main_form->get_frontend_data());
      if ([form respondsToSelector:@selector(searchString:)])
        [form searchString: search];
    }
  }
}


- (void)popupDiagramMenu:(wb::MenuItemList*)items atPoint:(NSPoint)pos
{
  NSView *diagram= [[_panels objectForKey: [[topTabView selectedTabViewItem] identifier]] canvasViewer];

  [_menuManager refreshMenu:contextMenu withItems:*items];
  
  [NSMenu popUpContextMenu:contextMenu withEvent:[NSApp currentEvent] forView:diagram];
}



- (void)resetWindowLayout
{
  NSLog(@"reset window layout");
}


- (WBBasePanel*)findPanelForView:(NSView*)view
                       inTabView:(NSTabView*)tabView
{
  for (NSTabViewItem *item in [tabView tabViewItems])
  {
    if ([item view] == view)
    {
      id form= [_panels objectForKey: [item identifier]];
      if (!form)
        NSLog(@"Item in toplevel tabview is not a known form", [item label]);
      return form;
    }
  }
  return nil;
}

//--------------------------------------------------------------------------------------------------

/**
 * Iterates over all currently open editors and returns the first one it encounters
 * that matches the given plugin type.
 */
- (WBBasePanel*) findPanelForPluginType: (Class) type
{
  for (NSString *key in [_panels keyEnumerator])
  {
    id panel= [_panels objectForKey: key];
    if ([panel respondsToSelector: @selector(pluginEditor)])
    {
      id plugin= [panel pluginEditor];
      if ([plugin isKindOfClass: type])
        return panel;
    }
  }
  return nil;
}

//--------------------------------------------------------------------------------------------------

- (void)closeEditorsMatching: (NSString*)identifier
{
  NSMutableArray *editorsToClose= [NSMutableArray array];
  
  for (NSString *key in [_panels keyEnumerator])
  {
    id form= [_panels objectForKey: key];
    if ([form isKindOfClass: [WBPluginPanel class]])
    {
      if (identifier == nil || [[form pluginEditor] matchesIdentifierForClosingEditor: identifier])
      {
        [editorsToClose addObject: [form identifier]];
      }
    }
  }
  
  for (id ident in editorsToClose)
    [self closeBottomPanelWithIdentifier: ident];
}


- (WBBasePanel*)panelForResponder: (NSResponder*)responder
{
  // look up the responder chain until we reach one of the known toplevel containers
  NSResponder *previous= nil;
  NSResponder *previousPrevious= nil;
  while (responder && (responder != topTabView && responder != bottomTabView))
  {
    // because of the bizarre way our custom tabviews are implemented, there will be a dummy NSTabView between the
    // actual tabview and the content view we want.
    previousPrevious= previous;
    previous= responder;
    responder= [responder nextResponder];
  }
  
  if (responder && [responder isKindOfClass: [NSTabView class]])
    return [self findPanelForView: (NSView*)previousPrevious inTabView: (NSTabView*)responder];
  
  return 0;  
}


/** Find the active panel based on firstResponder
 */
- (WBBasePanel*)activePanel
{
  return [self panelForResponder: [[self window] firstResponder]];
}

- (WBBasePanel*)selectedMainPanel
{
  return [_panels objectForKey: [[topTabView selectedTabViewItem] identifier]];
}


- (void)firstResponderChanged: (NSResponder*)responder
{
  WBBasePanel *panel= [self panelForResponder:responder];
  
  if (panel)
    _wbui->set_active_form([panel formBE]);
  else
    _wbui->set_active_form(0);
}

//--------------------------------------------------------------------------------------------------

/**
 * Selects (activates) the given panel in the UI.
 */
- (void) activatePanel: (WBBasePanel*) panel
{
  if (panel)
    _wbui->set_active_form([panel formBE]);
  else
    _wbui->set_active_form(0);
}

//--------------------------------------------------------------------------------------------------

#pragma mark Creation, Destruction



- (id)init
{
  self = [super init];
  if (self != nil) 
  {
    _panels= [[NSMutableDictionary alloc] init];
    _closedTopTabs= [[NSMutableArray alloc] init];
    _closedBottomTabs= [[NSMutableArray alloc] init];
  }
  return self;
}



- (void)load
{
  if (![NSBundle loadNibNamed:@"MainWindow" owner:self])
    NSLog(@"Failed loading nib");
}



- (void)awakeFromNib
{  
  [statusBarText setStringValue:@""];
  
  [sideSplitView setPosition: NSWidth([sideSplitView frame]) - MIN_SIDEBAR_WIDTH ofDividerAtIndex: 0];
}



- (void)dealloc
{
  [_closedTopTabs release];
  [_closedBottomTabs release];
  
  [_menuManager release];
  [_toolbarManager release];
  [_panels release];
  [_workspace_overview release];
  [_physical_overview release];
	
  [super dealloc];
}


- (BOOL)windowShouldClose:(id)window
{
  if (_wbui->request_quit())
  {
    _wbui->perform_quit();
    return YES;
  }
  return NO;
}

@end


