﻿/*************************************************************************************

   Extended WPF Toolkit

   Copyright (C) 2007-2013 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 

   For more features, controls, and fast professional support,
   pick up the Plus Edition at http://xceed.com/wpf_toolkit

   Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids

  ***********************************************************************************/

using System;
using System.Linq;
using System.Windows;
using Xceed.Wpf.AvalonDock.Layout;
using System.Windows.Input;
using Xceed.Wpf.AvalonDock.Commands;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Controls;

namespace Xceed.Wpf.AvalonDock.Controls
{
  public abstract class LayoutItem : FrameworkElement
  {
    #region Members

    private ICommand _defaultCloseCommand;
    private ICommand _defaultFloatCommand;
    private ICommand _defaultDockAsDocumentCommand;
    private ICommand _defaultCloseAllButThisCommand;
    private ICommand _defaultCloseAllCommand;
    private ICommand _defaultActivateCommand;
    private ICommand _defaultNewVerticalTabGroupCommand;
    private ICommand _defaultNewHorizontalTabGroupCommand;
    private ICommand _defaultMoveToNextTabGroupCommand;
    private ICommand _defaultMoveToPreviousTabGroupCommand;
    private ContentPresenter _view = null;
    private ReentrantFlag _isSelectedReentrantFlag = new ReentrantFlag();
    private ReentrantFlag _isActiveReentrantFlag = new ReentrantFlag();

    #endregion

    #region Constructors

    static LayoutItem()
    {
      ToolTipProperty.OverrideMetadata( typeof( LayoutItem ), new FrameworkPropertyMetadata( null, ( s, e ) => OnToolTipChanged( s, e ) ) );
      VisibilityProperty.OverrideMetadata( typeof( LayoutItem ), new FrameworkPropertyMetadata( Visibility.Visible, ( s, e ) => OnVisibilityChanged( s, e ) ) );
    }


    internal LayoutItem()
    {
    }

    #endregion

    #region Properties

    #region LayoutElement

    public LayoutContent LayoutElement
    {
      get;
      private set;
    }

    #endregion

    #region Model

    public object Model
    {
      get;
      private set;
    }

    #endregion

    #region View

    public ContentPresenter View
    {
      get
      {
        if( _view == null )
        {
          _view = new ContentPresenter();

          _view.SetBinding( ContentPresenter.ContentProperty, new Binding( "Content" ) { Source = LayoutElement } );
          _view.SetBinding( ContentPresenter.ContentTemplateProperty, new Binding( "LayoutItemTemplate" ) { Source = LayoutElement.Root.Manager } );
          _view.SetBinding( ContentPresenter.ContentTemplateSelectorProperty, new Binding( "LayoutItemTemplateSelector" ) { Source = LayoutElement.Root.Manager } );
          LayoutElement.Root.Manager.InternalAddLogicalChild( _view );
        }

        return _view;
      }
    }

    #endregion

    #region Title

    /// <summary>
    /// Title Dependency Property
    /// </summary>
    public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof( string ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( string )null, new PropertyChangedCallback( OnTitleChanged ) ) );

