/*
 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. 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; version 2 of the
 * License.
 *  
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

using Aga.Controls.Tree;

using MySQL.Base;
using MySQL.Controls;
using MySQL.Forms;
using MySQL.Grt;
using MySQL.GUI.Mdc;
using MySQL.GUI.Workbench.Plugins;
using MySQL.GUI.Workbench.Properties;
using MySQL.Utilities;
using MySQL.Utilities.SysUtils;
using MySQL.Workbench;

namespace MySQL.GUI.Workbench
{
  public partial class MainForm : Form, IWorkbenchObserver
  {
    #region Member Variables

    static private String[] resourcePaths = {
      "",
      "images/ui",
      "images/icons",
      "images/grt",
      "images/grt/structs",
      "images/cursors",
      "images/home",
      "images/sql",
      "images/sql/mac",
    };

    // The Workbench context
    protected WbContext wbContext;
    // the GRT manager
    protected GrtManager grtManager;

    // A timer for helping backend timers.
    private System.Windows.Forms.Timer timer = null;

    // Special Forms
    private ModelOverviewForm workbenchPhysicalOverviewForm = null;

    private bool shuttingDown = false;
    private ImageList tabImageList = null;

    public MainPageDockDelegate dockDelegate = null;

    #endregion

    #region Constructors

    /// <summary>
    /// Standard constructor
    /// </summary>
    private MainForm()
    {
      InitializeComponent();
    }

    /// <summary>
    /// Constructor that takes a WbContext and passes it to the sub-forms that get created
    /// </summary>
    /// <param name="WbContext">The WbContext Backend Wrapper</param>
    public MainForm(WbContext WbContext)
      : this()
    {
      wbContext = WbContext;
      grtManager = wbContext.get_grt_manager();

      dockDelegate = new MainPageDockDelegate(this, null);

      tabImageList = new ImageList();
      tabImageList.ColorDepth = ColorDepth.Depth32Bit;
      tabImageList.ImageSize = new Size(18, 16);
      ImageListHelper.Add(ApplicationCommand(AppCommand.AppGetResourcePath, null, "WB_Home.png"), tabImageList);
      contentTabControl.ImageList = tabImageList;

      // Create a timer to be triggered when the backend needs
      timer = new System.Windows.Forms.Timer();
      timer.Tick += new EventHandler(timer_Tick);

      // Prepare Statusbar
      PictureBox statusStripImg = new PictureBox();
      statusStripImg.SizeMode = PictureBoxSizeMode.CenterImage;
      statusStripImg.Image = Resources.statusbar_separator;
      statusStripImg.BackColor = Color.Transparent;
      ToolStripControlHost host = new ToolStripControlHost(statusStripImg);
      host.Alignment = ToolStripItemAlignment.Right;
      mainStatusStrip.Items.Add(host);

      // output img
      statusStripImg = new PictureBox();
      statusStripImg.Name = "grtShellStripButton";
      statusStripImg.SizeMode = PictureBoxSizeMode.CenterImage;
      statusStripImg.Image = Resources.statusbar_output;
      statusStripImg.BackColor = Color.Transparent;
      statusStripImg.Click += new System.EventHandler(grtOutputImg_Click);
      mainFormToolTip.SetToolTip(statusStripImg, "Display Output Window");
      host = new ToolStripControlHost(statusStripImg);
      host.Alignment = ToolStripItemAlignment.Right;
      mainStatusStrip.Items.Add(host);

      statusStripImg = new PictureBox();
      statusStripImg.SizeMode = PictureBoxSizeMode.CenterImage;
      statusStripImg.Image = Resources.statusbar_separator;
      statusStripImg.BackColor = Color.Transparent;
      host = new ToolStripControlHost(statusStripImg);
      host.Alignment = ToolStripItemAlignment.Right;
      mainStatusStrip.Items.Add(host);

      ManagedNotificationCenter.AddObserver(this, "GNColorsChanged");
      ManagedNotificationCenter.AddObserver(this, "GNFocusChanged");
    }

    void Destroy()
    {
      ManagedNotificationCenter.RemoveObserver(this, "");

      // All documents are closed in Form_Closing.
    }

    #endregion

    #region Implement Interfaces

    delegate void DelegateFunc();

    public string StatusBarText
    {
      get { return statusText.Text; }
      set
      {
        if (InvokeRequired)
        {
          DelegateFunc f = delegate
          {
            statusText.Text = value.Replace("\r\n", "\n").Replace("\n", "");
          };

          BeginInvoke(f);
        }
        else
          statusText.Text = value.Replace("\r\n", "\n").Replace("\n", "");
      }
    }

    #endregion

    #region IWorkbenchObserver interface

    public void HandleNotification(string name, IntPtr sender, Dictionary<string, string> info)
    {
      switch (name)
      {
        case "GNColorsChanged":
          UpdateColors();
          break;

        case "GNFocusChanged":
          wbContext.validate_edit_menu();
          break;
      }
    }

    #endregion

    #region Properties

    public ModelOverviewForm WorkbenchPhysicalOverviewForm
    {
      get { return workbenchPhysicalOverviewForm; }
    }

    #endregion

    #region Callbacks

    public void ShowStatusText(String message)
    {
      StatusBarText = message;
    }

    public bool ShowProgress(String title, String detail, float progress)
    {
      if (progress < 0)
      {
        if (title != "" && detail != "")
          StatusBarText = title + ": " + detail;
        else if (title != "")
          StatusBarText = title;
        else
          StatusBarText = "Finished";

        statusProgress.Visible = false;
        return false;
      }
      else if (!statusProgress.Visible)
        statusProgress.Visible = true;

      if (title != "" && detail != "")
        StatusBarText = title + ": " + detail;
      else
        StatusBarText = title;

      statusProgress.Value = (int)(progress * 100);

      return false;
    }

    private bool RequestInputPressedOk;

    public void OnRequestInputButton(Object sender, EventArgs e)
    {
      Button b = sender as Button;
      if (b == null)
        return;
      Form f = b.Parent as Form;
      if (f == null)
        return;
      f.Close();
      RequestInputPressedOk = (b.Text == "OK");
    }

    public bool RequestInput(String title, int flags, out String result)
    {
      this.RequestInputPressedOk = false;
      result = "";

      Form dialog = new Form();
      if (flags != 0)
        dialog.Text = "Input Password";
      else
        dialog.Text = "Input Text";
      dialog.Size = new Size(350, 125);
      dialog.StartPosition = FormStartPosition.CenterParent;
      dialog.FormBorderStyle = FormBorderStyle.FixedDialog;

      Label prompt = new Label();
      prompt.Location = new Point(10, 5);
      prompt.AutoSize = true;
      prompt.Text = title;

      TextBox input = new TextBox();
      input.Location = new Point(10, 30);
      input.Size = new Size(dialog.Size.Width - (input.Location.X * 2 + 1), 50);
      if (flags != 0)
        input.PasswordChar = '*';

      Button ok = new Button();
      ok.Text = "OK"; // if you rename this also update OnRequestInputButton()
      ok.Location = new Point(dialog.Size.Width / 2 - ok.Size.Width - 10,
        input.Location.Y + input.Size.Height + 10);
      ok.Click += new EventHandler(OnRequestInputButton);

      Button cancel = new Button();
      cancel.Text = "Cancel";
      cancel.Location = new Point(dialog.Size.Width / 2 + 10,
        input.Location.Y + input.Size.Height + 10);
      cancel.Click += new EventHandler(OnRequestInputButton);

      dialog.AcceptButton = ok;
      dialog.CancelButton = cancel;

      dialog.Controls.Add(prompt);
      dialog.Controls.Add(input);
      dialog.Controls.Add(ok);
      dialog.Controls.Add(cancel);
      dialog.ShowDialog();

      if (RequestInputPressedOk)
        result = input.Text;

      return RequestInputPressedOk;
    }

    public String ApplicationCommand(AppCommand command, MySQL.Forms.AppViewImpl view, String str)
    {
      if (IsDisposed || Disposing)
        return "";

      String result = "";
      switch (command)
      {
        case AppCommand.AppQuit: // Not yet used.
          Close();
          break;
        case AppCommand.AppGetResourcePath:
          result = GetIconPath(str);
          break;
        case AppCommand.AppSetStatusText:
          StatusBarText = str;
          break;
        case AppCommand.AppGetBounds:
          // Returns the current bounds of the main window, encoded in a string (we only have one return type: string).
          // This can be used to center messages over the application window, regardless on which screen/monitor
          // the app windows currently is.
          result = String.Format("{0} {1} {2} {3} ", Left, Top, Width, Height);
          break;
      }
      return result;
    }

    public void RefreshGUI(RefreshType refresh, String str, IntPtr ptr)
    {
      if (IsDisposed || Disposing)
        return;

      SuspendLayout();

      switch (refresh)
      {
        case RefreshType.RefreshCloseDocument:
          {
            if (workbenchPhysicalOverviewForm != null)
            {
              workbenchPhysicalOverviewForm.ResetDocument(true);
              workbenchPhysicalOverviewForm.Close();
              workbenchPhysicalOverviewForm = null;
            }

            wbContext.flush_idle_tasks();
            wbContext.close_document_finish();

            break;
          }
        case RefreshType.RefreshNewDiagram:
          {
            BaseWindowsCanvasView canvas = BaseWindowsCanvasView.GetFromFixedId(ptr);
            if (canvas != null)
            {
              // Open only diagrams which were open when this model was closed last time.
              ModelDiagramForm modelDiagramForm = canvas.GetOwnerForm() as ModelDiagramForm;
              if (modelDiagramForm != null && !modelDiagramForm.DiagramWrapper.is_closed())
                DockDocument(modelDiagramForm, true, true);
            }
            break;
          }

        case RefreshType.RefreshNewModel: // A new model was loaded or created. Show UI.
          ShowPhysicalOverviewForm(true);

          // Let the backend create what is necessary.
          wbContext.new_model_finish();
          break;

        case RefreshType.RefreshDocument:
        case RefreshType.RefreshCloseEditor:
        case RefreshType.RefreshOverviewNodeInfo:
          ForwardRefreshToAllDocuments(refresh, str, ptr);
          break;

        case RefreshType.RefreshSchemaList:
        case RefreshType.RefreshSchemaNoReload:
        case RefreshType.RefreshSelection:
        case RefreshType.RefreshZoom:
          ForwardRefreshToActivDocument(refresh, str, ptr);
          break;

        case RefreshType.RefreshTimer:
          UpdateTimer();
          break;

        case RefreshType.RefreshFinishEdits:
          // Force all ongoing edits to be Commited (like in listview cells)
          try
          {
            Control activeControl = ControlUtilities.GetLeafActiveControl(this);
            if (activeControl != null)
            {
              if (activeControl.Parent is Aga.Controls.Tree.TreeViewAdv)
              {
                ActiveControl = null;
              }
            }
          }
          catch (Exception e)
          {
            Program.HandleException(e);
          }
          break;

        case RefreshType.RefreshOverviewNodeChildren:
          if (workbenchPhysicalOverviewForm != null)
            workbenchPhysicalOverviewForm.RefreshGUI(refresh, str, ptr);
          break;
      }

      ResumeLayout();
    }

    /// <summary>
    /// Creates a new diagram form with a given name and id. UI handling (e.g. docking) is done later.
    /// </summary>
    public BaseWindowsCanvasView CreateNewDiagram(string viewId, string name, IntPtr payload)
    {
      ModelDiagramForm modelDiagramForm = new ModelDiagramForm(wbContext, viewId, payload);

      modelDiagramForm.Text = name;
      modelDiagramForm.TabText = name;

      return modelDiagramForm.Canvas;
    }

    /// <summary>
    /// Frees a model diagram form once its backend was freed.
    /// </summary>
    public void DestroyView(BaseWindowsCanvasView canvasView)
    {
      ModelDiagramForm modelDiagramForm = canvasView.GetOwnerForm() as ModelDiagramForm;
      if (modelDiagramForm != null)
      {
        modelDiagramForm.Close();
        if (workbenchPhysicalOverviewForm != null)
          workbenchPhysicalOverviewForm.Activate();
      }
    }

    public void SwitchedView(BaseWindowsCanvasView canvasView)
    {
      ModelDiagramForm modelDiagramForm = canvasView.GetOwnerForm() as ModelDiagramForm;
      if (modelDiagramForm != null)
        DockDocument(modelDiagramForm, true, true);
    }

    public void ToolChanged(BaseWindowsCanvasView canvasView)
    {
      ModelDiagramForm modelDiagramForm = canvasView.GetOwnerForm() as ModelDiagramForm;
      if (modelDiagramForm != null)
        modelDiagramForm.OnToolChanged();
    }

    public IntPtr OpenPlugin(GrtManager GrtManager, GrtModule GrtModule, string AssemblyName,
      string ClassName, GrtValue GrtList, GUIPluginFlags flags)
    {
      IntPtr ptr = IntPtr.Zero;

      try
      {
        // Load assembly
        Assembly assembly = Assembly.LoadFrom(System.IO.Path.Combine(
          Application.StartupPath, AssemblyName));

        // Find class
        foreach (Type type in assembly.GetTypes())
        {
          if (type.IsClass == true && type.FullName.EndsWith("." + ClassName))
          {
            // use global grtManager
            Object[] args = { grtManager, GrtList };

            if (typeof(DockablePlugin).IsAssignableFrom(type))
            {
              // If ForceNewWindowFlag is not set and this is an object editor
              if ((GUIPluginFlags.ForceNewWindowFlag & flags) == 0 &&
                (GUIPluginFlags.StandaloneWindowFlag & flags) == 0 &&
                typeof(ObjectEditorPlugin).IsAssignableFrom(type))
              {
                // Check if a plugin of this type is already open. If we can find one on the active
                // document page then reuse that. If not try the other pages and close the first
                // editor of that plugin type we find.
                DockablePlugin plugin = null;
                if (contentTabControl.ActiveDocument is IWorkbenchDocument)
                  plugin = (contentTabControl.ActiveDocument as IWorkbenchDocument).FindPluginOfType(type);

                if (plugin == null)
                {
                  foreach (ITabDocument document in contentTabControl.Documents)
                    if (document is IWorkbenchDocument && document != contentTabControl.ActiveDocument)
                    {
                      if ((document as IWorkbenchDocument).ClosePluginOfType(type))
                        break;
                    }
                }
                else
                {
                  // If so, try to change the current GRT Object and exit
                  if ((plugin as ObjectEditorView).EditorPlugin.ReinitWithArguments(GrtList))
                  {
                    // Unregister plugin from back end.
                    wbContext.close_gui_plugin(plugin.GetFixedPtr());

                    // return old Ptr as Ptr for the new editor
                    return plugin.GetFixedPtr();
                  }
                  else
                    Logger.LogDebug("Plugins .NET", 1, String.Format("Object editor for {0} does not support reuse", ClassName));
                }
              }

              ObjectEditorPlugin objectEditorPlugin = Activator.CreateInstance(type, args) as ObjectEditorPlugin;
              if (objectEditorPlugin != null)
              {
                objectEditorPlugin.Context = wbContext;

                ObjectEditorView pluginForm;
                if ((GUIPluginFlags.StandaloneWindowFlag & flags) != 0)
                  pluginForm = new StandaloneWindowPlugin(objectEditorPlugin);
                else
                  pluginForm = new ObjectEditorView(objectEditorPlugin);

                // Get fixed ptr
                ptr = pluginForm.GetFixedPtr();

                if (ptr == IntPtr.Zero)
                  throw new Exception("Internal error: could not pin memory for plugin.");
              }

              break;
            }
            else if (typeof(Plugin).IsAssignableFrom(type))
            {
              Plugin plugin = Activator.CreateInstance(type, args) as Plugin;

              try
              {
                plugin.Execute();
              }
              catch (Exception e)
              {
                Program.HandleException(e);
              }
            }
          }
        }
      }
      catch (Exception e)
      {
        Program.HandleException(e);
      }

      return ptr;
    }

    public void CreateMainFormView(String ViewName, UIForm viewBE)
    {
      try
      {
        DockablePlugin form = null;
        switch (ViewName)
        {
          case "dbquery":
            form = new SqlIdeForm(wbContext, viewBE);
            break;
        }
        if (form != null)
        {
          form.Context = wbContext;
          DockDocument(form, true, true);
        }
      }
      catch (Exception e)
      {
        Program.HandleException(e);
      }
    }

    public void ShowPlugin(IntPtr ptr)
    {
      if (ptr != IntPtr.Zero)
      {
        TabDocument pluginForm = DockablePlugin.GetFromFixedPtr(ptr);
        if (pluginForm != null)
        {
          if (pluginForm is ObjectEditorPlugin)
          {
            ObjectEditorView objectEditorView = (pluginForm as ObjectEditorPlugin).ContainerForm;
            if (objectEditorView is StandaloneWindowPlugin)
              objectEditorView.Show();
            else if (objectEditorView is ObjectEditorView)
              DockDocument(objectEditorView, false, true);
          }
          else if (pluginForm is DockablePlugin && !(pluginForm is WizardPlugin))
            DockDocument(pluginForm, false, true);
          else
            pluginForm.ShowDialog();
        }
      }
    }

    public void HidePlugin(IntPtr ptr)
    {
      if (ptr != IntPtr.Zero)
      {
        TabDocument editorForm = DockablePlugin.GetFromFixedPtr(ptr);

        if (editorForm is ObjectEditorPlugin)
          editorForm = (editorForm as ObjectEditorPlugin).ContainerForm;

        if (editorForm != null)
          editorForm.Hide();
      }
    }
    #endregion

    #region DockingPointDelegate

    public bool select_view(AppViewImpl view)
    {
      foreach (ITabDocument document in contentTabControl.Documents)
      {
        if (document is AppViewDockContent)
        {
          AppViewDockContent content = (document as AppViewDockContent);
          if (content == view.GetHost())
          {
            content.Activate();
            return true;
          }
        }
      }
      return false;
    }

    public void set_view_title(AppViewImpl view, String str)
    {
      ITabDocument document = view.GetHost();
      if (contentTabControl.IndexFromDocument(document) > 0)
        view.GetHost().TabText = str;
    }

    public void get_size(out int w, out int h)
    {
      w = contentTabControl.Width;
      h = contentTabControl.Height;
    }

    #endregion

    #region Edit Menu Handlers

    private void EditUndo()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase)
        (activeControl as TextBoxBase).Undo();
      else
        if (activeControl is ComboBox)
          Win32Api.Undo(activeControl as ComboBox);
        else
          if (activeControl is ScintillaControl)
            (activeControl as ScintillaControl).Undo();
          else
            if (wbContext.edit_can_undo())
              wbContext.edit_undo();
    }

    private bool EditCanUndo()
    {
      // If we are on a model form then edit actions are controlled by the backend.
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == workbenchPhysicalOverviewForm)
        return wbContext.edit_can_undo();

      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox)
        return true;
      else
        if (activeControl is ScintillaControl)
          return (activeControl as ScintillaControl).CanUndo;
        else
          return wbContext.edit_can_undo();
    }

    private void EditRedo()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox)
        return;
      else
        if (activeControl is ScintillaControl)
          (activeControl as ScintillaControl).Redo();
        else
          if (wbContext.edit_can_redo())
            wbContext.edit_redo();
    }

    private bool EditCanRedo()
    {
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == workbenchPhysicalOverviewForm)
        return wbContext.edit_can_redo();

      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox)
        return true;
      else
        if (activeControl is ScintillaControl)
          return (activeControl as ScintillaControl).CanRedo;
        else
          return wbContext.edit_can_redo();
    }

    private void EditCopy()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase)
        (activeControl as TextBoxBase).Copy();
      else
        if (activeControl is ComboBox)
          Win32Api.Copy(activeControl as ComboBox);
        else
          if (activeControl is ScintillaControl)
            (activeControl as ScintillaControl).Copy();
          else
            if (activeControl is GridView)
              (activeControl as GridView).Copy();
            else
              if (wbContext.edit_can_copy())
                wbContext.edit_copy();
    }

    private bool EditCanCopy()
    {
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == workbenchPhysicalOverviewForm)
        return wbContext.edit_can_copy();

      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox)
        return true;
      else
        if (activeControl is ScintillaControl)
          return (activeControl as ScintillaControl).CanCopy;
        else
          if (activeControl is GridView)
            return (activeControl as GridView).CanCopy;
          else
            return wbContext.edit_can_copy();
    }

    private void EditCut()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase)
        (activeControl as TextBoxBase).Cut();
      else
        if (activeControl is ComboBox)
          Win32Api.Cut(activeControl as ComboBox);
        else
          if (activeControl is ScintillaControl)
            (activeControl as ScintillaControl).Cut();
          else
            if (wbContext.edit_can_copy()) // TODO: check really necessary?
              wbContext.edit_cut();
    }

    private bool EditCanCut()
    {
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == workbenchPhysicalOverviewForm)
        return wbContext.edit_can_cut();

      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox)
        return false;
      else
        if (activeControl is ScintillaControl)
          return (activeControl as ScintillaControl).CanCut;
        else
          if ((activeControl is TreeViewAdv) || (activeControl != null && activeControl.Parent is TreeViewAdv))
            return false; // Returning false tells the backend not to take over the operation.
          else
            return wbContext.edit_can_cut();
    }

    private void EditPaste()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase)
        (activeControl as TextBoxBase).Paste();
      else
        if (activeControl is ComboBox)
          Win32Api.Paste(activeControl as ComboBox);
        else
          if (activeControl is ScintillaControl)
            (activeControl as ScintillaControl).Paste();
          else
            if (wbContext.edit_can_paste())
              wbContext.edit_paste();
    }

    private bool EditCanPaste()
    {
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == workbenchPhysicalOverviewForm)
        return wbContext.edit_can_paste();

      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox || activeControl is GridView)
        return false;
      else
        if (activeControl is ScintillaControl)
          return (activeControl as ScintillaControl).CanPaste;
        else
          return wbContext.edit_can_paste();
    }

    private void EditSelectAll()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is ComboBox)
        (activeControl as ComboBox).SelectAll();
      else
        if (activeControl is TextBoxBase)
          (activeControl as TextBoxBase).SelectAll();
        else
          if (activeControl is ScintillaControl)
            (activeControl as ScintillaControl).SelectAll();
          else
            if (activeControl is DataGridView)
              (activeControl as DataGridView).SelectAll();
            else
              if (wbContext.edit_can_select_all())
                wbContext.edit_select_all();
    }

    private bool EditCanSelectAll()
    {
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == workbenchPhysicalOverviewForm)
        return wbContext.edit_can_select_all();

      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox || activeControl is ScintillaControl ||
        activeControl is DataGridView)
        return true;
      else
        return wbContext.edit_can_select_all();
    }

    private void EditDelete()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase)
        (activeControl as TextBoxBase).SelectedText = "";
      else
        if (activeControl is ScintillaControl)
          (activeControl as ScintillaControl).Delete();
        else
          if (wbContext.edit_can_delete())
            wbContext.edit_delete();
    }

    private bool EditCanDelete()
    {
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == workbenchPhysicalOverviewForm)
        return wbContext.edit_can_delete();

      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is TextBoxBase || activeControl is ComboBox)
        return false;
      else
        if (activeControl is ScintillaControl)
          return (activeControl as ScintillaControl).CanDelete;
        else
          return wbContext.edit_can_delete();
    }

    private void EditFind()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is ScintillaControl)
        (activeControl as ScintillaControl).ShowFindPanel(false);
      else
      {
        // Not an editor control. So try focusing the search box in the toolbar (if there's any).
        IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
        if (wbDocument != null)
        {
          wbContext.focus_search_box(wbDocument.BackendForm);

          // and then search
          string searchString = wbContext.get_search_string(wbDocument.BackendForm);
          if (searchString != "")
          {
            if (!wbContext.try_searching_diagram(searchString))
            {
              // if active main tab is Overview, search it
              if (contentTabControl.ActiveDocument == workbenchPhysicalOverviewForm)
                workbenchPhysicalOverviewForm.SearchAndFocusNode(searchString);
            }
          }
        }
      }
    }

    private bool EditCanFind()
    {
      IWorkbenchDocument wbDocument = contentTabControl.ActiveDocument as IWorkbenchDocument;
      if (wbDocument == null && !EditCanFindReplace())
        return false;

      return true;
    }

    private void EditFindReplace()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is ScintillaControl)
        (activeControl as ScintillaControl).ShowFindPanel(true);
    }

    private bool EditCanFindReplace()
    {
      Control activeControl = ControlUtilities.GetLeafActiveControl(this);
      if (activeControl is ScintillaControl)
        return true;
      return false;
    }

    private void UpdateColors()
    {
      contentTabControl.RenderWithGlow = false;
      contentTabControl.UpdateColors();
      contentTabControl.BackgroundColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false);
      mainStatusStrip.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false);
      mainStatusStrip.ForeColor = Conversions.GetApplicationColor(ApplicationColor.AppColorStatusbar, true);
      statusText.ForeColor = Conversions.GetApplicationColor(ApplicationColor.AppColorStatusbar, true);

      foreach (ITabDocument document in contentTabControl.Documents)
      {
        if (document is IWorkbenchDocument)
          (document as IWorkbenchDocument).UpdateColors();
        else
          if (document is AppViewDockContent)
            (document as AppViewDockContent).UpdateColors();
      }
    }

    /// <summary>
    /// Some initialization done after everything else is set up and available.
    /// </summary>
    public void PostInit()
    {
      UpdateColors();

      List<String> commands = new List<string>();

      commands.Add("overview.mysql_model");
      commands.Add("diagram_size");
      commands.Add("view_model_navigator");
      commands.Add("view_catalog");
      commands.Add("view_layers");
      commands.Add("view_user_datatypes");
      commands.Add("view_object_properties");
      commands.Add("view_object_description");
      commands.Add("view_undo_history");
      commands.Add("reset_layout");
      commands.Add("wb.page_setup");
      commands.Add("help_version_check");
      commands.Add("wb.toggleSidebar");
      commands.Add("wb.toggleSecondarySidebar");
      commands.Add("wb.next_tab");
      commands.Add("wb.back_tab");
      commands.Add("closetab");
      commands.Add("close_tab");

      // SQL IDE specific commands.
      commands.Add("wb.toggleOutputArea");
      commands.Add("close_editor");

      wbContext.add_frontend_commands(commands);

      // special command in Windows implemented in wrapper to perform 
      // edit menu actions
      wbContext.set_edit_menu_delegates(
        EditUndo, EditCanUndo,
        EditRedo, EditCanRedo,
        EditCopy, EditCanCopy,
        EditCut, EditCanCut, EditPaste, EditCanPaste,
        EditSelectAll, EditCanSelectAll, EditDelete, EditCanDelete,
        EditFind, EditCanFind,
        EditFindReplace, EditCanFindReplace);
    }

    public void PerformCommand(String command)
    {
      switch (command)
      {
        case "overview.mysql_model":
          ShowPhysicalOverviewForm();
          break;
        case "diagram_size":
          ShowDiagramOptionsForm();
          break;
        case "reset_layout":
          ResetWindowLayout();
          break;
        case "wb.page_setup":
          ShowPageSettingsForm();
          break;
        case "closetab":
          CloseActiveTab();
          break;
        case "close_tab":
          contentTabControl.CloseTabPage(contentTabControl.SelectedTab);
          break;
        case "wb.next_tab":
          if (contentTabControl.SelectedIndex == contentTabControl.TabCount - 1)
            contentTabControl.SelectedIndex = 0;
          else
            contentTabControl.SelectedIndex++;
          break;
        case "wb.back_tab":
          if (contentTabControl.SelectedIndex == 0)
            contentTabControl.SelectedIndex = contentTabControl.TabCount - 1;
          else
            contentTabControl.SelectedIndex--;
          break;
        case "help_version_check":
          Program.CheckForNewVersion();
          break;
        case "wb.toggleSidebar":
        case "wb.toggleSecondarySidebar":
          ForwardCommandToActivDocument(command);
          break;
        default:
          ForwardCommandToActivDocument(command);
          break;
      }
    }

    /// <summary>
    /// Closes the active tab page in tab controls of a specific type. That is, all tabs with entities
    /// which should be closed as a whole (e.g. model + diagram documents, object editors).
    /// </summary>
    private void CloseActiveTab()
    {
      // Start at the bottom of the hierarchy and go up. This will include also tab controls
      // from child forms (e.g. model diagram, model overview, SQL IDE etc.), so we don't need to
      // forward this close call and handle it in different places.
      Control control = ControlUtilities.GetLeafActiveControl(this);
      if (control is FlatTabControl)
        control = (control as FlatTabControl).SelectedTab;

      Control host = null;
      while (host == null)
      {
        while ((control != null) && !(control is TabPage))
          control = control.Parent;

        if (control == null)
          break;

        host = control.Parent;
        if (host is FlatTabControl)
        {
          // Reset host if the tab control is not what we are looking for and start over.
          FlatTabControl.TabStyleType style = (host as FlatTabControl).TabStyle;
          if (style == FlatTabControl.TabStyleType.TopNormal || style == FlatTabControl.TabStyleType.TopTransparent)
            break;
        }
        control = host;
        host = null;
      }

      TabPage page = control as TabPage;
      if (host != null && page.Text.Length > 0) // Don't close the home screen.
        (host as FlatTabControl).CloseTabPage(control as TabPage);
    }

    private void ShowPageSettingsForm()
    {
      PageSettingsForm form = new PageSettingsForm(wbContext);

      form.ShowDialog();
      form.Dispose();
    }

    private void ShowDiagramOptionsForm()
    {
      DiagramOptionsForm form = new DiagramOptionsForm(wbContext);

      form.ShowDialog();
      form.Close();
    }

    public String ShowFileDialog(String type, String title, String extensions)
    {
      FileDialog dialog;

      if (type == "save")
      {
        dialog = new SaveFileDialog();
      }
      else if (type == "open")
      {
        dialog = new OpenFileDialog();
      }
      else
        return "";

      String[] exts = extensions.Split(new char[] { ',' });

      dialog.RestoreDirectory = true;
      dialog.Title = title;
      dialog.DefaultExt = exts[0];
      String filter = "";
      foreach (String ext in exts)
      {
        if (ext.Contains("|"))
          filter = filter + ext + "|";
        else if (ext == "mwb")
          filter = filter + String.Format("{0} (*.mwb)|*.mwb|",
            "MySQL Workbench Models");
        else if (ext == "sql")
          filter = filter + String.Format("{0} (*.sql)|*.sql|",
            "SQL Script Files");
        else
          filter = filter + String.Format("{0} files (*.{0})|*.{0}|", ext);
      }
      filter = filter + String.Format("{0} (*.*)|*.*", "All files");

      dialog.Filter = filter;

      if (dialog.ShowDialog() == DialogResult.OK)
        return dialog.FileName;

      return "";
    }

    public bool QuitApplication()
    {
      if (!shuttingDown)
        Close();

      return true;
    }

    #endregion

    #region Other Application Logic

    protected override void WndProc(ref Message m)
    {
      if (m.Msg == (int)WM.ACTIVATEAPP && m.WParam != IntPtr.Zero)
      {
        ManagedNotificationCenter.Send("GNApplicationActivated", IntPtr.Zero);
      }
      base.WndProc(ref m);
    }

    /// <summary>
    /// Try to find the given file in known locations. If name is empty return the absolute application
    /// path (as common root for all resource subfolders).
    /// </summary>
    /// <param name="name"></param>
    static public string GetIconPath(string name)
    {
      if (name == "")
        return Environment.CurrentDirectory;

      string result = "";
      if (File.Exists(name) || Directory.Exists(name))
        return name;
      foreach (String path in resourcePaths)
        if (File.Exists(path + "/" + name))
        {
          result = path + "/" + name;
          break;
        }
      return result;
    }

    private void grtOutputImg_Click(object sender, EventArgs e)
    {
      wbContext.show_output();
    }

    public void ShowPhysicalOverviewForm(bool isNew = false)
    {
      if (workbenchPhysicalOverviewForm == null)
        workbenchPhysicalOverviewForm = new ModelOverviewForm(wbContext, wbContext.get_physical_overview());

      //SuspendLayout();
      //contentTabControl.SuspendLayout();
      DockDocument(workbenchPhysicalOverviewForm, true, true);
      Update();
      if (isNew)
        workbenchPhysicalOverviewForm.RebuildModelContents();
      //contentTabControl.ResumeLayout(true);
      //ResumeLayout();

      workbenchPhysicalOverviewForm.Activate();
    }

    public void LockGUI(bool value)
    {
      if (value)
        Cursor.Current = Cursors.WaitCursor;
      else
        Cursor.Current = null;
    }

    public void ResetWindowLayout()
    {
      // Ignore.
    }

    /// <summary>
    /// Restores the form to a usable state and activates it.
    /// </summary>
    public void Activate(bool restoreIfMinimized)
    {
      if (restoreIfMinimized && WindowState == FormWindowState.Minimized)
        WindowState = FormWindowState.Normal;
      Activate();
    }

    /// <summary>
    /// Make the upper part of the window extend the glass frame (if we are on Aero).
    /// </summary>
    /// <param name="toolbarHeight"></param>
    private void AdjustGlassFrame(int toolbarHeight)
    {
      if (ControlUtilities.IsCompositionEnabled())
      {
        int topAreaHeight = contentTabControl.ItemSize.Height + contentTabControl.Margin.Top +
          toolbarHeight + Margin.Top;

        // For Win8 glass area extension is producing weird results (white frames).
        // So we only extend the top area.
        Win32.MARGINS margins;
        if (ControlUtilities.IsWin8OrAbove())
          margins = new Win32.MARGINS(0, topAreaHeight - 3, 0, 0);
        else
          margins = new Win32.MARGINS(3, topAreaHeight, 3, 2);
        Win32.DwmExtendFrameIntoClientArea(Handle, margins);
      }
    }

    /// <summary>
    /// Prepares the given document to conform to the overall layout. This requires to draw an
    /// intermediate set of controls (panels) and add menu + toolbar.
    /// </summary>
    /// <param name="document">The document to adjust, which comes with the actual content.</param>
    /// <param name="backend">The backend UI form used to get toolbar and menu.</param>
    /// <param name="withPadding">Indicates if we need padding around the content or not.</param>
    private void SetupDockLayout(ITabDocument document, UIForm backend, bool withPadding)
    {
      // Setting up the layout requires some additional nesting of controls.
      // A tab document should have only one container as top level control, which we re-host.
      Control root = document.Content;
      Control content = (root.Controls.Count > 0) ? root.Controls[0] : null;

      // If there's already a drawable panel then this document has already been set up
      // and just needs to be shown. Happens for re-used views (like the Output view).
      if (content is DrawablePanel)
        return;

      root.Controls.Clear();

      // Content area.
      DrawablePanel contentPanel = new DrawablePanel();
      contentPanel.BackColor = MySQL.Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false);
      contentPanel.CustomBackground = false;
      contentPanel.Dock = DockStyle.Fill;
      contentPanel.Padding = withPadding ? new Padding(6) : new Padding(0);
      root.Controls.Add(contentPanel);
      if (content != null)
      {
        contentPanel.Controls.Add(content);
        content.Dock = DockStyle.Fill;
      }

      // Menu + toolbar area. We have two types of tab documents:
      // - appview based (and hence backend-managed)
      // - native Form (with interface via WBContext).
      // Appview based documents have own methods to return their menu and toolbar because
      // retrieval via managed code requires a managed UIForm. However appview classes are
      // already managed via mforms and adding another reference management via UIForm
      // will lead to unpredictable results.
      ToolStrip toolbar;
      MenuStrip menu;
      if (backend != null)
      {
        // Retrieval via WBContext will return a default toolbar if no other is defined.
        toolbar = wbContext.toolbar_for_form(backend);
        menu = wbContext.menu_for_form(backend);
      }
      else
      {
        AppViewDockContent dockContent = document as AppViewDockContent;
        toolbar = dockContent.GetAppView().GetToolBar();
        if (toolbar == null)
          toolbar = wbContext.toolbar_for_form(null); // Currently never returns a toolbar.
        menu = wbContext.menu_for_appview(dockContent);
      }
      DrawablePanel menuPanel = new DrawablePanel();
      if (toolbar != null || menu != null)
      {
        menuPanel.BackColor = Color.Transparent;
        menuPanel.CustomBackground = true;
        menuPanel.Dock = DockStyle.Top;
        menuPanel.AutoSize = true;
        root.Controls.Add(menuPanel);
        if (toolbar != null)
        {
          toolbar.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainTab, false);

          menuPanel.Controls.Add(toolbar);
          toolbar.Dock = DockStyle.Top;
        }

        if (menu != null)
        {
          menu.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainTab, false);

          menuPanel.Controls.Add(menu);
          menu.Dock = DockStyle.Top;
          menu.AutoSize = true;
        }
      }
    }

    #endregion

    #region Document Handling

    private void tabControl_ContentAdded(object sender, ControlEventArgs e)
    {
      if (e.Control is TabPage)
      {
        ITabDocument document = (sender as FlatTabControl).DocumentFromPage(e.Control as TabPage);
        if (document is ModelDiagramForm)
        {
          ModelDiagramForm form = document as ModelDiagramForm;
          ((ModelDiagramFormWrapper)form.BackendForm).set_closed(false);
        }
      }
    }

    private void tabControl_ContentRemoved(object sender, ControlEventArgs e)
    {
      if (e.Control is TabPage)
      {
        ITabDocument document = (sender as FlatTabControl).DocumentFromPage(e.Control as TabPage);
        if (document is ModelDiagramForm)
        {
          ModelDiagramForm form = document as ModelDiagramForm;
          ((ModelDiagramFormWrapper)form.BackendForm).set_closed(true);
        }
      }
    }

    private void SetEnableForMenuItem(ToolStripItem item, bool enabled)
    {
      item.Enabled = enabled;
      if (item is ToolStripMenuItem)
      {
        foreach (ToolStripItem subitem in (item as ToolStripMenuItem).DropDownItems)
          SetEnableForMenuItem(subitem, enabled);
      }
    }

    private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
    {
      // Reset status bar to clean up any previously done action message.
      // Hide the statusbar on the home page, though.
      StatusBarText = "Ready";

      // The home screen tab has no text since we only use an icon there.
      // So we can use this as quick check here.
      mainStatusStrip.Visible = contentTabControl.SelectedIndex > -1 &&
        (contentTabControl.SelectedTab.Text.Length > 0);

      // Disable embedded menus for all inactive pages.
      ITabDocument[] documents = contentTabControl.DocumentsToArray();
      for (int i = 0; i < documents.Length; i++)
      {
        AppViewDockContent content = documents[i] as AppViewDockContent;
        if (content != null)
        {
          MenuStrip menu = content.GetAppView().GetMenuBar();
          if (menu != null)
          {
            menu.Enabled = (contentTabControl.SelectedIndex == i);
            foreach (ToolStripItem item in menu.Items)
              SetEnableForMenuItem(item, menu.Enabled);
          }
        }
        else
        {
          IWorkbenchDocument wbDocument = documents[i] as IWorkbenchDocument;
          if (wbDocument != null)
          {
            MenuStrip menu = wbContext.menu_for_form(wbDocument.BackendForm);
            if (menu != null)
            {
              menu.Enabled = (contentTabControl.SelectedIndex == i);
              foreach (ToolStripItem item in menu.Items)
                SetEnableForMenuItem(item, menu.Enabled);
            }
          }
        }
      }

      // For the glass effect behind the tabs (and the proper content drawing) we need the
      // height of the tabs as well as menu and toolbar of the active tab.
      int topAreaHeight = 0;

      // Suspend dock layout to prevent flicker.
      SuspendLayout();
      contentTabControl.SuspendLayout();
      try
      {
        FlatTabControl tabControl = sender as FlatTabControl;
        if (tabControl.ActiveDocument != null)
        {
          ITabDocument activeDocument = tabControl.ActiveDocument;
          activeDocument.Activate();
          topAreaHeight += activeDocument.ToolbarHeight;

          if (activeDocument is IWorkbenchDocument)
          {
            IWorkbenchDocument wbDoc = activeDocument as IWorkbenchDocument;

            // If the current View is changed, update ModelCatalogForm
            if (activeDocument is ModelDiagramForm)
            {
              ModelDiagramForm form = activeDocument as ModelDiagramForm;

              wbContext.set_active_form(form.BackendForm);

              form.FocusCanvasControl();
            }
            else
            {
              wbContext.set_active_form(wbDoc.BackendForm);
            }
          }
          else if (activeDocument is Plugins.DockablePlugin)
          {
            Plugins.DockablePlugin form = activeDocument as Plugins.DockablePlugin;
            wbContext.set_active_form(form.BackendForm);
          }
          else if (activeDocument is UIForm)
          {
            wbContext.set_active_form(activeDocument as UIForm);
          }
          else if (activeDocument is MySQL.Forms.AppViewDockContent)
          {
            MySQL.Forms.AppViewDockContent form = activeDocument as MySQL.Forms.AppViewDockContent;

            wbContext.set_active_form_from_appview(form);
          }
          else
            wbContext.set_active_form(null);
        }
        else
          wbContext.set_active_form(null);
      }
      finally
      {
        // Resume suspended dock layout.
        contentTabControl.ResumeLayout(true);
        ResumeLayout();
      }

      // Different pages might have a different height of the menu/toolbar area.
      AdjustGlassFrame(topAreaHeight);
    }

    private void ForwardRefreshToActivDocument(RefreshType refresh, String str, IntPtr ptr)
    {
      if (contentTabControl.ActiveDocument is IWorkbenchDocument)
        (contentTabControl.ActiveDocument as IWorkbenchDocument).RefreshGUI(refresh, str, ptr);
    }

    private void ForwardRefreshToAllDocuments(RefreshType refresh, String str, IntPtr ptr)
    {
      foreach (ITabDocument document in contentTabControl.Documents)
        if (document is IWorkbenchDocument)
          (document as IWorkbenchDocument).RefreshGUI(refresh, str, ptr);
    }

    private void ForwardCommandToActivDocument(String command)
    {
      if (contentTabControl.ActiveDocument is IWorkbenchDocument)
        (contentTabControl.ActiveDocument as IWorkbenchDocument).PerformCommand(command);
    }

    private void ForwardCommandToAllDocuments(String command)
    {
      foreach (ITabDocument document in contentTabControl.Documents)
        if (document is IWorkbenchDocument)
          (document as IWorkbenchDocument).PerformCommand(command);
    }

    /// <summary>
    /// Docks the given document to either the own tab control (as main document) or as
    /// editor window to the currently active main document.
    /// </summary>
    /// <param name="document">The document to dock.</param>
    /// <param name="main">If true then dock that document to our own tab control, otherwise
    /// dock it to the currently active main document.</param>
    public void DockDocument(ITabDocument document, bool main, bool activate)
    {
      if (document is IWorkbenchDocument)
        (document as IWorkbenchDocument).UpdateColors();
      else
        if (document is AppViewDockContent)
          (document as AppViewDockContent).UpdateColors();

      if (main)
      {
        int index = -1;
        if (!contentTabControl.HasDocument(document))
        {
          IWorkbenchDocument wbDocument = document as IWorkbenchDocument;
          SetupDockLayout(document, (wbDocument != null) ? wbDocument.BackendForm : null, contentTabControl.TabCount > 0);
          index = contentTabControl.AddDocument(document);
        }

        if (index == 0) // Replace the title by an icon.
        {
          contentTabControl.TabPages[index].ImageIndex = 0;
          contentTabControl.SetCloseButtonVisibility(0, FlatTabControl.CloseButtonVisiblity.HideButton);
        }

        if (activate)
        {
          document.Activate();
          AdjustGlassFrame(document.ToolbarHeight);
        }
      }
      else
      {
        ITabDocument host = contentTabControl.ActiveDocument;
        if (host is ModelDiagramForm)
          (host as ModelDiagramForm).DockDocument(document, activate);
        else
          if (host is ModelOverviewForm)
            (host as ModelOverviewForm).DockDocument(document, activate);
          else
            if (host is SqlIdeForm)
              (host as SqlIdeForm).DockDocument(document, activate);
      }
    }

    /// <summary>
    /// Undocks the given document from our tab control if it is there.
    /// If not forward the call to the currently active document.
    /// </summary>
    /// <param name="document"></param>
    public void UndockDocument(ITabDocument document)
    {
      if (contentTabControl.HasDocument(document))
        contentTabControl.RemoveDocument(document);
      else
      {
        ITabDocument host = contentTabControl.ActiveDocument;
        if (host is ModelDiagramForm)
          (host as ModelDiagramForm).UndockDocument(document);
        else
          if (host is ModelOverviewForm)
            (host as ModelOverviewForm).UndockDocument(document);
          else
            if (host is SqlIdeForm)
              (host as SqlIdeForm).UndockDocument(document);
      }
    }

    #endregion

    #region UI Event Handling

    private void spacerPanel_Paint(object sender, PaintEventArgs e)
    {
      Graphics g = e.Graphics;

      g.DrawLine(Pens.White, e.ClipRectangle.Left, 1, e.ClipRectangle.Right - 1, 1);
    }

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
      //wbContext.handle_key_event(true, e);
    }

    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
      // Temporarily remove the focus from what ever control has it currently.
      // This way controls that save content on leave get their chance.
      Focus();

      shuttingDown = true;

      // Query all documents if they can close in advance, so they stay open if one says nay.
      // For historical reasons diagram and model overview pages are on the same tab level
      // (the main one) even though diagrams are subordinated to model tabs. This means we have
      // to do two rounds to ensure diagram tabs are checked before the model tabs are.
      foreach (ITabDocument document in contentTabControl.Documents)
      {
        if (document is ModelDiagramForm)
        {
          e.Cancel = !(document as IWorkbenchDocument).CanCloseDocument();

          // Make the page active that refused to close.
          if (e.Cancel)
          {
            document.Activate();
            break;
          }
        }
      }

      if (!e.Cancel)
      {
        foreach (ITabDocument document in contentTabControl.Documents)
        {
          if (document is ModelDiagramForm)
            continue;

          if (document is IWorkbenchDocument)
            e.Cancel = !(document as IWorkbenchDocument).CanCloseDocument();
          else
            if (document is MySQL.Forms.AppViewDockContent)
            {
              MySQL.Forms.AppViewDockContent content = document as MySQL.Forms.AppViewDockContent;
              e.Cancel = !content.GetAppView().DocumentClosing();
            }

          if (e.Cancel)
          {
            // Make the page active that refused to close.
            document.Activate();
            break;
          }
        }
      }

      if (!e.Cancel)
        e.Cancel = !wbContext.request_quit();

      if (e.Cancel)
      {
        // Reset termination flag, in case it was set.
        grtManager.resetTermination();
        shuttingDown = false;
        return;
      }

      // Restore previous clipboard chain.
      SaveFormState();

      Hide();

      // Go through each workbench document and close it, now unconditionally.
      contentTabControl.SuspendLayout();
      try
      {
        ITabDocument[] documents = contentTabControl.DocumentsToArray();

        // Close all documents (editors implement the document interface too, so we get them
        // with that loop as well).
        for (int i = documents.Length - 1; i >= 0; i--)
        {
          // WBA is closed via the appview's DocumentClosing() function not by normal closing.
          if (documents[i] is MySQL.Forms.AppViewDockContent)
          {
            MySQL.Forms.AppViewDockContent content = documents[i] as MySQL.Forms.AppViewDockContent;
            content.GetAppView().DocumentClosing();
          }
          else
            documents[i].Close();
        }
        workbenchPhysicalOverviewForm = null;
      }
      finally
      {
        contentTabControl.ResumeLayout();
      }

      wbContext.perform_quit();
    }

    private void LoadFormState()
    {
      int x = wbContext.read_state("left", "mainform", 100);
      int y = wbContext.read_state("top", "mainform", 100);
      int w = wbContext.read_state("width", "mainform", 1200);
      int h = wbContext.read_state("height", "mainform", 900);

      // Sanity checks to avoid restoring the application into a state where it cannot be reached.
      if (x + w < 100)
        x = 100 - w;
      if (y + h < 100)
        y = 100 - h;
      Rectangle workingArea = Screen.GetWorkingArea(new Point(x, y));
      if (x > workingArea.Right - 100)
        x = workingArea.Right - 100;
      if (y > workingArea.Bottom - 100)
        y = workingArea.Bottom - 100;

      // First set the bounds then the state. If the window is maximized this will properly set
      // RestoreBounds so un-maximizing will later do the right job.
      Bounds = new Rectangle(x, y, w, h);
      WindowState = (FormWindowState)wbContext.read_state("windowstate", "mainform", (int)FormWindowState.Normal);

      // Don't restore into minimized state. Confusing for the user and makes trouble for layouting.
      if (WindowState == FormWindowState.Minimized)
        WindowState = FormWindowState.Normal;
    }

    private void SaveFormState()
    {
      wbContext.save_state("windowstate", "mainform", (int)WindowState);

      if (WindowState == FormWindowState.Normal)
      {
        wbContext.save_state("left", "mainform", Left);
        wbContext.save_state("top", "mainform", Top);
        wbContext.save_state("width", "mainform", Width);
        wbContext.save_state("height", "mainform", Height);
      }
      else
      {
        wbContext.save_state("left", "mainform", RestoreBounds.Left);
        wbContext.save_state("top", "mainform", RestoreBounds.Top);
        wbContext.save_state("width", "mainform", RestoreBounds.Width);
        wbContext.save_state("height", "mainform", RestoreBounds.Height);
      }

      bool outputVisible = false;
      foreach (ITabDocument content in contentTabControl.Documents)
      {
        if (content.TabText == "Output")
        {
          outputVisible = true;
          break;
        }
      }
      wbContext.save_state("outputvisible", "mainform", outputVisible ? 1 : 0);
    }

    private void MainForm_Load(object sender, EventArgs e)
    {
      LoadFormState();

      BringToFront();
      Activate();
    }


    public void UpdateTimer()
    {
      int interval = (int)(1000 * wbContext.delay_for_next_timer());
      if (interval > 0)
      {
        timer.Interval = interval;
        timer.Start();
      }
      else if (interval < 0)
        timer.Stop();
      else
      {
        timer.Interval = 1;
        timer.Start();
      }
    }

    private void timer_Tick(object sender, EventArgs e)
    {
      wbContext.flush_timers();
      UpdateTimer();
    }

    private void tabControl_TabClosing(object sender, TabClosingEventArgs e)
    {
      e.canClose = true;

      ITabDocument document = (sender as FlatTabControl).DocumentFromPage(e.page);
      if (document == null)
        return;

      if (document is IWorkbenchDocument)
        e.canClose = (document as IWorkbenchDocument).CanCloseDocument();
      else
        if (document is MySQL.Forms.AppViewDockContent)
        {
          MySQL.Forms.AppViewDockContent content = document as MySQL.Forms.AppViewDockContent;
          e.canClose = content.GetAppView().DocumentClosing();
        }
    }

    private void contentTabControl_TabClosed(object sender, TabClosedEventArgs e)
    {
      ITabDocument document = (sender as FlatTabControl).DocumentFromPage(e.page);
      if (document is IWorkbenchDocument)
        (document as IWorkbenchDocument).CloseDocument();
      else
        if (document is MySQL.Forms.AppViewDockContent)
        {
          // Remove the appview from the page, otherwise it gets disposed with the page,
          // what we don't want.
          e.page.Controls.Clear();
        }
    }

    private void tabsContextMenuItemClick(object sender, EventArgs e)
    {
      TabPage page = contentTabControl.SelectedTab;
      if (page == null)
        return;

      ToolStripMenuItem item = sender as ToolStripMenuItem;
      int clickedTab = (int)item.Owner.Tag;
      switch (item.Tag as string)
      {
        case "0": // Close page.
          contentTabControl.CloseTabPage(page);
          break;
        case "1": // Close all editors but the clicked one.
          TabControl.TabPageCollection pages = contentTabControl.TabPages;
          for (int i = 0; i < pages.Count; i++)
          {
            // Do not close the home screen.
            if (i == 0 || pages[i] == page)
              continue;

            contentTabControl.CloseTabPage(pages[i]);
          }
          break;
        case "2": // Close tabs of the same type.
          ITabDocument document = contentTabControl.DocumentFromIndex(clickedTab);
          Type classToClose = document.GetType();
          for (int i = contentTabControl.TabCount - 1; i >= 0; i--)
          {
            if (contentTabControl.DocumentFromIndex(i).GetType() == classToClose)
              contentTabControl.CloseTabPage(contentTabControl.TabPages[i]);
          }
          break;
      }
    }

    private void contentTabControl_MouseClick(object sender, MouseEventArgs e)
    {
      switch (e.Button)
      {
        case MouseButtons.Right:
          {
            int clickedIndex = contentTabControl.TabIndexFromPosition(e.Location);
            if (clickedIndex < 0)
              return;

            closeTabMenuItem.Enabled = clickedIndex > 0;
            closeOtherTabsMenuItem.Enabled = contentTabControl.TabCount > 1;

            ITabDocument document = contentTabControl.DocumentFromIndex(clickedIndex);
            closeTabsOfSameTypeItem.Enabled = document is SqlIdeForm || document is ModelDiagramForm
              || document is ModelOverviewForm;

            // Keep the found index for later handling in the item click handler.
            tabsContextMenuStrip.Tag = clickedIndex;

            Point p = contentTabControl.PointToScreen(e.Location);
            tabsContextMenuStrip.Show(p);
          }
          break;
      }
    }

    #endregion

    public class MainPageDockDelegate : DockingPointDelegateWrapper
    {
      private MainForm _owner = null;

      public MainPageDockDelegate(MainForm owner, Object representedObject)
        : base(representedObject)
      {
        _owner = owner;
      }

      public override String get_type(Object representedObject)
      {
        return "MainWindow";
      }

      public override void dock_view(Object representedObject, AppViewImpl view, String arg1, int arg2)
      {
        _owner.DockDocument(view.GetHost(), true, true);
      }

      public override void undock_view(Object representedObject, AppViewImpl view)
      {
        _owner.UndockDocument(view.GetHost());
      }

      public override bool select_view(Object representedObject, AppViewImpl view)
      {
        return _owner.select_view(view);
      }

      public override void set_view_title(Object representedObject, AppViewImpl view, String str)
      {
        _owner.set_view_title(view, str);
      }

      public override void get_size(Object representedObject, out int w, out int h)
      {
        _owner.get_size(out w, out h);
      }
    };
  }
}
