///
/// 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.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using JetBrains.Omea.GUIControls;
using JetBrains.Omea.GUIControls.CommandBar;
using JetBrains.Omea.OpenAPI;
using JetBrains.UI.Interop;
namespace JetBrains.Omea
{
///
/// A bar containing shortcuts for quickly jumping to OmniaMea resources.
///
internal class ShortcutBar : UserControl, ICommandBar, ICommandBarSite
{
///
/// A toolbar with a chevron that displays the shortcuts.
///
private ChevronBar _chevronBar;
private ContextMenu _shortcutContextMenu;
private MenuItem _miDeleteShortcut;
private IContainer components;
private IResource _contextMenuShortcut;
private int _maxOrder;
///
/// Number of shortcuts currently displayed on the shortcut bar.
/// If 0, then the shortcuts toolbar should be hidden and the no-items-drop-welcome displayed instead.
///
private int _numShortcuts;
private IResourceList _resourcesWithShortcuts;
private ColorScheme _colorScheme;
private ToolTip _tooltip;
private static ShortcutBar _theInstance;
private Label _lblOrganize;
///
/// The command bar site.
///
private ICommandBarSite _site = null;
///
/// The grip support.
///
protected Grip _grip;
///
/// Text for the no-items-banner.
///
protected static readonly string _sNoItemsBanner = "Drop resources here to create shortcuts";
///
/// Font for the no-items-banner.
///
protected static readonly Font _fontNoItemsBanner = new Font( "Tahoma", 8.25F, FontStyle.Regular, GraphicsUnit.Point, ((Byte) (204)) );
///
/// Format flags for the no-items-banner text.
///
private DrawTextFormatFlags _dtfNoItemsBannerFormatFlags = DrawTextFormatFlags.DT_CENTER | DrawTextFormatFlags.DT_NOPREFIX | DrawTextFormatFlags.DT_WORDBREAK | DrawTextFormatFlags.DT_END_ELLIPSIS;
///
/// Padding between the title and the controls after it.
///
protected static readonly int c_nAfterTitlePadding = 4;
#region Construction
public ShortcutBar()
{
_theInstance = this;
// This call is required by the Windows.Forms Form Designer.
InitializeComponentSelf();
_lblOrganize = new Label();
_lblOrganize.Text = "Organize...";
}
public static ShortcutBar GetInstance()
{
return _theInstance;
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#endregion
#region Visual Init
///
/// Visual Init.
///
private void InitializeComponentSelf()
{
components = new Container();
_shortcutContextMenu = new ContextMenu();
_miDeleteShortcut = new MenuItem();
_tooltip = new ToolTip( components );
SuspendLayout();
//
// _chevronBar
//
_chevronBar = new ChevronBar();
_chevronBar.SetSite( this );
_chevronBar.AllowDrop = true;
_chevronBar.Name = "_chevronBar";
_chevronBar.TabIndex = 1;
_chevronBar.ChevronMenuItemClick += OnChevronMenuItemClick;
_chevronBar.DragDrop += OnDragDropAny;
_chevronBar.DragEnter += OnDragEnterAny;
_chevronBar.BackColor = SystemColors.Control;
_chevronBar.GetChevronMenuText = OnGetChevronMenuText;
_chevronBar.SeparateHiddenControls = true;
_chevronBar.AllowOversizing = true;
//
// _shortcutContextMenu
//
_shortcutContextMenu.MenuItems.AddRange( new MenuItem[] { _miDeleteShortcut } );
//
// miDeleteShortcut
//
_miDeleteShortcut.Index = 0;
_miDeleteShortcut.Text = "Delete Shortcut";
_miDeleteShortcut.Click += miDeleteShortcut_Click;
// Grip
_grip = new Grip( this );
_grip.SetSite( this );
//
// ShortcutBar
//
Controls.Add( _chevronBar );
AllowDrop = true;
Name = "ShortcutBar";
Font = new Font( "Tahoma", 8.25F, FontStyle.Bold, GraphicsUnit.Point, ((Byte) (204)) );
Text = "Shortcuts";
Size = new Size( 308, 30 );
DragEnter += OnDragEnterAny;
DragDrop += OnDragDropAny;
ResumeLayout( false );
SetStyle( ControlStyles.AllPaintingInWmPaint
| ControlStyles.CacheText
| ControlStyles.ContainerControl
| ControlStyles.ResizeRedraw
| ControlStyles.Selectable
| ControlStyles.UserPaint
| ControlStyles.Opaque
, true );
}
#endregion
public ColorScheme ColorScheme
{
get { return _colorScheme; }
set
{
_colorScheme = value;
//BackColor = ColorScheme.GetColor( _colorScheme, "ShortcutBar.Background",
// Color.FromArgb( 223, 220, 203 ) );
//_chevronBar.ChevronBackColor = BackColor;
}
}
///
/// Fills the chevron bar with shortcut links.
///
public void RebuildShortcutBar()
{
if( _resourcesWithShortcuts != null )
{
_resourcesWithShortcuts.ResourceDeleting -= OnResourceWithShortcutDeleting;
_resourcesWithShortcuts.Dispose();
}
_chevronBar.ClearControls();
using( new LayoutSuspender( _chevronBar ) )
{
IResourceList shortcuts = Core.ResourceStore.GetAllResources( "Shortcut" );
shortcuts.Sort( new int[] {ShortcutProps.Order}, true );
_numShortcuts = shortcuts.Count;
foreach( IResource shortcut in shortcuts )
{
if( shortcut.IsDeleting )
continue;
IResource target = shortcut.GetLinkProp( ShortcutProps.Target );
if( target == null || target.IsDeleting )
{
// delete the shortcut when its target is deleted
new ResourceProxy( shortcut ).Delete();
continue;
}
if( !Core.ResourceStore.ResourceTypes[ target.Type ].OwnerPluginLoaded )
{
continue;
}
ResourceLinkLabel lbl = new ResourceLinkLabel();
lbl.Resource = target;
if( shortcut.HasProp( "Name" ) )
{
lbl.Text = shortcut.GetStringProp( "Name" );
if( target.DisplayName != lbl.Text )
{
_tooltip.SetToolTip( lbl.NameLabel, target.DisplayName );
}
}
lbl.Tag = shortcut;
lbl.ResourceLinkClicked += OnResourceLinkClicked;
lbl.LinkContextMenu += OnResourceLinkContextMenu;
lbl.ResourceDragOver += OnResourceLinkDragOver;
lbl.ResourceDrop += OnResourceLinkDrop;
lbl.Width = lbl.PreferredWidth + 4;
_chevronBar.AddControl( lbl );
_maxOrder = shortcut.GetIntProp( ShortcutProps.Order );
}
_chevronBar.AddHiddenControl( _lblOrganize );
}
_resourcesWithShortcuts = Core.ResourceStore.FindResourcesWithPropLive( null, ShortcutProps.Target );
_resourcesWithShortcuts.ResourceDeleting += OnResourceWithShortcutDeleting;
if( _site != null )
_site.PerformLayout( this );
PerformLayout();
}
private void OnResourceWithShortcutDeleting( object sender, ResourceIndexEventArgs e )
{
Core.UIManager.QueueUIJob( new MethodInvoker( RebuildShortcutBar ) );
}
private static void OnResourceLinkDragOver( object sender, ResourceDragEventArgs e )
{
e.Effect = DragDropEffects.Link;
}
private void OnResourceLinkDrop( object sender, ResourceDragEventArgs e )
{
AddShortcutsFromList( e.DroppedResources );
}
private static void OnResourceLinkClicked( object sender, CancelEventArgs e )
{
ResourceLinkLabel lbl = (ResourceLinkLabel) sender;
HandleLinkLabelClick( lbl );
e.Cancel = true;
}
///
/// When a link is clicked, opens the webpage if it's a Web link, or displays
/// the resource in context if it's a resource link.
///
private static void HandleLinkLabelClick( ResourceLinkLabel lbl )
{
if( lbl.Resource.IsDeleted )
return;
if( lbl.Resource.Type == "Weblink" )
Core.UIManager.OpenInNewBrowserWindow(lbl.Resource.GetStringProp( "URL" ));
else
{
IResource shortcut = (IResource) lbl.Tag;
IUIManager uiManager = Core.UIManager;
uiManager.BeginUpdateSidebar();
if( shortcut.HasProp( ShortcutProps.Workspace ) )
{
Core.WorkspaceManager.ActiveWorkspace = shortcut.GetLinkProp( ShortcutProps.Workspace );
}
else
{
Core.WorkspaceManager.ActiveWorkspace = null;
}
if( shortcut.HasProp( ShortcutProps.TabID ) )
{
Core.TabManager.CurrentTabId = shortcut.GetStringProp( ShortcutProps.TabID );
}
uiManager.EndUpdateSidebar();
ActionContext context = new ActionContext( ActionContextKind.Other, null,
lbl.Resource.ToResourceList() );
if( !Core.ActionManager.ExecuteLinkClickAction( context ) )
{
uiManager.DisplayResourceInContext( lbl.Resource );
}
}
}
///
/// When a link is right-clicked, shows the context menu.
///
private void OnResourceLinkContextMenu( object sender, ResourceLinkLabelEventArgs e )
{
ResourceLinkLabel lbl = (ResourceLinkLabel) sender;
_contextMenuShortcut = (IResource) lbl.Tag;
_shortcutContextMenu.Show( lbl, e.Point );
}
///
/// "Delete Shortcut" menu item handler.
///
private void miDeleteShortcut_Click( object sender, EventArgs e )
{
if( _contextMenuShortcut != null )
{
new ResourceProxy( _contextMenuShortcut ).Delete();
//RebuildShortcutBar();
}
}
///
/// Drag enter handler - accepts any resource list dragged in the pane.
///
private static void OnDragEnterAny( object sender, DragEventArgs e )
{
if( e.Data.GetDataPresent( typeof( IResourceList ) ) )
{
e.Effect = DragDropEffects.Link;
}
}
///
/// Drag drop handler - creates a shortcut if there isn't any shortcut
/// for the dropped resource.
///
private void OnDragDropAny( object sender, DragEventArgs e )
{
if( e.Data.GetDataPresent( typeof( IResourceList ) ) )
{
IResourceList droppedResources = (IResourceList) e.Data.GetData( typeof( IResourceList ) );
AddShortcutsFromList( droppedResources );
}
}
public int ShortcutCount
{
get { return _numShortcuts; }
}
///
/// Adds shortcuts to resources from the specified resource list to the shortcut
/// bar.
///
internal void AddShortcutsFromList( IResourceList droppedResources )
{
foreach( IResource res in droppedResources )
{
AddShortcutToResource( res );
}
}
///
/// Adds a shortcut to the specified resource to the shortcut bar.
///
public void AddShortcutToResource( IResource newShortcut )
{
ITabManager tabManager = Core.TabManager;
IResource activeWorkspace = Core.WorkspaceManager.ActiveWorkspace;
IResourceList shortcutTargets = newShortcut.GetLinksOfType( "Shortcut", ShortcutProps.Target );
if( newShortcut.Type == "SearchView" )
{
foreach( IResource res in shortcutTargets )
{
if( res.GetStringProp( ShortcutProps.TabID ) == tabManager.CurrentTabId &&
res.GetLinkProp( ShortcutProps.Workspace ) == activeWorkspace )
{
return;
}
}
}
else
{
if( shortcutTargets.Count > 0 )
return;
}
ResourceProxy proxy = ResourceProxy.BeginNewResource( "Shortcut" );
proxy.SetProp( ShortcutProps.Order, _maxOrder + 1 );
proxy.AddLink( ShortcutProps.Target, newShortcut );
if( activeWorkspace != null )
{
proxy.AddLink( ShortcutProps.Workspace, activeWorkspace );
}
if( newShortcut.Type == "SearchView" )
{
proxy.SetProp( ShortcutProps.TabID, tabManager.CurrentTabId );
string wsName = "";
if ( activeWorkspace != null )
{
wsName = " in " + activeWorkspace.GetStringProp( Core.Props.Name );
}
proxy.SetProp( Core.Props.Name, tabManager.CurrentTab.Name + wsName + ": " +
newShortcut.DisplayName );
}
else if( newShortcut.DisplayName.Length > 20 )
{
proxy.SetProp( Core.Props.Name, newShortcut.DisplayName.Substring( 0, 20 ) + "..." );
}
proxy.EndUpdate();
RebuildShortcutBar();
}
///
/// When a link in the chevron menu is clicked, performs regular processing
/// of the click.
///
private void OnChevronMenuItemClick( object sender, ChevronBar.ChevronMenuItemClickEventArgs args )
{
ResourceLinkLabel lbl = args.ClickedControl as ResourceLinkLabel;
if( lbl != null )
{
HandleLinkLabelClick( lbl );
}
else
{
OrganizeShortcuts();
}
}
internal void OrganizeShortcuts()
{
using( OrganizeShortcutsDlg dlg = new OrganizeShortcutsDlg() )
{
dlg.ShowOrganizeDialog();
}
RebuildShortcutBar();
}
private static string OnGetChevronMenuText( Control ctl )
{
ResourceLinkLabel lbl = ctl as ResourceLinkLabel;
if( lbl != null )
{
IResource shortcut = lbl.Resource.GetLinkProp( ShortcutProps.Target );
if( shortcut != null && !shortcut.HasProp( ShortcutProps.Renamed ) &&
!shortcut.HasProp( ShortcutProps.TabID ) )
{
return lbl.Resource.DisplayName;
}
}
return ctl.Text;
}
protected override void OnPaint( PaintEventArgs e )
{
// Background
e.Graphics.FillRectangle( SystemBrushes.Control, 2, 2, ClientRectangle.Width - 4, ClientRectangle.Height - 4 );
// Border
/*
Borders drawing sequence:
# dark-dark
@ dark
L lightlight
@ @@@@@@@@@@@@@@-1-@@@@@@@@@@@@@@@@@L
@ L
@ L LLLLLLLLLLL-5-LLLLLLLLLLLLLL# L
@ L # L
| | | |
2 6 8 4
| | | |
@ L L
@ #############-7-############### L
@
LLLLLLLLLLLLLLLL-3-LLLLLLLLLLLLLLLLLL
*/
Rectangle rc = ClientRectangle;
// Outer
e.Graphics.DrawLine( SystemPens.ControlDark, rc.Left + 1, rc.Top + 0, rc.Right - 2, rc.Top + 0 ); // 1
e.Graphics.DrawLine( SystemPens.ControlDark, rc.Left + 0, rc.Top + 0, rc.Left + 0, rc.Bottom - 2 ); // 2
e.Graphics.DrawLine( SystemPens.ControlLightLight, rc.Left + 0, rc.Bottom - 1, rc.Right - 1, rc.Bottom - 1 ); // 3
e.Graphics.DrawLine( SystemPens.ControlLightLight, rc.Right - 1, rc.Top + 0, rc.Right - 1, rc.Bottom - 2 ); // 4
// Inner
e.Graphics.DrawLine( SystemPens.ControlLightLight, rc.Left + 2, rc.Top + 1, rc.Right - 3, rc.Top + 1 ); // 5
e.Graphics.DrawLine( SystemPens.ControlLightLight, rc.Left + 1, rc.Top + 1, rc.Left + 1, rc.Bottom - 3 ); // 6
e.Graphics.DrawLine( SystemPens.ControlDarkDark, rc.Left + 1, rc.Bottom - 2, rc.Right - 2, rc.Bottom - 2 ); // 7
e.Graphics.DrawLine( SystemPens.ControlDarkDark, rc.Right - 2, rc.Top + 1, rc.Right - 2, rc.Bottom - 3 ); // 8
// Paint the grip
_grip.OnPaint( e.Graphics );
// Paint the title (if visisble)
if( _rectTitle != Rectangle.Empty )
JetLinkLabel.DrawText( e.Graphics, Text, _rectTitle, Font, SystemColors.ControlText,
DrawTextFormatFlags.DT_CENTER | DrawTextFormatFlags.DT_NOPREFIX | DrawTextFormatFlags.DT_SINGLELINE | DrawTextFormatFlags.DT_VCENTER );
// Paint the no-items-banner (if visisble)
if( _rectNoItemsBanner != Rectangle.Empty )
JetLinkLabel.DrawText( e.Graphics, _sNoItemsBanner, _rectNoItemsBanner, _fontNoItemsBanner, SystemColors.ControlText,
_dtfNoItemsBannerFormatFlags );
}
///
/// The outer rectangle (.Bounds at the moment of calculation).
///
private Rectangle _rectBounds;
///
/// Client area within the border (no padding).
///
private Rectangle _rectInBorder;
///
/// Client area without the padding and with grip excluded.
///
private Rectangle _rectWithoutGrip;
///
/// Client area within the borders, without the grip, and with padding included.
///
private Rectangle _rectClientPadded;
///
/// Title placeholder.
///
private Rectangle _rectTitle;
///
/// Placeholder for the shortcuts chevronette toolbar.
///
private Rectangle _rectToolbar;
///
/// Placeholder for the welcome string.
///
private Rectangle _rectNoItemsBanner;
///
/// Optimal size of the title string.
///
private Size _sizeTitle;
///
/// Optimal size of the no-items-banner string.
///
private Size _sizeNoItemsBanner;
protected override void OnLayout( LayoutEventArgs levent )
{
using( new LayoutSuspender( _chevronBar ) )
{
//////
// Calculate the rectangles
Size sizeToolbarMin = _chevronBar.MinSize;
_rectBounds = ClientRectangle;
// Client rect within the control's borders
_rectInBorder = ClientRectangle;
_rectInBorder.Inflate( -2, -2 ); // Exclude the borders
// Exclude the grip
_rectWithoutGrip = _grip.OnLayout( _rectInBorder );
// The innermost client rect
_rectClientPadded = _rectWithoutGrip;
_rectClientPadded.Inflate( -2, -1 );
// Size of the labels
_sizeTitle = JetLinkLabel.GetTextSize( this, Text, Font );
_sizeNoItemsBanner = JetLinkLabel.GetTextSize( this, _sNoItemsBanner, _fontNoItemsBanner, _rectClientPadded.Size, _dtfNoItemsBannerFormatFlags );
if( _numShortcuts != 0 )
{ // There are shortcuts. Include the toolbar and (possibly) the title
if( _rectClientPadded.Width >= sizeToolbarMin.Width + c_nAfterTitlePadding + _sizeTitle.Width )
{ // Title fits
//_rectTitle = new Rectangle( _rectClientPadded.Location, new Size( _sizeTitle.Width, _rectClientPadded.Height ) );
_rectTitle = new Rectangle( new Point( _rectClientPadded.Left, _rectClientPadded.Top + (_rectClientPadded.Height - _sizeTitle.Height) / 2 ), _sizeTitle );
_rectToolbar = new Rectangle( _rectTitle.Right + c_nAfterTitlePadding, _rectClientPadded.Top, _rectClientPadded.Width - (_rectTitle.Width + c_nAfterTitlePadding), _rectClientPadded.Height );
}
else
{ // Title does not fit
_rectTitle = Rectangle.Empty;
_rectToolbar = _rectClientPadded;
}
_rectNoItemsBanner = Rectangle.Empty;
}
else
{ // No shortcuts, display just the no-items-banner
_rectTitle = Rectangle.Empty;
_rectToolbar = Rectangle.Empty;
// Align the banner
Size sizeFit = new Size
(
_sizeNoItemsBanner.Width < _rectClientPadded.Width ? _sizeNoItemsBanner.Width : _rectClientPadded.Width,
_sizeNoItemsBanner.Height < _rectClientPadded.Height ? _sizeNoItemsBanner.Height : _rectClientPadded.Height
);
_rectNoItemsBanner = new Rectangle(
new Point
(
_rectClientPadded.Left, // H-Align left
_rectClientPadded.Top + (_rectClientPadded.Height - sizeFit.Height) / 2 // V-center
),
sizeFit );
}
/////////////////////
// Apply the layout
_chevronBar.Visible = _rectToolbar != Rectangle.Empty;
_chevronBar.Bounds = _rectToolbar;
}
// Apply the visual changes
Invalidate( false );
}
#region ICommandBar Interface Members
public void SetSite( ICommandBarSite site )
{
_site = site;
}
public Size MinSize
{
get
{
// Min size of the inlying controls
Size sizeInnerMinSize = _numShortcuts != 0 ?
_chevronBar.MinSize // It's the chevron only
: new Size( 0, 0 ); // No shortcuts, no size (we may drop even on the text-less small bar)
// Append the paddings and grip
Size sizePadding = _rectBounds.Size - _rectClientPadded.Size;
return sizeInnerMinSize + sizePadding;
}
}
public Size MaxSize
{
get
{
// Max size of the inlying controls
Size sizeInnerMaxSize;
if( _numShortcuts != 0 ) // There are shortcuts, ask the chevron bar
sizeInnerMaxSize = _chevronBar.MaxSize; // It's the chevron only
else
sizeInnerMaxSize = new Size( _sizeNoItemsBanner.Width, 30 ); // Size width to the max text label width
// Append the paddings and grip
Size sizePadding = _rectBounds.Size - _rectClientPadded.Size;
Size sizeRet = sizeInnerMaxSize + sizePadding;
sizeRet.Width += _sizeTitle.Width + c_nAfterTitlePadding;
// Don't allow the max-sizes to wrap over to negative numbers
if(sizeInnerMaxSize.Width == int.MaxValue)
sizeRet.Width = int.MaxValue;
if(sizeInnerMaxSize.Height == int.MaxValue)
sizeRet.Height = int.MaxValue;
return sizeRet;
}
}
public Size OptimalSize
{
get
{
Size sizeInnerOptSize = (_numShortcuts != 0) ?
_chevronBar.OptimalSize // There are controls, take their summary size
: _sizeNoItemsBanner; // There are no controls, fit the label
// Append the paddings and grip
Size sizePadding = _rectBounds.Size - _rectClientPadded.Size;
Size sizeRet = sizeInnerOptSize + sizePadding;
sizeRet.Width += _sizeTitle.Width + c_nAfterTitlePadding;
return sizeInnerOptSize + sizePadding;
}
}
public Size Integral
{
get { return new Size( 1, 1 ); }
}
#endregion
#region ICommandBarSite Interface Members
public bool RequestMove( ICommandBar sender, Size offset )
{
if( _site != null )
return _site.RequestMove( sender, offset );
else
return false;
}
public bool RequestSize( ICommandBar sender, Size difference )
{
if( _site != null )
return _site.RequestSize( sender, difference );
else
return false;
}
public bool PerformLayout( ICommandBar sender )
{
if( (_site != null) && (_site.PerformLayout( sender )) )
{
PerformLayout(); // Call the base function
return true;
}
else
return false;
}
#endregion
}
#region ShortcutProps Class
internal class ShortcutProps
{
private static int _propShortcutTarget;
private static int _propShortcutWorkspace;
private static int _propShortcutOrder;
private static int _propShortcutTabID;
private static int _propShortcutRenamed;
///
/// Registers the resource types used for shortcuts.
///
public static void Register()
{
IResourceStore store = ICore.Instance.ResourceStore;
store.ResourceTypes.Register( "Shortcut", "", ResourceTypeFlags.Internal | ResourceTypeFlags.NoIndex );
_propShortcutTarget = store.PropTypes.Register( "ShortcutTarget", PropDataType.Link, PropTypeFlags.Internal );
_propShortcutWorkspace = store.PropTypes.Register( "ShortcutWorkspace", PropDataType.Link, PropTypeFlags.Internal );
_propShortcutOrder = store.PropTypes.Register( "ShortcutOrder", PropDataType.Int, PropTypeFlags.Internal );
_propShortcutTabID = store.PropTypes.Register( "ShortcutTabID", PropDataType.String, PropTypeFlags.Internal );
_propShortcutRenamed = store.PropTypes.Register( "ShortcutRenamed", PropDataType.Bool, PropTypeFlags.Internal );
}
internal static int Target
{
get { return _propShortcutTarget; }
}
internal static int Workspace
{
get { return _propShortcutWorkspace; }
}
internal static int Order
{
get { return _propShortcutOrder; }
}
internal static int TabID
{
get { return _propShortcutTabID; }
}
internal static int Renamed
{
get { return _propShortcutRenamed; }
}
}
#endregion
}