    /// <summary>
    /// Gets or sets the Title property.  This dependency property 
    /// indicates the title of the element.
    /// </summary>
    public string Title
    {
      get
      {
        return ( string )GetValue( TitleProperty );
      }
      set
      {
        SetValue( TitleProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the Title property.
    /// </summary>
    private static void OnTitleChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnTitleChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the Title property.
    /// </summary>
    protected virtual void OnTitleChanged( DependencyPropertyChangedEventArgs e )
    {
      if( LayoutElement != null )
        LayoutElement.Title = ( string )e.NewValue;
    }

    #endregion

    #region IconSource

    /// <summary>
    /// IconSource Dependency Property
    /// </summary>
    public static readonly DependencyProperty IconSourceProperty = DependencyProperty.Register( "IconSource", typeof( ImageSource ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( ImageSource )null, new PropertyChangedCallback( OnIconSourceChanged ) ) );

    /// <summary>
    /// Gets or sets the IconSource property.  This dependency property 
    /// indicates icon associated with the item.
    /// </summary>
    public ImageSource IconSource
    {
      get
      {
        return ( ImageSource )GetValue( IconSourceProperty );
      }
      set
      {
        SetValue( IconSourceProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the IconSource property.
    /// </summary>
    private static void OnIconSourceChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnIconSourceChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the IconSource property.
    /// </summary>
    protected virtual void OnIconSourceChanged( DependencyPropertyChangedEventArgs e )
    {
      if( LayoutElement != null )
        LayoutElement.IconSource = IconSource;
    }

    #endregion

    #region ContentId

    /// <summary>
    /// ContentId Dependency Property
    /// </summary>
    public static readonly DependencyProperty ContentIdProperty = DependencyProperty.Register( "ContentId", typeof( string ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( string )null, new PropertyChangedCallback( OnContentIdChanged ) ) );

    /// <summary>
    /// Gets or sets the ContentId property.  This dependency property 
    /// indicates the content id used to retrive content when deserializing layouts.
    /// </summary>
    public string ContentId
    {
      get
      {
        return ( string )GetValue( ContentIdProperty );
      }
      set
      {
        SetValue( ContentIdProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the ContentId property.
    /// </summary>
    private static void OnContentIdChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnContentIdChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the ContentId property.
    /// </summary>
    protected virtual void OnContentIdChanged( DependencyPropertyChangedEventArgs e )
    {
      if( LayoutElement != null )
        LayoutElement.ContentId = ( string )e.NewValue;
    }

    #endregion

    #region IsSelected

    /// <summary>
    /// IsSelected Dependency Property
    /// </summary>
    public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register( "IsSelected", typeof( bool ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( bool )false, new PropertyChangedCallback( OnIsSelectedChanged ) ) );

    /// <summary>
    /// Gets or sets the IsSelected property.  This dependency property 
    /// indicates if the item is selected inside its container.
    /// </summary>
    public bool IsSelected
    {
      get
      {
        return ( bool )GetValue( IsSelectedProperty );
      }
      set
      {
        SetValue( IsSelectedProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the IsSelected property.
    /// </summary>
    private static void OnIsSelectedChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnIsSelectedChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the IsSelected property.
    /// </summary>
    protected virtual void OnIsSelectedChanged( DependencyPropertyChangedEventArgs e )
    {
      if( _isSelectedReentrantFlag.CanEnter )
      {
        using( _isSelectedReentrantFlag.Enter() )
        {
          if( LayoutElement != null )
            LayoutElement.IsSelected = ( bool )e.NewValue;
        }
      }
    }

    #endregion

    #region IsActive  

    /// <summary>
    /// IsActive Dependency Property
    /// </summary>
    public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register( "IsActive", typeof( bool ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( bool )false, new PropertyChangedCallback( OnIsActiveChanged ) ) );

    /// <summary>
    /// Gets or sets the IsActive property.  This dependency property 
    /// indicates if the item is active in the UI.
    /// </summary>
    public bool IsActive
    {
      get
      {
        return ( bool )GetValue( IsActiveProperty );
      }
      set
      {
        SetValue( IsActiveProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the IsActive property.
    /// </summary>
    private static void OnIsActiveChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnIsActiveChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the IsActive property.
    /// </summary>
    protected virtual void OnIsActiveChanged( DependencyPropertyChangedEventArgs e )
    {
      if( _isActiveReentrantFlag.CanEnter )
      {
        using( _isActiveReentrantFlag.Enter() )
        {
          if( LayoutElement != null )
            LayoutElement.IsActive = ( bool )e.NewValue;
        }
      }
    }

    #endregion

    #region CanClose

    /// <summary>
    /// CanClose Dependency Property
    /// </summary>
    public static readonly DependencyProperty CanCloseProperty = DependencyProperty.Register( "CanClose", typeof( bool ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( bool )true, new PropertyChangedCallback( OnCanCloseChanged ) ) );

    /// <summary>
    /// Gets or sets the CanClose property.  This dependency property 
    /// indicates if the item can be closed.
    /// </summary>
    public bool CanClose
    {
      get
      {
        return ( bool )GetValue( CanCloseProperty );
      }
      set
      {
        SetValue( CanCloseProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the CanClose property.
    /// </summary>
    private static void OnCanCloseChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnCanCloseChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the CanClose property.
    /// </summary>
    protected virtual void OnCanCloseChanged( DependencyPropertyChangedEventArgs e )
    {
      if( LayoutElement != null )
        LayoutElement.CanClose = ( bool )e.NewValue;
    }

    #endregion

    #region CanFloat

    /// <summary>
    /// CanFloat Dependency Property
    /// </summary>
    public static readonly DependencyProperty CanFloatProperty = DependencyProperty.Register( "CanFloat", typeof( bool ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( bool )true, new PropertyChangedCallback( OnCanFloatChanged ) ) );

    /// <summary>
    /// Gets or sets the CanFloat property.  This dependency property 
    /// indicates if user can move the layout element dragging it to another position.
    /// </summary>
    public bool CanFloat
    {
      get
      {
        return ( bool )GetValue( CanFloatProperty );
      }
      set
      {
        SetValue( CanFloatProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the CanFloat property.
    /// </summary>
    private static void OnCanFloatChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnCanFloatChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the CanFloat property.
    /// </summary>
    protected virtual void OnCanFloatChanged( DependencyPropertyChangedEventArgs e )
    {
      if( LayoutElement != null )
        LayoutElement.CanFloat = ( bool )e.NewValue;
    }

    #endregion

    #region CloseCommand

    /// <summary>
    /// CloseCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register( "CloseCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( null, new PropertyChangedCallback( OnCloseCommandChanged ), new CoerceValueCallback( CoerceCloseCommandValue ) ) );

    /// <summary>
    /// Gets or sets the CloseCommand property.  This dependency property 
    /// indicates the command to execute when user click the document close button.
    /// </summary>
    public ICommand CloseCommand
    {
      get
      {
        return ( ICommand )GetValue( CloseCommandProperty );
      }
      set
      {
        SetValue( CloseCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the CloseCommand property.
    /// </summary>
    private static void OnCloseCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnCloseCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the CloseCommand property.
    /// </summary>
    protected virtual void OnCloseCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    /// <summary>
    /// Coerces the CloseCommand value.
    /// </summary>
    private static object CoerceCloseCommandValue( DependencyObject d, object value )
    {
      return value;
    }

    private bool CanExecuteCloseCommand( object parameter )
    {
      return LayoutElement != null && LayoutElement.CanClose;
    }

    private void ExecuteCloseCommand( object parameter )
    {
      Close();
    }

    protected abstract void Close();


    #endregion

    #region FloatCommand
    /// <summary>
    /// FloatCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty FloatCommandProperty = DependencyProperty.Register( "FloatCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( null, new PropertyChangedCallback( OnFloatCommandChanged ), new CoerceValueCallback( CoerceFloatCommandValue ) ) );

    /// <summary>
    /// Gets or sets the FloatCommand property.  This dependency property 
    /// indicates the command to execute when user click the float button.
    /// </summary>
    /// <remarks>By default this command move the anchorable inside new floating window.</remarks>
    public ICommand FloatCommand
    {
      get
      {
        return ( ICommand )GetValue( FloatCommandProperty );
      }
      set
      {
        SetValue( FloatCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the FloatCommand property.
    /// </summary>
    private static void OnFloatCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnFloatCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the FloatCommand property.
    /// </summary>
    protected virtual void OnFloatCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    /// <summary>
    /// Coerces the FloatCommand value.
    /// </summary>
    private static object CoerceFloatCommandValue( DependencyObject d, object value )
    {
      return value;
    }

    private bool CanExecuteFloatCommand( object anchorable )
    {
      return LayoutElement != null && LayoutElement.CanFloat && LayoutElement.FindParent<LayoutFloatingWindow>() == null;
    }

    private void ExecuteFloatCommand( object parameter )
    {
      LayoutElement.Root.Manager._ExecuteFloatCommand( LayoutElement );
    }

    protected virtual void Float()
    {

    }

    #endregion








    #region DockAsDocumentCommand

    /// <summary>
    /// DockAsDocumentCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty DockAsDocumentCommandProperty = DependencyProperty.Register( "DockAsDocumentCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( null, new PropertyChangedCallback( OnDockAsDocumentCommandChanged ), new CoerceValueCallback( CoerceDockAsDocumentCommandValue ) ) );

    /// <summary>
    /// Gets or sets the DockAsDocumentCommand property.  This dependency property 
    /// indicates the command to execute when user click the DockAsDocument button.
    /// </summary>
    /// <remarks>By default this command move the anchorable inside the last focused document pane.</remarks>
    public ICommand DockAsDocumentCommand
    {
      get
      {
        return ( ICommand )GetValue( DockAsDocumentCommandProperty );
      }
      set
      {
        SetValue( DockAsDocumentCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the DockAsDocumentCommand property.
    /// </summary>
    private static void OnDockAsDocumentCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnDockAsDocumentCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the DockAsDocumentCommand property.
    /// </summary>
    protected virtual void OnDockAsDocumentCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    /// <summary>
    /// Coerces the DockAsDocumentCommand value.
    /// </summary>
    private static object CoerceDockAsDocumentCommandValue( DependencyObject d, object value )
    {
      return value;
    }

    protected virtual bool CanExecuteDockAsDocumentCommand()
    {
      return ( LayoutElement != null && LayoutElement.FindParent<LayoutDocumentPane>() == null );
    }

    private bool CanExecuteDockAsDocumentCommand( object parameter )
    {
      return this.CanExecuteDockAsDocumentCommand();
    }

    private void ExecuteDockAsDocumentCommand( object parameter )
    {
      LayoutElement.Root.Manager._ExecuteDockAsDocumentCommand( LayoutElement );
    }

    #endregion

    #region CloseAllButThisCommand

    /// <summary>
    /// CloseAllButThisCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty CloseAllButThisCommandProperty = DependencyProperty.Register( "CloseAllButThisCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( null, new PropertyChangedCallback( OnCloseAllButThisCommandChanged ), new CoerceValueCallback( CoerceCloseAllButThisCommandValue ) ) );

    /// <summary>
    /// Gets or sets the CloseAllButThisCommand property.  This dependency property 
    /// indicates the 'Close All But This' command.
    /// </summary>
    public ICommand CloseAllButThisCommand
    {
      get
      {
        return ( ICommand )GetValue( CloseAllButThisCommandProperty );
      }
      set
      {
        SetValue( CloseAllButThisCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the CloseAllButThisCommand property.
    /// </summary>
    private static void OnCloseAllButThisCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnCloseAllButThisCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the CloseAllButThisCommand property.
    /// </summary>
    protected virtual void OnCloseAllButThisCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    /// <summary>
    /// Coerces the CloseAllButThisCommand value.
    /// </summary>
    private static object CoerceCloseAllButThisCommandValue( DependencyObject d, object value )
    {
      return value;
    }

    private bool CanExecuteCloseAllButThisCommand( object parameter )
    {
      if( LayoutElement == null )
        return false;
      var root = LayoutElement.Root;
      if( root == null )
        return false;

      return LayoutElement.Root.Manager.Layout.
          Descendents().OfType<LayoutContent>().Where( d => d != LayoutElement && ( d.Parent is LayoutDocumentPane || d.Parent is LayoutDocumentFloatingWindow ) ).Any();
    }

    private void ExecuteCloseAllButThisCommand( object parameter )
    {
      LayoutElement.Root.Manager._ExecuteCloseAllButThisCommand( LayoutElement );
    }

    #endregion

    #region CloseAllCommand

    /// <summary>
    /// CloseAllCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty CloseAllCommandProperty = DependencyProperty.Register( "CloseAllCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( null, new PropertyChangedCallback( OnCloseAllCommandChanged ), new CoerceValueCallback( CoerceCloseAllCommandValue ) ) );

    /// <summary>
    /// Gets or sets the CloseAllCommand property.  This dependency property 
    /// indicates the 'Close All' command.
    /// </summary>
    public ICommand CloseAllCommand
    {
      get
      {
        return ( ICommand )GetValue( CloseAllCommandProperty );
      }
      set
      {
        SetValue( CloseAllCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the CloseAllCommand property.
    /// </summary>
    private static void OnCloseAllCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnCloseAllCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the CloseAllCommand property.
    /// </summary>
    protected virtual void OnCloseAllCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    /// <summary>
    /// Coerces the CloseAllCommand value.
    /// </summary>
    private static object CoerceCloseAllCommandValue( DependencyObject d, object value )
    {
      return value;
    }

    private bool CanExecuteCloseAllCommand( object parameter )
    {
      if( LayoutElement == null )
        return false;
      var root = LayoutElement.Root;
      if( root == null )
        return false;

      return LayoutElement.Root.Manager.Layout.
          Descendents().OfType<LayoutContent>().Where( d => ( d.Parent is LayoutDocumentPane || d.Parent is LayoutDocumentFloatingWindow ) ).Any();
    }

    private void ExecuteCloseAllCommand( object parameter )
    {
      LayoutElement.Root.Manager._ExecuteCloseAllCommand( LayoutElement );
    }

    #endregion

    #region ActivateCommand

    /// <summary>
    /// ActivateCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty ActivateCommandProperty = DependencyProperty.Register( "ActivateCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( null, new PropertyChangedCallback( OnActivateCommandChanged ), new CoerceValueCallback( CoerceActivateCommandValue ) ) );

    /// <summary>
    /// Gets or sets the ActivateCommand property.  This dependency property 
    /// indicates the command to execute when user wants to activate a content (either a Document or an Anchorable).
    /// </summary>
    public ICommand ActivateCommand
    {
      get
      {
        return ( ICommand )GetValue( ActivateCommandProperty );
      }
      set
      {
        SetValue( ActivateCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the ActivateCommand property.
    /// </summary>
    private static void OnActivateCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnActivateCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the ActivateCommand property.
    /// </summary>
    protected virtual void OnActivateCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    /// <summary>
    /// Coerces the ActivateCommand value.
    /// </summary>
    private static object CoerceActivateCommandValue( DependencyObject d, object value )
    {
      return value;
    }

    private bool CanExecuteActivateCommand( object parameter )
    {
      return LayoutElement != null;
    }

    private void ExecuteActivateCommand( object parameter )
    {
      LayoutElement.Root.Manager._ExecuteContentActivateCommand( LayoutElement );
    }

    #endregion

    #region NewVerticalTabGroupCommand

    /// <summary>
    /// NewVerticalTabGroupCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty NewVerticalTabGroupCommandProperty = DependencyProperty.Register( "NewVerticalTabGroupCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( ICommand )null, new PropertyChangedCallback( OnNewVerticalTabGroupCommandChanged ) ) );

    /// <summary>
    /// Gets or sets the NewVerticalTabGroupCommand property.  This dependency property 
    /// indicates the new vertical tab group command.
    /// </summary>
    public ICommand NewVerticalTabGroupCommand
    {
      get
      {
        return ( ICommand )GetValue( NewVerticalTabGroupCommandProperty );
      }
      set
      {
        SetValue( NewVerticalTabGroupCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the NewVerticalTabGroupCommand property.
    /// </summary>
    private static void OnNewVerticalTabGroupCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnNewVerticalTabGroupCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the NewVerticalTabGroupCommand property.
    /// </summary>
    protected virtual void OnNewVerticalTabGroupCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    private bool CanExecuteNewVerticalTabGroupCommand( object parameter )
    {
      if( LayoutElement == null )
        return false;
      var layoutDocument = LayoutElement as LayoutDocument;
      if( ( layoutDocument != null ) && !layoutDocument.CanMove )
        return false;
      var parentDocumentGroup = LayoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = LayoutElement.Parent as LayoutDocumentPane;
      return ( ( parentDocumentGroup == null ||
          parentDocumentGroup.ChildrenCount == 1 ||
          parentDocumentGroup.Root.Manager.AllowMixedOrientation ||
          parentDocumentGroup.Orientation == System.Windows.Controls.Orientation.Horizontal ) &&
          parentDocumentPane != null &&
          parentDocumentPane.ChildrenCount > 1 );
    }

    private void ExecuteNewVerticalTabGroupCommand( object parameter )
    {
      var layoutElement = LayoutElement;
      var parentDocumentGroup = layoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = layoutElement.Parent as LayoutDocumentPane;

      if( parentDocumentGroup == null )
      {
        var grandParent = parentDocumentPane.Parent as ILayoutContainer;
        parentDocumentGroup = new LayoutDocumentPaneGroup() { Orientation = System.Windows.Controls.Orientation.Horizontal };
        grandParent.ReplaceChild( parentDocumentPane, parentDocumentGroup );
        parentDocumentGroup.Children.Add( parentDocumentPane );
      }
      parentDocumentGroup.Orientation = System.Windows.Controls.Orientation.Horizontal;
      int indexOfParentPane = parentDocumentGroup.IndexOfChild( parentDocumentPane );
      parentDocumentGroup.InsertChildAt( indexOfParentPane + 1, new LayoutDocumentPane( layoutElement ) );
      layoutElement.IsActive = true;
      layoutElement.Root.CollectGarbage();
    }
    #endregion

    #region NewHorizontalTabGroupCommand

    /// <summary>
    /// NewHorizontalTabGroupCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty NewHorizontalTabGroupCommandProperty = DependencyProperty.Register( "NewHorizontalTabGroupCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( ICommand )null, new PropertyChangedCallback( OnNewHorizontalTabGroupCommandChanged ) ) );

    /// <summary>
    /// Gets or sets the NewHorizontalTabGroupCommand property.  This dependency property 
    /// indicates the new horizontal tab group command.
    /// </summary>
    public ICommand NewHorizontalTabGroupCommand
    {
      get
      {
        return ( ICommand )GetValue( NewHorizontalTabGroupCommandProperty );
      }
      set
      {
        SetValue( NewHorizontalTabGroupCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the NewHorizontalTabGroupCommand property.
    /// </summary>
    private static void OnNewHorizontalTabGroupCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnNewHorizontalTabGroupCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the NewHorizontalTabGroupCommand property.
    /// </summary>
    protected virtual void OnNewHorizontalTabGroupCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }


    private bool CanExecuteNewHorizontalTabGroupCommand( object parameter )
    {
      if( LayoutElement == null )
        return false;
      var layoutDocument = LayoutElement as LayoutDocument;
      if( ( layoutDocument != null ) && !layoutDocument.CanMove )
        return false;
      var parentDocumentGroup = LayoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = LayoutElement.Parent as LayoutDocumentPane;
      return ( ( parentDocumentGroup == null ||
          parentDocumentGroup.ChildrenCount == 1 ||
          parentDocumentGroup.Root.Manager.AllowMixedOrientation ||
          parentDocumentGroup.Orientation == System.Windows.Controls.Orientation.Vertical ) &&
          parentDocumentPane != null &&
          parentDocumentPane.ChildrenCount > 1 );
    }

    private void ExecuteNewHorizontalTabGroupCommand( object parameter )
    {
      var layoutElement = LayoutElement;
      var parentDocumentGroup = layoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = layoutElement.Parent as LayoutDocumentPane;

      if( parentDocumentGroup == null )
      {
        var grandParent = parentDocumentPane.Parent as ILayoutContainer;
        parentDocumentGroup = new LayoutDocumentPaneGroup() { Orientation = System.Windows.Controls.Orientation.Vertical };
        grandParent.ReplaceChild( parentDocumentPane, parentDocumentGroup );
        parentDocumentGroup.Children.Add( parentDocumentPane );
      }
      parentDocumentGroup.Orientation = System.Windows.Controls.Orientation.Vertical;
      int indexOfParentPane = parentDocumentGroup.IndexOfChild( parentDocumentPane );
      parentDocumentGroup.InsertChildAt( indexOfParentPane + 1, new LayoutDocumentPane( layoutElement ) );
      layoutElement.IsActive = true;
      layoutElement.Root.CollectGarbage();
    }
    #endregion

    #region MoveToNextTabGroupCommand

    /// <summary>
    /// MoveToNextTabGroupCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty MoveToNextTabGroupCommandProperty = DependencyProperty.Register( "MoveToNextTabGroupCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( ICommand )null, new PropertyChangedCallback( OnMoveToNextTabGroupCommandChanged ) ) );

    /// <summary>
    /// Gets or sets the MoveToNextTabGroupCommand property.  This dependency property 
    /// indicates move to next tab group command.
    /// </summary>
    public ICommand MoveToNextTabGroupCommand
    {
      get
      {
        return ( ICommand )GetValue( MoveToNextTabGroupCommandProperty );
      }
      set
      {
        SetValue( MoveToNextTabGroupCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the MoveToNextTabGroupCommand property.
    /// </summary>
    private static void OnMoveToNextTabGroupCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnMoveToNextTabGroupCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the MoveToNextTabGroupCommand property.
    /// </summary>
    protected virtual void OnMoveToNextTabGroupCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    private bool CanExecuteMoveToNextTabGroupCommand( object parameter )
    {
      if( LayoutElement == null )
        return false;

      var parentDocumentGroup = LayoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = LayoutElement.Parent as LayoutDocumentPane;
      return ( parentDocumentGroup != null &&
          parentDocumentPane != null &&
          parentDocumentGroup.ChildrenCount > 1 &&
          parentDocumentGroup.IndexOfChild( parentDocumentPane ) < parentDocumentGroup.ChildrenCount - 1 &&
          parentDocumentGroup.Children[ parentDocumentGroup.IndexOfChild( parentDocumentPane ) + 1 ] is LayoutDocumentPane );
    }

    private void ExecuteMoveToNextTabGroupCommand( object parameter )
    {
      var layoutElement = LayoutElement;
      var parentDocumentGroup = layoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = layoutElement.Parent as LayoutDocumentPane;
      int indexOfParentPane = parentDocumentGroup.IndexOfChild( parentDocumentPane );
      var nextDocumentPane = parentDocumentGroup.Children[ indexOfParentPane + 1 ] as LayoutDocumentPane;
      nextDocumentPane.InsertChildAt( 0, layoutElement );
      layoutElement.IsActive = true;
      layoutElement.Root.CollectGarbage();
    }

    #endregion

    #region MoveToPreviousTabGroupCommand

    /// <summary>
    /// MoveToPreviousTabGroupCommand Dependency Property
    /// </summary>
    public static readonly DependencyProperty MoveToPreviousTabGroupCommandProperty = DependencyProperty.Register( "MoveToPreviousTabGroupCommand", typeof( ICommand ), typeof( LayoutItem ),
            new FrameworkPropertyMetadata( ( ICommand )null, new PropertyChangedCallback( OnMoveToPreviousTabGroupCommandChanged ) ) );

    /// <summary>
    /// Gets or sets the MoveToPreviousTabGroupCommand property.  This dependency property 
    /// indicates move to rpevious tab group command.
    /// </summary>
    public ICommand MoveToPreviousTabGroupCommand
    {
      get
      {
        return ( ICommand )GetValue( MoveToPreviousTabGroupCommandProperty );
      }
      set
      {
        SetValue( MoveToPreviousTabGroupCommandProperty, value );
      }
    }

    /// <summary>
    /// Handles changes to the MoveToPreviousTabGroupCommand property.
    /// </summary>
    private static void OnMoveToPreviousTabGroupCommandChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )d ).OnMoveToPreviousTabGroupCommandChanged( e );
    }

    /// <summary>
    /// Provides derived classes an opportunity to handle changes to the MoveToPreviousTabGroupCommand property.
    /// </summary>
    protected virtual void OnMoveToPreviousTabGroupCommandChanged( DependencyPropertyChangedEventArgs e )
    {
    }

    private bool CanExecuteMoveToPreviousTabGroupCommand( object parameter )
    {
      if( LayoutElement == null )
        return false;
      var parentDocumentGroup = LayoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = LayoutElement.Parent as LayoutDocumentPane;
      return ( parentDocumentGroup != null &&
          parentDocumentPane != null &&
          parentDocumentGroup.ChildrenCount > 1 &&
          parentDocumentGroup.IndexOfChild( parentDocumentPane ) > 0 &&
          parentDocumentGroup.Children[ parentDocumentGroup.IndexOfChild( parentDocumentPane ) - 1 ] is LayoutDocumentPane );
    }

    private void ExecuteMoveToPreviousTabGroupCommand( object parameter )
    {
      var layoutElement = LayoutElement;
      var parentDocumentGroup = layoutElement.FindParent<LayoutDocumentPaneGroup>();
      var parentDocumentPane = layoutElement.Parent as LayoutDocumentPane;
      int indexOfParentPane = parentDocumentGroup.IndexOfChild( parentDocumentPane );
      var nextDocumentPane = parentDocumentGroup.Children[ indexOfParentPane - 1 ] as LayoutDocumentPane;
      nextDocumentPane.InsertChildAt( 0, layoutElement );
      layoutElement.IsActive = true;
      layoutElement.Root.CollectGarbage();
    }
    #endregion

    #endregion

    #region Internal Methods

    protected virtual void InitDefaultCommands()
    {
      _defaultCloseCommand = new RelayCommand( ( p ) => ExecuteCloseCommand( p ), ( p ) => CanExecuteCloseCommand( p ) );
      _defaultFloatCommand = new RelayCommand( ( p ) => ExecuteFloatCommand( p ), ( p ) => CanExecuteFloatCommand( p ) );
      _defaultDockAsDocumentCommand = new RelayCommand( ( p ) => ExecuteDockAsDocumentCommand( p ), ( p ) => CanExecuteDockAsDocumentCommand( p ) );
      _defaultCloseAllButThisCommand = new RelayCommand( ( p ) => ExecuteCloseAllButThisCommand( p ), ( p ) => CanExecuteCloseAllButThisCommand( p ) );
      _defaultCloseAllCommand = new RelayCommand( ( p ) => ExecuteCloseAllCommand( p ), ( p ) => CanExecuteCloseAllCommand( p ) );
      _defaultActivateCommand = new RelayCommand( ( p ) => ExecuteActivateCommand( p ), ( p ) => CanExecuteActivateCommand( p ) );
      _defaultNewVerticalTabGroupCommand = new RelayCommand( ( p ) => ExecuteNewVerticalTabGroupCommand( p ), ( p ) => CanExecuteNewVerticalTabGroupCommand( p ) );
      _defaultNewHorizontalTabGroupCommand = new RelayCommand( ( p ) => ExecuteNewHorizontalTabGroupCommand( p ), ( p ) => CanExecuteNewHorizontalTabGroupCommand( p ) );
      _defaultMoveToNextTabGroupCommand = new RelayCommand( ( p ) => ExecuteMoveToNextTabGroupCommand( p ), ( p ) => CanExecuteMoveToNextTabGroupCommand( p ) );
      _defaultMoveToPreviousTabGroupCommand = new RelayCommand( ( p ) => ExecuteMoveToPreviousTabGroupCommand( p ), ( p ) => CanExecuteMoveToPreviousTabGroupCommand( p ) );
    }

    protected virtual void ClearDefaultBindings()
    {
      if( CloseCommand == _defaultCloseCommand )
        BindingOperations.ClearBinding( this, CloseCommandProperty );
      if( FloatCommand == _defaultFloatCommand )
        BindingOperations.ClearBinding( this, FloatCommandProperty );
      if( DockAsDocumentCommand == _defaultDockAsDocumentCommand )
        BindingOperations.ClearBinding( this, DockAsDocumentCommandProperty );
      if( CloseAllButThisCommand == _defaultCloseAllButThisCommand )
        BindingOperations.ClearBinding( this, CloseAllButThisCommandProperty );
      if( CloseAllCommand == _defaultCloseAllCommand )
        BindingOperations.ClearBinding( this, CloseAllCommandProperty );
      if( ActivateCommand == _defaultActivateCommand )
        BindingOperations.ClearBinding( this, ActivateCommandProperty );
      if( NewVerticalTabGroupCommand == _defaultNewVerticalTabGroupCommand )
        BindingOperations.ClearBinding( this, NewVerticalTabGroupCommandProperty );
      if( NewHorizontalTabGroupCommand == _defaultNewHorizontalTabGroupCommand )
        BindingOperations.ClearBinding( this, NewHorizontalTabGroupCommandProperty );
      if( MoveToNextTabGroupCommand == _defaultMoveToNextTabGroupCommand )
        BindingOperations.ClearBinding( this, MoveToNextTabGroupCommandProperty );
      if( MoveToPreviousTabGroupCommand == _defaultMoveToPreviousTabGroupCommand )
        BindingOperations.ClearBinding( this, MoveToPreviousTabGroupCommandProperty );
    }

    protected virtual void SetDefaultBindings()
    {
      if( CloseCommand == null )
        CloseCommand = _defaultCloseCommand;
      if( FloatCommand == null )
        FloatCommand = _defaultFloatCommand;
      if( DockAsDocumentCommand == null )
        DockAsDocumentCommand = _defaultDockAsDocumentCommand;
      if( CloseAllButThisCommand == null )
        CloseAllButThisCommand = _defaultCloseAllButThisCommand;
      if( CloseAllCommand == null )
        CloseAllCommand = _defaultCloseAllCommand;
      if( ActivateCommand == null )
        ActivateCommand = _defaultActivateCommand;
      if( NewVerticalTabGroupCommand == null )
        NewVerticalTabGroupCommand = _defaultNewVerticalTabGroupCommand;
      if( NewHorizontalTabGroupCommand == null )
        NewHorizontalTabGroupCommand = _defaultNewHorizontalTabGroupCommand;
      if( MoveToNextTabGroupCommand == null )
        MoveToNextTabGroupCommand = _defaultMoveToNextTabGroupCommand;
      if( MoveToPreviousTabGroupCommand == null )
        MoveToPreviousTabGroupCommand = _defaultMoveToPreviousTabGroupCommand;


      IsSelected = LayoutElement.IsSelected;
      IsActive = LayoutElement.IsActive;
      CanClose = LayoutElement.CanClose;
    }

    protected virtual void OnVisibilityChanged()
    {
      if( LayoutElement != null &&
          Visibility == System.Windows.Visibility.Collapsed )
        LayoutElement.Close();
    }

    internal virtual void Attach( LayoutContent model )
    {
      LayoutElement = model;
      Model = model.Content;

      InitDefaultCommands();

      LayoutElement.IsSelectedChanged += new EventHandler( LayoutElement_IsSelectedChanged );
      LayoutElement.IsActiveChanged += new EventHandler( LayoutElement_IsActiveChanged );

      DataContext = this;
    }

    internal virtual void Detach()
    {
      LayoutElement.IsSelectedChanged -= new EventHandler( LayoutElement_IsSelectedChanged );
      LayoutElement.IsActiveChanged -= new EventHandler( LayoutElement_IsActiveChanged );
      LayoutElement = null;
      Model = null;
    }

    internal void _ClearDefaultBindings()
    {
      ClearDefaultBindings();
    }

    internal void _SetDefaultBindings()
    {
      SetDefaultBindings();
    }

    internal bool IsViewExists()
    {
      return ( _view != null );
    }

    #endregion

    #region Private Methods

    private void LayoutElement_IsActiveChanged( object sender, EventArgs e )
    {
      if( _isActiveReentrantFlag.CanEnter )
      {
        using( _isActiveReentrantFlag.Enter() )
        {
          var bnd = BindingOperations.GetBinding( this, IsActiveProperty );
          IsActive = LayoutElement.IsActive;
          var bnd2 = BindingOperations.GetBinding( this, IsActiveProperty );
        }
      }
    }

    private void LayoutElement_IsSelectedChanged( object sender, EventArgs e )
    {
      if( _isSelectedReentrantFlag.CanEnter )
      {
        using( _isSelectedReentrantFlag.Enter() )
        {
          IsSelected = LayoutElement.IsSelected;
        }
      }
    }

    private static void OnToolTipChanged( DependencyObject s, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )s ).OnToolTipChanged();
    }

    private void OnToolTipChanged()
    {
      if( LayoutElement != null )
        LayoutElement.ToolTip = ToolTip;
    }

    private static void OnVisibilityChanged( DependencyObject s, DependencyPropertyChangedEventArgs e )
    {
      ( ( LayoutItem )s ).OnVisibilityChanged();
    }

    #endregion
  }
}
