///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using JetBrains.DataStructures;
using JetBrains.Omea.GUIControls;
using JetBrains.Omea.OpenAPI;
namespace JetBrains.Omea
{
public enum SidebarSide { Left, Right };
///
/// A pane with a row of vertical buttons and a stack of panes connected to those buttons.
///
internal class VerticalSidebar : UserControl, ISidebar, IContextProvider
{
private const int _minPaneHeight = 120;
private Panel _contentPane;
private SidebarButtons _paneButtons;
private PaneData _lastActivePaneData;
private ColorScheme _colorScheme;
private SidebarSide _side = SidebarSide.Left;
private int _expandedWidth;
private int _updateCount;
private bool _expanded;
private readonly HashSet _populatedPanes = new HashSet();
///
/// Required designer variable.
///
private Container components;
internal class PaneData
{
public string PaneId;
public int PaneHeight;
public SidebarPaneBackground PaneBackground;
public AbstractViewPane Pane;
public PaneCaption PaneCaption;
public Splitter PaneSplitter;
public PaneData( string paneID, SidebarPaneBackground paneBackground, AbstractViewPane pane,
PaneCaption paneCaption, Splitter paneSplitter, int paneHeight )
{
PaneId = paneID;
PaneBackground = paneBackground;
Pane = pane;
PaneCaption = paneCaption;
PaneSplitter = paneSplitter;
PaneHeight = paneHeight;
}
}
public VerticalSidebar()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
SetStyle( ControlStyles.Selectable, false );
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this._paneButtons = new SidebarButtons();
this._contentPane = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// _paneButtons
//
this._paneButtons.Dock = System.Windows.Forms.DockStyle.Bottom;
this._paneButtons.Name = "_paneButtons";
this._paneButtons.Size = new System.Drawing.Size(150, 30);
this._paneButtons.TabIndex = 0;
//
// _contentPane
//
this._contentPane.Dock = System.Windows.Forms.DockStyle.Fill;
this._contentPane.Location = new System.Drawing.Point(30, 0);
this._contentPane.Name = "_contentPane";
this._contentPane.Size = new System.Drawing.Size(120, 150);
this._contentPane.TabIndex = 1;
//
// VerticalSidebar
//
this.Controls.Add(this._contentPane);
this.Controls.Add(this._paneButtons);
this.Name = "VerticalSidebar";
this.ResumeLayout(false);
}
#endregion
public event EventHandler PaneAdded;
public event EventHandler ExpandedChanged;
[DefaultValue(SidebarSide.Left)]
public SidebarSide Side
{
get { return _side; }
set { _side = value; }
}
public int ExpandedWidth
{
get
{
return Expanded ? Width : _expandedWidth;
}
set
{
_expandedWidth = value;
if ( Expanded )
{
Width = value;
}
}
}
public bool Expanded
{
// get { return _expanded; }
get { return true; }
}
private void SetExpanded( bool value )
{
if ( _expanded != value )
{
_expanded = value;
if ( ExpandedChanged != null )
{
ExpandedChanged( this, EventArgs.Empty );
}
}
}
public int PanesCount
{
get { return _paneButtons.Items.Count; }
}
public ColorScheme ColorScheme
{
get { return _colorScheme; }
set
{
_colorScheme = value;
foreach( ToolStripButton btn in _paneButtons.Items )
{
PaneData paneData = (PaneData) btn.Tag;
paneData.PaneCaption.ColorScheme = _colorScheme;
paneData.PaneBackground.ColorScheme = _colorScheme;
IColorSchemeable schemable = paneData.Pane as IColorSchemeable;
if ( schemable != null )
{
schemable.ColorScheme = _colorScheme;
}
}
}
}
public void RegisterPane( AbstractViewPane pane, string id, string caption, Image icon )
{
PaneCaption paneCaption = new PaneCaption();
paneCaption.Text = caption;
paneCaption.Dock = DockStyle.Top;
paneCaption.CaptionButtons = PaneCaptionButtons.Minimize;
paneCaption.Click += OnPaneCaptionClick;
paneCaption.MinimizeClick += OnPaneMinimize;
paneCaption.ColorScheme = _colorScheme;
_contentPane.Controls.Add( paneCaption );
_contentPane.Controls.SetChildIndex( paneCaption, 0 );
int paneHeight = pane.Height;
SidebarPaneBackground background = new SidebarPaneBackground();
background.SetContents( pane );
background.ColorScheme = _colorScheme;
background.Dock = DockStyle.Top;
background.Visible = false;
background.Height = 0;
_contentPane.Controls.Add( background );
_contentPane.Controls.SetChildIndex( background, 0 );
pane.ShowSelection = false;
pane.Enter += OnPaneEnter;
pane.Leave += OnPaneLeave;
Splitter paneSplitter = new Splitter();
paneSplitter.Dock = DockStyle.Top;
paneSplitter.Height = 3;
_contentPane.Controls.Add( paneSplitter );
_contentPane.Controls.SetChildIndex( paneSplitter, 0 );
PaneData paneData = new PaneData( id, background, pane, paneCaption, paneSplitter, paneHeight );
paneCaption.Tag = paneData;
IColorSchemeable schemeable = pane as IColorSchemeable;
if ( schemeable != null )
{
schemeable.ColorScheme = _colorScheme;
}
ToolStripButton button = _paneButtons.AddButton( paneData, icon, caption );
button.CheckedChanged += OnToolbarButtonClicked;
AdjustDockAndSplitters( null, false );
if ( PaneAdded != null )
{
PaneAdded( this, EventArgs.Empty );
}
}
public bool ContainsPane( string paneId )
{
AbstractViewPane pane = GetPane( paneId );
return pane != null;
}
public AbstractViewPane GetPane( string paneId )
{
ToolStripButton btn = _paneButtons.GetPaneButton( paneId );
return ((PaneData) btn.Tag).Pane;
}
public bool IsPaneExpanded( string paneId )
{
ToolStripButton btn = _paneButtons.GetPaneButton( paneId );
return btn.Checked;
}
public void SetPaneExpanded( string paneId, bool expanded )
{
ToolStripButton btn = _paneButtons.GetPaneButton( paneId );
btn.Checked = expanded;
}
public void SetPaneCaption( string paneId, string caption )
{
ToolStripButton btn = _paneButtons.GetPaneButton( paneId );
((PaneData)btn.Tag).PaneCaption.Text = btn.ToolTipText = caption;
}
private int GetPressedCount()
{
return _paneButtons.CheckedButtonsCount;
}
public void ActivatePane( string paneId )
{
AbstractViewPane pane = GetPane( paneId );
pane.Focus();
// hack (see #5371, #5746)
IResource selResource = pane.SelectedResource;
if ( selResource != null )
{
pane.SelectResource( selResource, false );
}
_paneButtons.SetButtonPressed( paneId, true );
}
public void PopulateViewPanes()
{
foreach( ToolStripButton btn in _paneButtons.Items )
{
if ( btn.Checked )
{
PaneData paneData = (PaneData) btn.Tag;
if ( !_populatedPanes.Contains( paneData.PaneId ) )
{
_populatedPanes.Add( paneData.PaneId );
paneData.Pane.Populate();
}
}
}
_paneButtons.CheckButtonsState();
}
public void UpdateActiveWorkspace()
{
IResource workspace = Core.WorkspaceManager.ActiveWorkspace;
foreach( ToolStripButton btn in _paneButtons.Items )
{
PaneData paneData = (PaneData) btn.Tag;
if ( _populatedPanes.Contains( paneData.PaneId ) )
{
paneData.Pane.SetActiveWorkspace( workspace );
}
}
}
public void BeginUpdate()
{
SuspendLayout();
_updateCount++;
}
public void EndUpdate()
{
_updateCount--;
if ( _updateCount == 0 )
{
AdjustDockAndSplitters( null, false );
}
ResumeLayout();
}
///
/// Tries to select the resource in any of the sidebar panes.
///
///
public void ForceSelectResource( IResource res )
{
foreach( ToolStripButton btn in _paneButtons.Items )
{
PaneData paneData = (PaneData) btn.Tag;
CheckPopulatePane( paneData );
if ( paneData.Pane.SelectResource( res, false ) )
{
ActivatePane( paneData.PaneId );
break;
}
}
}
internal SidebarState CurrentState
{
get
{
int[] paneHeights = new int [ PanesCount ];
int activePaneIndex = -1;
IResource selectedResource = null;
for( int i = 0; i < _paneButtons.Items.Count; i++ )
{
ToolStripButton btn = (ToolStripButton)_paneButtons.Items[ i ];
PaneData paneData = (PaneData) btn.Tag;
if ( !btn.Checked )
{
paneHeights[ i ] = -1;
}
else
{
paneHeights[ i ] = paneData.PaneBackground.Height;
if ( paneData == _lastActivePaneData )
{
activePaneIndex = i;
selectedResource = paneData.Pane.SelectedResource;
}
}
}
return new SidebarState( paneHeights, activePaneIndex, selectedResource );
}
set
{
if ( value == null )
return;
int[] paneHeights = value.PaneHeights;
bool foundSelectedNode = false;
BeginUpdate();
for( int i = 0; i < _paneButtons.Items.Count; i++ )
{
if ( i >= paneHeights.Length )
break;
ToolStripButton btn = (ToolStripButton)_paneButtons.Items[ i ];
PaneData paneData = (PaneData) btn.Tag;
if ( paneHeights [i] < 0 )
{
_paneButtons.SetButtonPressed( paneData.PaneId, false );
}
else
{
_paneButtons.SetButtonPressed( paneData.PaneId, true );
if ( paneHeights [i] > 0 )
{
paneData.PaneBackground.Height = paneHeights [i];
}
}
if ( i == value.ActivePaneIndex )
{
_lastActivePaneData = paneData;
if( value.SelectedResource != null )
{
SetActivePane( paneData.Pane );
if ( paneData.Pane.SelectResource( value.SelectedResource, true ) )
{
if ( Core.State == CoreState.StartingPlugins )
{
paneData.Pane.UpdateSelection();
}
else
{
paneData.Pane.AsyncUpdateSelection();
}
foundSelectedNode = true;
}
else
{
value.SelectedResource = null;
}
}
}
}
if ( !foundSelectedNode )
{
Core.ResourceBrowser.DisplayResourceList( null, Core.ResourceStore.EmptyResourceList, "", null );
}
EndUpdate();
UpdatePaneSizes( _minPaneHeight );
}
}
public void FocusActivePane()
{
if ( _lastActivePaneData != null )
{
_lastActivePaneData.Pane.Focus();
_lastActivePaneData.Pane.ShowSelection = true;
}
}
public string ActivePaneId
{
get
{
return ( _lastActivePaneData == null ) ? null : _lastActivePaneData.PaneId;
}
}
private void OnToolbarButtonClicked(object sender, EventArgs e)
{
ToolStripButton btn = (ToolStripButton) sender;
SuspendLayout();
try
{
PaneData paneData = (PaneData) btn.Tag;
if ( _updateCount == 0 )
{
AdjustDockAndSplitters( paneData, btn.Checked );
}
bool focusedPaneHidden = false;
if ( btn.Checked )
{
CheckPopulatePane( paneData );
paneData.PaneBackground.Height = paneData.PaneHeight;
}
else
{
focusedPaneHidden = ( paneData == _lastActivePaneData );
paneData.PaneHeight = paneData.PaneBackground.Height;
paneData.PaneBackground.Height = 0;
}
paneData.PaneBackground.Visible = btn.Checked;
paneData.PaneCaption.Visible = btn.Checked;
if ( focusedPaneHidden )
{
bool foundActive = false;
foreach( ToolStripButton button in _paneButtons.Items )
{
if( button.Checked )
{
PaneData visibleData = (PaneData)button.Tag;
visibleData.Pane.Focus();
visibleData.Pane.UpdateSelection();
foundActive = true;
break;
}
}
if ( !foundActive )
{
Core.ResourceBrowser.FocusResourceList();
}
}
if ( GetPressedCount() == 0 )
{
_expandedWidth = Width;
Width = 0;
}
else
{
Width = _expandedWidth;
}
}
finally
{
ResumeLayout();
}
if ( btn.Checked )
{
UpdatePaneSizes( _minPaneHeight );
}
// SetExpanded( GetPressedCount() != 0 );
_paneButtons.CheckButtonsState();
}
private void CheckPopulatePane( PaneData paneData )
{
if ( !_populatedPanes.Contains( paneData.PaneId ) )
{
_populatedPanes.Add( paneData.PaneId );
paneData.Pane.Populate();
paneData.Pane.SetActiveWorkspace( Core.WorkspaceManager.ActiveWorkspace );
}
}
///
/// Sets DockStyle.Fill for the last visible pane and DockStyle.Top
/// for the remaining ones. Also updates splitter visibility.
///
private void AdjustDockAndSplitters( PaneData toggledPane, bool toggledPaneState )
{
bool lastPane = true;
int visiblePanes = 0;
for( int i = PanesCount - 1; i >= 0; i-- )
{
ToolStripButton btn = (ToolStripButton)_paneButtons.Items[ i ];
PaneData paneData = (PaneData)btn.Tag;
// don't check Visible because it will return false if the sidebar
// as a whole is hidden
bool visible = btn.Checked;
if ( paneData == toggledPane )
visible = toggledPaneState;
if ( visible )
{
visiblePanes++;
paneData.PaneBackground.Dock = lastPane ? DockStyle.Fill : DockStyle.Top;
paneData.PaneSplitter.Visible = !lastPane;
lastPane = false;
}
else
{
paneData.PaneSplitter.Visible = false;
}
int paneIndex = PanesCount - 1 - i;
_contentPane.Controls.SetChildIndex( paneData.PaneSplitter, paneIndex * 3 );
_contentPane.Controls.SetChildIndex( paneData.PaneBackground, paneIndex * 3 + 1 );
_contentPane.Controls.SetChildIndex( paneData.PaneCaption, paneIndex * 3 + 2 );
}
if ( visiblePanes > 0 )
{
for( int i = 0; i < PanesCount; i++ )
{
ToolStripButton btn = (ToolStripButton)_paneButtons.Items[ i ];
PaneData paneData = (PaneData)btn.Tag;
bool visible = btn.Checked;
// if ( paneData == toggledPane )
// visible = toggledPaneState;
if ( visible )
{
PaneCaptionButtons buttons = (visiblePanes > 1) ? PaneCaptionButtons.Minimize : PaneCaptionButtons.None;
paneData.PaneCaption.CaptionButtons = buttons;
}
}
}
// _paneButtons.Visible = ( _paneButtons.Items.Count > 1 );
}
///
/// Updates the heights of panes so that all visible panes get their share of height.
///
private void UpdatePaneSizes( int minHeight )
{
int totalNeedHeight = 0;
for( int i = 0; i < PanesCount; i++ )
{
ToolStripButton btn = (ToolStripButton)_paneButtons.Items[ i ];
PaneData paneData = (PaneData)btn.Tag;
if( btn.Checked )
{
int needHeight = minHeight - paneData.PaneBackground.Height;
if( needHeight > 0 )
{
totalNeedHeight += needHeight;
}
}
}
if ( totalNeedHeight > 0 )
{
int totalAvailHeight = 0;
for( int i = 0; i < PanesCount; i++ )
{
PaneData paneData = DataAt( i );
if ( paneData.PaneBackground.Height > minHeight )
{
int availHeight = paneData.PaneBackground.Height - minHeight;
if ( availHeight > totalNeedHeight )
{
availHeight = totalNeedHeight;
}
paneData.PaneBackground.Height = paneData.PaneBackground.Height - availHeight;
totalAvailHeight += availHeight;
}
else if ( paneData.PaneBackground.Height < minHeight )
{
int needHeight = minHeight - paneData.PaneBackground.Height;
if ( needHeight < totalAvailHeight )
{
needHeight = totalAvailHeight;
}
paneData.PaneBackground.Height = paneData.PaneBackground.Height + needHeight;
totalNeedHeight -= needHeight;
totalAvailHeight -= needHeight;
}
}
}
}
///
/// When the size of the pane changes, ensures that all visible child panes get their
/// share of height.
///
protected override void OnLayout( LayoutEventArgs levent )
{
base.OnLayout( levent );
if ( levent.AffectedControl != null && levent.AffectedProperty != null )
{
if ( levent.AffectedProperty == "Bounds" )
{
Form frm = FindForm();
if ( frm != null && frm.WindowState != FormWindowState.Minimized )
{
UpdatePaneSizes( 40 );
}
}
}
}
private PaneData DataForPane( Control pane )
{
foreach( ToolStripButton btn in _paneButtons.Items )
{
PaneData data = (PaneData) btn.Tag;
if ( data.Pane == pane )
return data;
}
throw new Exception( "Could not find PaneData for pane " + pane );
}
private PaneData DataAt( int index )
{
return (PaneData) _paneButtons.Items[ index ].Tag;
}
private void OnPaneEnter( object sender, EventArgs e )
{
SetActivePane( sender as Control );
_lastActivePaneData.PaneCaption.Active = true;
}
private void OnPaneLeave( object sender, EventArgs e )
{
PaneData paneData = DataForPane( sender as Control );
paneData.PaneCaption.Active = false;
}
private void SetActivePane( Control senderCtl )
{
PaneData paneData = DataForPane( senderCtl );
if ( _lastActivePaneData != null )
{
_lastActivePaneData.Pane.ShowSelection = false;
}
_lastActivePaneData = paneData;
_lastActivePaneData.Pane.ShowSelection = true;
}
private static void OnPaneCaptionClick( object sender, EventArgs e )
{
PaneCaption caption = (PaneCaption) sender;
AbstractViewPane pane = ((PaneData)caption.Tag).Pane;
if ( !pane.ContainsFocus )
{
pane.Focus();
// HACK: Remove when all panes are converted to JetListView
if ( !(pane is ResourceTreePaneBase ))
{
IResource node = pane.SelectedResource;
if( node != null )
{
pane.SelectResource( node, false );
}
}
}
}
private void OnPaneMinimize( object sender, EventArgs e )
{
PaneCaption caption = (PaneCaption) sender;
PaneData paneData = (PaneData) caption.Tag;
_paneButtons.SetButtonPressed( paneData.PaneId, false );
}
public IActionContext GetContext( ActionContextKind kind )
{
if ( _lastActivePaneData != null )
{
IContextProvider provider = _lastActivePaneData.Pane as IContextProvider;
if ( provider != null )
{
return provider.GetContext( kind );
}
}
return null;
}
}
internal class SidebarState
{
private readonly int[] _paneHeights;
private readonly int _activePaneIndex;
private IResource _selectedResource;
internal SidebarState( int[] paneHeights, int activePaneIndex, IResource selectedResource )
{
_paneHeights = paneHeights;
_activePaneIndex = activePaneIndex;
_selectedResource = selectedResource;
}
public int[] PaneHeights
{
get { return _paneHeights; }
}
public int ActivePaneIndex
{
get { return _activePaneIndex; }
}
public IResource SelectedResource
{
get { return _selectedResource; }
set { _selectedResource = value; }
}
public static SidebarState RestoreFromIni( string section )
{
ISettingStore ini = Core.SettingStore;
int count = ini.ReadInt( section, "PaneCount", 0 );
if ( count == 0 )
return null;
int[] paneHeights = new int[ count ];
for( int i = 0; i < count; i++ )
{
paneHeights[ i ] = ini.ReadInt( section, "Pane" + i + "Height", 0 );
}
int activePaneIndex = ini.ReadInt( section, "ActivePaneIndex", 0 );
if ( activePaneIndex >= paneHeights.Length )
{
activePaneIndex = 0;
}
IResource selResource = null;
int resId = ini.ReadInt( section, "SelectedResource", -1 );
if ( resId >= 0 )
{
selResource = Core.ResourceStore.TryLoadResource( resId );
}
return new SidebarState( paneHeights, activePaneIndex, selResource );
}
public void SaveToIni( string section )
{
ISettingStore ini = Core.SettingStore;
ini.WriteInt( section, "PaneCount", _paneHeights.Length );
for( int i = 0; i < _paneHeights.Length; i++ )
{
ini.WriteInt( section, "Pane" + i + "Height", _paneHeights [i] );
}
ini.WriteInt( section, "ActivePaneIndex", _activePaneIndex );
ini.WriteInt( section, "SelectedResource", (_selectedResource == null) ? -1 : _selectedResource.Id );
}
}
///
/// The container panel for the toolbar with icons corresponding to structural panes
/// possible within the particular resource tab.
///
internal class SidebarButtons: Panel
{
private ToolStrip _strip;
private readonly Dictionary _mapId = new Dictionary();
// private ColorScheme _colorScheme;
// private string _colorSchemeKey = "Sidebar.Background";
public SidebarButtons()
{
SetStyle( ControlStyles.ResizeRedraw, true );
SetStyle( ControlStyles.Opaque, false );
InitializeComponents();
}
private void InitializeComponents()
{
_strip = new ToolStrip();
_strip.AllowItemReorder = true;
_strip.CanOverflow = true;
_strip.GripStyle = ToolStripGripStyle.Hidden;
_strip.AutoSize = true;
_strip.ImageScalingSize = new Size( 24, 24 );
Controls.Add( _strip );
}
public ToolStripButton GetPaneButton( string paneId )
{
ToolStripButton btn = _mapId[ paneId ];
if( btn == null )
throw new ArgumentException( "Internal Error -- Invalid value for PaneID [" + paneId + "]" );
return btn;
}
public ToolStripButton AddButton( VerticalSidebar.PaneData paneData, Image image, string text )
{
ToolStripButton btn = new ToolStripButton();
btn.Image = image;
btn.ToolTipText = text;
btn.DisplayStyle = ToolStripItemDisplayStyle.Image;
btn.CheckOnClick = true;
btn.Tag = paneData;
_strip.Items.Add( btn );
_mapId.Add( paneData.PaneId, btn );
return btn;
}
public void SetButtonPressed( string id, bool val )
{
ToolStripButton btn = _mapId[ id ];
if( btn != null )
{
btn.Checked = val;
}
}
public int CheckedButtonsCount
{
get
{
int count = 0;
foreach( ToolStripButton btn in _strip.Items )
{
if( btn.Checked )
count ++;
}
return count;
}
}
public void CheckButtonsState()
{
bool enable = (CheckedButtonsCount > 1);
foreach( ToolStripButton btn in _strip.Items )
{
if( btn.Checked )
btn.Enabled = enable;
}
}
public ToolStripItemCollection Items
{
get { return _strip.Items; }
}
}
}