using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using System.ComponentModel; using System.Collections; using System.Collections.Generic; namespace WeifenLuo.WinFormsUI.Docking { internal class VS2005DockPaneStrip : DockPaneStripBase { private class TabVS2005 : Tab { public TabVS2005(IDockContent content) : base(content) { } private int m_tabX; public int TabX { get { return m_tabX; } set { m_tabX = value; } } private int m_tabWidth; public int TabWidth { get { return m_tabWidth; } set { m_tabWidth = value; } } private int m_maxWidth; public int MaxWidth { get { return m_maxWidth; } set { m_maxWidth = value; } } private bool m_flag; protected internal bool Flag { get { return m_flag; } set { m_flag = value; } } } protected internal override DockPaneStripBase.Tab CreateTab(IDockContent content) { return new TabVS2005(content); } private sealed class InertButton : InertButtonBase { private Bitmap m_image0, m_image1; public InertButton(Bitmap image0, Bitmap image1) : base() { m_image0 = image0; m_image1 = image1; } private int m_imageCategory = 0; public int ImageCategory { get { return m_imageCategory; } set { if (m_imageCategory == value) return; m_imageCategory = value; Invalidate(); } } public override Bitmap Image { get { return ImageCategory == 0 ? m_image0 : m_image1; } } } #region consts private const int _ToolWindowStripGapTop = 0; private const int _ToolWindowStripGapBottom = 1; private const int _ToolWindowStripGapLeft = 0; private const int _ToolWindowStripGapRight = 0; private const int _ToolWindowImageHeight = 16; private const int _ToolWindowImageWidth = 16; private const int _ToolWindowImageGapTop = 3; private const int _ToolWindowImageGapBottom = 1; private const int _ToolWindowImageGapLeft = 2; private const int _ToolWindowImageGapRight = 0; private const int _ToolWindowTextGapRight = 3; private const int _ToolWindowTabSeperatorGapTop = 3; private const int _ToolWindowTabSeperatorGapBottom = 3; private const int _DocumentStripGapTop = 0; private const int _DocumentStripGapBottom = 1; private const int _DocumentTabMaxWidth = 200; private const int _DocumentButtonGapTop = 4; private const int _DocumentButtonGapBottom = 4; private const int _DocumentButtonGapBetween = 0; private const int _DocumentButtonGapRight = 3; private const int _DocumentTabGapTop = 3; private const int _DocumentTabGapLeft = 3; private const int _DocumentTabGapRight = 3; private const int _DocumentIconGapBottom = 2; private const int _DocumentIconGapLeft = 8; private const int _DocumentIconGapRight = 0; private const int _DocumentIconHeight = 16; private const int _DocumentIconWidth = 16; private const int _DocumentTextGapRight = 3; #endregion private static Bitmap _imageButtonClose; private static Bitmap ImageButtonClose { get { if (_imageButtonClose == null) _imageButtonClose = Resources.DockPane_Close; return _imageButtonClose; } } private InertButton m_buttonClose; private InertButton ButtonClose { get { if (m_buttonClose == null) { m_buttonClose = new InertButton(ImageButtonClose, ImageButtonClose); m_toolTip.SetToolTip(m_buttonClose, ToolTipClose); m_buttonClose.Click += new EventHandler(Close_Click); Controls.Add(m_buttonClose); } return m_buttonClose; } } private static Bitmap _imageButtonWindowList; private static Bitmap ImageButtonWindowList { get { if (_imageButtonWindowList == null) _imageButtonWindowList = Resources.DockPane_Option; return _imageButtonWindowList; } } private static Bitmap _imageButtonWindowListOverflow; private static Bitmap ImageButtonWindowListOverflow { get { if (_imageButtonWindowListOverflow == null) _imageButtonWindowListOverflow = Resources.DockPane_OptionOverflow; return _imageButtonWindowListOverflow; } } private InertButton m_buttonWindowList; private InertButton ButtonWindowList { get { if (m_buttonWindowList == null) { m_buttonWindowList = new InertButton(ImageButtonWindowList, ImageButtonWindowListOverflow); m_toolTip.SetToolTip(m_buttonWindowList, ToolTipSelect); m_buttonWindowList.Click += new EventHandler(WindowList_Click); Controls.Add(m_buttonWindowList); } return m_buttonWindowList; } } private static GraphicsPath GraphicsPath { get { return VS2005AutoHideStrip.GraphicsPath; } } private IContainer m_components; private ToolTip m_toolTip; private IContainer Components { get { return m_components; } } #region Customizable Properties private static int ToolWindowStripGapTop { get { return _ToolWindowStripGapTop; } } private static int ToolWindowStripGapBottom { get { return _ToolWindowStripGapBottom; } } private static int ToolWindowStripGapLeft { get { return _ToolWindowStripGapLeft; } } private static int ToolWindowStripGapRight { get { return _ToolWindowStripGapRight; } } private static int ToolWindowImageHeight { get { return _ToolWindowImageHeight; } } private static int ToolWindowImageWidth { get { return _ToolWindowImageWidth; } } private static int ToolWindowImageGapTop { get { return _ToolWindowImageGapTop; } } private static int ToolWindowImageGapBottom { get { return _ToolWindowImageGapBottom; } } private static int ToolWindowImageGapLeft { get { return _ToolWindowImageGapLeft; } } private static int ToolWindowImageGapRight { get { return _ToolWindowImageGapRight; } } private static int ToolWindowTextGapRight { get { return _ToolWindowTextGapRight; } } private static int ToolWindowTabSeperatorGapTop { get { return _ToolWindowTabSeperatorGapTop; } } private static int ToolWindowTabSeperatorGapBottom { get { return _ToolWindowTabSeperatorGapBottom; } } private static string _toolTipClose; private static string ToolTipClose { get { if (_toolTipClose == null) _toolTipClose = Strings.DockPaneStrip_ToolTipClose; return _toolTipClose; } } private static string _toolTipSelect; private static string ToolTipSelect { get { if (_toolTipSelect == null) _toolTipSelect = Strings.DockPaneStrip_ToolTipWindowList; return _toolTipSelect; } } private TextFormatFlags ToolWindowTextFormat { get { TextFormatFlags textFormat = TextFormatFlags.EndEllipsis | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter; if (RightToLeft == RightToLeft.Yes) return textFormat | TextFormatFlags.RightToLeft | TextFormatFlags.Right; else return textFormat; } } private static int DocumentStripGapTop { get { return _DocumentStripGapTop; } } private static int DocumentStripGapBottom { get { return _DocumentStripGapBottom; } } private TextFormatFlags DocumentTextFormat { get { TextFormatFlags textFormat = TextFormatFlags.EndEllipsis | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter; if (RightToLeft == RightToLeft.Yes) return textFormat | TextFormatFlags.RightToLeft; else return textFormat; } } private static int DocumentTabMaxWidth { get { return _DocumentTabMaxWidth; } } private static int DocumentButtonGapTop { get { return _DocumentButtonGapTop; } } private static int DocumentButtonGapBottom { get { return _DocumentButtonGapBottom; } } private static int DocumentButtonGapBetween { get { return _DocumentButtonGapBetween; } } private static int DocumentButtonGapRight { get { return _DocumentButtonGapRight; } } private static int DocumentTabGapTop { get { return _DocumentTabGapTop; } } private static int DocumentTabGapLeft { get { return _DocumentTabGapLeft; } } private static int DocumentTabGapRight { get { return _DocumentTabGapRight; } } private static int DocumentIconGapBottom { get { return _DocumentIconGapBottom; } } private static int DocumentIconGapLeft { get { return _DocumentIconGapLeft; } } private static int DocumentIconGapRight { get { return _DocumentIconGapRight; } } private static int DocumentIconWidth { get { return _DocumentIconWidth; } } private static int DocumentIconHeight { get { return _DocumentIconHeight; } } private static int DocumentTextGapRight { get { return _DocumentTextGapRight; } } private static Pen PenToolWindowTabBorder { get { return SystemPens.GrayText; } } private static Pen PenDocumentTabActiveBorder { get { return SystemPens.ControlDarkDark; } } private static Pen PenDocumentTabInactiveBorder { get { return SystemPens.GrayText; } } #endregion public VS2005DockPaneStrip(DockPane pane) : base(pane) { SetStyle(ControlStyles.ResizeRedraw | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); SuspendLayout(); m_components = new Container(); m_toolTip = new ToolTip(Components); m_selectMenu = new ContextMenuStrip(Components); ResumeLayout(); } protected override void Dispose(bool disposing) { if (disposing) { Components.Dispose(); if (m_boldFont != null) { m_boldFont.Dispose(); m_boldFont = null; } } base.Dispose (disposing); } private static Font TextFont { get { return SystemInformation.MenuFont; } } private Font m_font; private Font m_boldFont; private Font BoldFont { get { if (IsDisposed) return null; if (m_boldFont == null) { m_font = TextFont; m_boldFont = new Font(TextFont, FontStyle.Bold); } else if (m_font != TextFont) { m_boldFont.Dispose(); m_font = TextFont; m_boldFont = new Font(TextFont, FontStyle.Bold); } return m_boldFont; } } private int m_startDisplayingTab = 0; private int StartDisplayingTab { get { return m_startDisplayingTab; } set { m_startDisplayingTab = value; Invalidate(); } } private int m_endDisplayingTab = 0; private int EndDisplayingTab { get { return m_endDisplayingTab; } set { m_endDisplayingTab = value; } } private int m_firstDisplayingTab = 0; private int FirstDisplayingTab { get { return m_firstDisplayingTab; } set { m_firstDisplayingTab = value; } } private bool m_documentTabsOverflow = false; private bool DocumentTabsOverflow { set { if (m_documentTabsOverflow == value) return; m_documentTabsOverflow = value; if (value) ButtonWindowList.ImageCategory = 1; else ButtonWindowList.ImageCategory = 0; } } protected internal override int MeasureHeight() { if (Appearance == DockPane.AppearanceStyle.ToolWindow) return MeasureHeight_ToolWindow(); else return MeasureHeight_Document(); } private int MeasureHeight_ToolWindow() { if (DockPane.IsAutoHide || Tabs.Count <= 1) return 0; int height = Math.Max(TextFont.Height, ToolWindowImageHeight + ToolWindowImageGapTop + ToolWindowImageGapBottom) + ToolWindowStripGapTop + ToolWindowStripGapBottom; return height; } private int MeasureHeight_Document() { int height = Math.Max(TextFont.Height + DocumentTabGapTop, ButtonClose.Height + DocumentButtonGapTop + DocumentButtonGapBottom) + DocumentStripGapBottom + DocumentStripGapTop; return height; } protected override void OnPaint(PaintEventArgs e) { Rectangle rect = TabsRectangle; if (Appearance == DockPane.AppearanceStyle.Document) { rect.X -= DocumentTabGapLeft; // Add these values back in so that the DockStrip color is drawn // beneath the close button and window list button. rect.Width += DocumentTabGapLeft + DocumentTabGapRight + DocumentButtonGapRight + ButtonClose.Width + ButtonWindowList.Width; // It is possible depending on the DockPanel DocumentStyle to have // a Document without a DockStrip. if (rect.Width > 0 && rect.Height > 0) { Color startColor = DockPane.DockPanel.Skin.DockPaneStripSkin.DocumentGradient.DockStripGradient.StartColor; Color endColor = DockPane.DockPanel.Skin.DockPaneStripSkin.DocumentGradient.DockStripGradient.EndColor; LinearGradientMode gradientMode = DockPane.DockPanel.Skin.DockPaneStripSkin.DocumentGradient.DockStripGradient.LinearGradientMode; using (LinearGradientBrush brush = new LinearGradientBrush(rect, startColor, endColor, gradientMode)) { e.Graphics.FillRectangle(brush, rect); } } } else { Color startColor = DockPane.DockPanel.Skin.DockPaneStripSkin.ToolWindowGradient.DockStripGradient.StartColor; Color endColor = DockPane.DockPanel.Skin.DockPaneStripSkin.ToolWindowGradient.DockStripGradient.EndColor; LinearGradientMode gradientMode = DockPane.DockPanel.Skin.DockPaneStripSkin.ToolWindowGradient.DockStripGradient.LinearGradientMode; using (LinearGradientBrush brush = new LinearGradientBrush(rect, startColor, endColor, gradientMode)) { e.Graphics.FillRectangle(brush, rect); } } base.OnPaint (e); CalculateTabs(); if (Appearance == DockPane.AppearanceStyle.Document && DockPane.ActiveContent != null) { if (EnsureDocumentTabVisible(DockPane.ActiveContent, false)) CalculateTabs(); } DrawTabStrip(e.Graphics); } protected override void OnRefreshChanges() { SetInertButtons(); Invalidate(); } protected internal override GraphicsPath GetOutline(int index) { if (Appearance == DockPane.AppearanceStyle.Document) return GetOutline_Document(index); else return GetOutline_ToolWindow(index); } private GraphicsPath GetOutline_Document(int index) { Rectangle rectTab = GetTabRectangle(index); rectTab.X -= rectTab.Height / 2; rectTab.Intersect(TabsRectangle); rectTab = RectangleToScreen(DrawHelper.RtlTransform(this, rectTab)); Rectangle rectPaneClient = DockPane.RectangleToScreen(DockPane.ClientRectangle); GraphicsPath path = new GraphicsPath(); GraphicsPath pathTab = GetTabOutline_Document(Tabs[index], true, true, true); path.AddPath(pathTab, true); if (DockPane.DockPanel.DocumentTabStripLocation == DocumentTabStripLocation.Bottom) { path.AddLine(rectTab.Right, rectTab.Top, rectPaneClient.Right, rectTab.Top); path.AddLine(rectPaneClient.Right, rectTab.Top, rectPaneClient.Right, rectPaneClient.Top); path.AddLine(rectPaneClient.Right, rectPaneClient.Top, rectPaneClient.Left, rectPaneClient.Top); path.AddLine(rectPaneClient.Left, rectPaneClient.Top, rectPaneClient.Left, rectTab.Top); path.AddLine(rectPaneClient.Left, rectTab.Top, rectTab.Right, rectTab.Top); } else { path.AddLine(rectTab.Right, rectTab.Bottom, rectPaneClient.Right, rectTab.Bottom); path.AddLine(rectPaneClient.Right, rectTab.Bottom, rectPaneClient.Right, rectPaneClient.Bottom); path.AddLine(rectPaneClient.Right, rectPaneClient.Bottom, rectPaneClient.Left, rectPaneClient.Bottom); path.AddLine(rectPaneClient.Left, rectPaneClient.Bottom, rectPaneClient.Left, rectTab.Bottom); path.AddLine(rectPaneClient.Left, rectTab.Bottom, rectTab.Right, rectTab.Bottom); } return path; } private GraphicsPath GetOutline_ToolWindow(int index) { Rectangle rectTab = GetTabRectangle(index); rectTab.Intersect(TabsRectangle); rectTab = RectangleToScreen(DrawHelper.RtlTransform(this, rectTab)); int y = rectTab.Top; Rectangle rectPaneClient = DockPane.RectangleToScreen(DockPane.ClientRectangle); GraphicsPath path = new GraphicsPath(); GraphicsPath pathTab = GetTabOutline(Tabs[index], true, true); path.AddPath(pathTab, true); path.AddLine(rectTab.Left, rectTab.Top, rectPaneClient.Left, rectTab.Top); path.AddLine(rectPaneClient.Left, rectTab.Top, rectPaneClient.Left, rectPaneClient.Top); path.AddLine(rectPaneClient.Left, rectPaneClient.Top, rectPaneClient.Right, rectPaneClient.Top); path.AddLine(rectPaneClient.Right, rectPaneClient.Top, rectPaneClient.Right, rectTab.Top); path.AddLine(rectPaneClient.Right, rectTab.Top, rectTab.Right, rectTab.Top); return path; } private void CalculateTabs() { if (Appearance == DockPane.AppearanceStyle.ToolWindow) CalculateTabs_ToolWindow(); else CalculateTabs_Document(); } private void CalculateTabs_ToolWindow() { if (Tabs.Count <= 1 || DockPane.IsAutoHide) return; Rectangle rectTabStrip = TabStripRectangle; // Calculate tab widths int countTabs = Tabs.Count; foreach (TabVS2005 tab in Tabs) { tab.MaxWidth = GetMaxTabWidth(Tabs.IndexOf(tab)); tab.Flag = false; } // Set tab whose max width less than average width bool anyWidthWithinAverage = true; int totalWidth = rectTabStrip.Width - ToolWindowStripGapLeft - ToolWindowStripGapRight; int totalAllocatedWidth = 0; int averageWidth = totalWidth / countTabs; int remainedTabs = countTabs; for (anyWidthWithinAverage=true; anyWidthWithinAverage && remainedTabs>0;) { anyWidthWithinAverage = false; foreach (TabVS2005 tab in Tabs) { if (tab.Flag) continue; if (tab.MaxWidth <= averageWidth) { tab.Flag = true; tab.TabWidth = tab.MaxWidth; totalAllocatedWidth += tab.TabWidth; anyWidthWithinAverage = true; remainedTabs--; } } if (remainedTabs != 0) averageWidth = (totalWidth - totalAllocatedWidth) / remainedTabs; } // If any tab width not set yet, set it to the average width if (remainedTabs > 0) { int roundUpWidth = (totalWidth - totalAllocatedWidth) - (averageWidth * remainedTabs); foreach (TabVS2005 tab in Tabs) { if (tab.Flag) continue; tab.Flag = true; if (roundUpWidth > 0) { tab.TabWidth = averageWidth + 1; roundUpWidth --; } else tab.TabWidth = averageWidth; } } // Set the X position of the tabs int x = rectTabStrip.X + ToolWindowStripGapLeft; foreach (TabVS2005 tab in Tabs) { tab.TabX = x; x += tab.TabWidth; } } private bool CalculateDocumentTab(Rectangle rectTabStrip, ref int x, int index) { bool overflow = false; TabVS2005 tab = Tabs[index] as TabVS2005; tab.MaxWidth = GetMaxTabWidth(index); int width = Math.Min(tab.MaxWidth, DocumentTabMaxWidth); if (x + width < rectTabStrip.Right || index == StartDisplayingTab) { tab.TabX = x; tab.TabWidth = width; EndDisplayingTab = index; } else { tab.TabX = 0; tab.TabWidth = 0; overflow = true; } x += width; return overflow; } /// /// Calculate which tabs are displayed and in what order. /// private void CalculateTabs_Document() { if (m_startDisplayingTab >= Tabs.Count) m_startDisplayingTab = 0; Rectangle rectTabStrip = TabsRectangle; int x = rectTabStrip.X + rectTabStrip.Height / 2; bool overflow = false; // Originally all new documents that were considered overflow // (not enough pane strip space to show all tabs) were added to // the far left (assuming not right to left) and the tabs on the // right were dropped from view. If StartDisplayingTab is not 0 // then we are dealing with making sure a specific tab is kept in focus. if (m_startDisplayingTab > 0) { int tempX = x; TabVS2005 tab = Tabs[m_startDisplayingTab] as TabVS2005; tab.MaxWidth = GetMaxTabWidth(m_startDisplayingTab); int width = Math.Min(tab.MaxWidth, DocumentTabMaxWidth); // Add the active tab and tabs to the left for (int i = StartDisplayingTab; i >= 0; i--) CalculateDocumentTab(rectTabStrip, ref tempX, i); // Store which tab is the first one displayed so that it // will be drawn correctly (without part of the tab cut off) FirstDisplayingTab = EndDisplayingTab; tempX = x; // Reset X location because we are starting over // Start with the first tab displayed - name is a little misleading. // Loop through each tab and set its location. If there is not enough // room for all of them overflow will be returned. for (int i = EndDisplayingTab; i < Tabs.Count; i++) overflow = CalculateDocumentTab(rectTabStrip, ref tempX, i); // If not all tabs are shown then we have an overflow. if (FirstDisplayingTab != 0) overflow = true; } else { for (int i = StartDisplayingTab; i < Tabs.Count; i++) overflow = CalculateDocumentTab(rectTabStrip, ref x, i); for (int i = 0; i < StartDisplayingTab; i++) overflow = CalculateDocumentTab(rectTabStrip, ref x, i); FirstDisplayingTab = StartDisplayingTab; } if (!overflow) { m_startDisplayingTab = 0; FirstDisplayingTab = 0; x = rectTabStrip.X + rectTabStrip.Height / 2; foreach (TabVS2005 tab in Tabs) { tab.TabX = x; x += tab.TabWidth; } } DocumentTabsOverflow = overflow; } protected internal override void EnsureTabVisible(IDockContent content) { if (Appearance != DockPane.AppearanceStyle.Document || !Tabs.Contains(content)) return; CalculateTabs(); EnsureDocumentTabVisible(content, true); } private bool EnsureDocumentTabVisible(IDockContent content, bool repaint) { int index = Tabs.IndexOf(content); TabVS2005 tab = Tabs[index] as TabVS2005; if (tab.TabWidth != 0) return false; StartDisplayingTab = index; if (repaint) Invalidate(); return true; } private int GetMaxTabWidth(int index) { if (Appearance == DockPane.AppearanceStyle.ToolWindow) return GetMaxTabWidth_ToolWindow(index); else return GetMaxTabWidth_Document(index); } private int GetMaxTabWidth_ToolWindow(int index) { IDockContent content = Tabs[index].Content; Size sizeString = TextRenderer.MeasureText(content.DockHandler.TabText, TextFont); return ToolWindowImageWidth + sizeString.Width + ToolWindowImageGapLeft + ToolWindowImageGapRight + ToolWindowTextGapRight; } private int GetMaxTabWidth_Document(int index) { IDockContent content = Tabs[index].Content; int height = GetTabRectangle_Document(index).Height; Size sizeText = TextRenderer.MeasureText(content.DockHandler.TabText, BoldFont, new Size(DocumentTabMaxWidth, height), DocumentTextFormat); if (DockPane.DockPanel.ShowDocumentIcon) return sizeText.Width + DocumentIconWidth + DocumentIconGapLeft + DocumentIconGapRight + DocumentTextGapRight; else return sizeText.Width + DocumentIconGapLeft + DocumentTextGapRight; } private void DrawTabStrip(Graphics g) { if (Appearance == DockPane.AppearanceStyle.Document) DrawTabStrip_Document(g); else DrawTabStrip_ToolWindow(g); } private void DrawTabStrip_Document(Graphics g) { int count = Tabs.Count; if (count == 0) return; Rectangle rectTabStrip = TabStripRectangle; // Draw the tabs Rectangle rectTabOnly = TabsRectangle; Rectangle rectTab = Rectangle.Empty; TabVS2005 tabActive = null; g.SetClip(DrawHelper.RtlTransform(this, rectTabOnly)); for (int i=0; i