1 using System;
2 using System.Drawing;
3 using System.Drawing.Drawing2D;
4 using System.Windows.Forms;
5 using System.ComponentModel;
6 using WeifenLuo.WinFormsUI.Docking;
7 using WeifenLuo.WinFormsUI.ThemeVS2012;
8 
9 namespace WeifenLuo.WinFormsUI.ThemeVS2013
10 {
11     internal class VS2013DockPaneStrip : DockPaneStripBase
12     {
13         private class TabVS2013 : Tab
14         {
TabVS2013(IDockContent content)15             public TabVS2013(IDockContent content)
16                 : base(content)
17             {
18             }
19 
20             private int m_tabX;
21             public int TabX
22             {
23                 get { return m_tabX; }
24                 set { m_tabX = value; }
25             }
26 
27             private int m_tabWidth;
28             public int TabWidth
29             {
30                 get { return m_tabWidth; }
31                 set { m_tabWidth = value; }
32             }
33 
34             private int m_maxWidth;
35             public int MaxWidth
36             {
37                 get { return m_maxWidth; }
38                 set { m_maxWidth = value; }
39             }
40 
41             private bool m_flag;
42             protected internal bool Flag
43             {
44                 get { return m_flag; }
45                 set { m_flag = value; }
46             }
47         }
48 
CreateTab(IDockContent content)49         protected override Tab CreateTab(IDockContent content)
50         {
51             return new TabVS2013(content);
52         }
53 
54         private sealed class InertButton : InertButtonBase
55         {
56             private Bitmap _hovered, _normal, _pressed;
57 
InertButton(Bitmap hovered, Bitmap normal, Bitmap pressed)58             public InertButton(Bitmap hovered, Bitmap normal, Bitmap pressed)
59                 : base()
60             {
61                 _hovered = hovered;
62                 _normal = normal;
63                 _pressed = pressed;
64             }
65 
66             public override Bitmap Image
67             {
68                 get { return _normal; }
69             }
70 
71             public override Bitmap HoverImage
72             {
73                 get { return _hovered; }
74             }
75 
76             public override Bitmap PressImage
77             {
78                 get { return _pressed; }
79             }
80         }
81 
82         #region Constants
83 
84         private const int _ToolWindowStripGapTop = 0;
85         private const int _ToolWindowStripGapBottom = 0;
86         private const int _ToolWindowStripGapLeft = 0;
87         private const int _ToolWindowStripGapRight = 0;
88         private const int _ToolWindowImageHeight = 16;
89         private const int _ToolWindowImageWidth = 0;//16;
90         private const int _ToolWindowImageGapTop = 3;
91         private const int _ToolWindowImageGapBottom = 1;
92         private const int _ToolWindowImageGapLeft = 2;
93         private const int _ToolWindowImageGapRight = 0;
94         private const int _ToolWindowTextGapRight = 3;
95         private const int _ToolWindowTabSeperatorGapTop = 3;
96         private const int _ToolWindowTabSeperatorGapBottom = 3;
97 
98         private const int _DocumentStripGapTop = 0;
99         private const int _DocumentStripGapBottom = 1;
100         private const int _DocumentTabMaxWidth = 200;
101         private const int _DocumentButtonGapTop = 3;
102         private const int _DocumentButtonGapBottom = 3;
103         private const int _DocumentButtonGapBetween = 0;
104         private const int _DocumentButtonGapRight = 3;
105         private const int _DocumentTabGapTop = 0;//3;
106         private const int _DocumentTabGapLeft = 0;//3;
107         private const int _DocumentTabGapRight = 0;//3;
108         private const int _DocumentIconGapBottom = 2;//2;
109         private const int _DocumentIconGapLeft = 8;
110         private const int _DocumentIconGapRight = 0;
111         private const int _DocumentIconHeight = 16;
112         private const int _DocumentIconWidth = 16;
113         private const int _DocumentTextGapRight = 6;
114 
115         #endregion
116 
117         #region Members
118 
119         private ContextMenuStrip m_selectMenu;
120         private InertButton m_buttonOverflow;
121         private InertButton m_buttonWindowList;
122         private IContainer m_components;
123         private ToolTip m_toolTip;
124         private Font m_font;
125         private Font m_boldFont;
126         private int m_startDisplayingTab = 0;
127         private int m_endDisplayingTab = 0;
128         private int m_firstDisplayingTab = 0;
129         private bool m_documentTabsOverflow = false;
130         private static string m_toolTipSelect;
131         private Rectangle _activeClose;
132         private int _selectMenuMargin = 5;
133         private bool m_suspendDrag = false;
134         #endregion
135 
136         #region Properties
137 
138         private Rectangle TabStripRectangle
139         {
140             get
141             {
142                 if (Appearance == DockPane.AppearanceStyle.Document)
143                     return TabStripRectangle_Document;
144                 else
145                     return TabStripRectangle_ToolWindow;
146             }
147         }
148 
149         private Rectangle TabStripRectangle_ToolWindow
150         {
151             get
152             {
153                 Rectangle rect = ClientRectangle;
154                 return new Rectangle(rect.X, rect.Top + ToolWindowStripGapTop, rect.Width, rect.Height - ToolWindowStripGapTop - ToolWindowStripGapBottom);
155             }
156         }
157 
158         private Rectangle TabStripRectangle_Document
159         {
160             get
161             {
162                 Rectangle rect = ClientRectangle;
163                 return new Rectangle(rect.X, rect.Top + DocumentStripGapTop, rect.Width, rect.Height + DocumentStripGapTop - DocumentStripGapBottom);
164             }
165         }
166 
167         private Rectangle TabsRectangle
168         {
169             get
170             {
171                 if (Appearance == DockPane.AppearanceStyle.ToolWindow)
172                     return TabStripRectangle;
173 
174                 Rectangle rectWindow = TabStripRectangle;
175                 int x = rectWindow.X;
176                 int y = rectWindow.Y;
177                 int width = rectWindow.Width;
178                 int height = rectWindow.Height;
179 
180                 x += DocumentTabGapLeft;
181                 width -= DocumentTabGapLeft +
182                     DocumentTabGapRight +
183                     DocumentButtonGapRight +
184                     ButtonOverflow.Width +
185                     ButtonWindowList.Width +
186                     2 * DocumentButtonGapBetween;
187 
188                 return new Rectangle(x, y, width, height);
189             }
190         }
191 
192         private ContextMenuStrip SelectMenu
193         {
194             get { return m_selectMenu; }
195         }
196 
197         public int SelectMenuMargin
198         {
199             get { return _selectMenuMargin; }
200             set { _selectMenuMargin = value; }
201         }
202 
203         private InertButton ButtonOverflow
204         {
205             get
206             {
207                 if (m_buttonOverflow == null)
208                 {
209                     m_buttonOverflow = new InertButton(
210                         DockPane.DockPanel.Theme.ImageService.DockPaneHover_OptionOverflow,
211                         DockPane.DockPanel.Theme.ImageService.DockPane_OptionOverflow,
212                         DockPane.DockPanel.Theme.ImageService.DockPanePress_OptionOverflow);
213                     m_buttonOverflow.Click += new EventHandler(WindowList_Click);
214                     Controls.Add(m_buttonOverflow);
215                 }
216 
217                 return m_buttonOverflow;
218             }
219         }
220 
221         private InertButton ButtonWindowList
222         {
223             get
224             {
225                 if (m_buttonWindowList == null)
226                 {
227                     m_buttonWindowList = new InertButton(
228                         DockPane.DockPanel.Theme.ImageService.DockPaneHover_List,
229                         DockPane.DockPanel.Theme.ImageService.DockPane_List,
230                         DockPane.DockPanel.Theme.ImageService.DockPanePress_List);
231                     m_buttonWindowList.Click += new EventHandler(WindowList_Click);
232                     Controls.Add(m_buttonWindowList);
233                 }
234 
235                 return m_buttonWindowList;
236             }
237         }
238 
239         private static GraphicsPath GraphicsPath
240         {
241             get { return VS2012AutoHideStrip.GraphicsPath; }
242         }
243 
244         private IContainer Components
245         {
246             get { return m_components; }
247         }
248 
249         public Font TextFont
250         {
251             get { return DockPane.DockPanel.Theme.Skin.DockPaneStripSkin.TextFont; }
252         }
253 
254         private Font BoldFont
255         {
256             get
257             {
258                 if (IsDisposed)
259                     return null;
260 
261                 if (m_boldFont == null)
262                 {
263                     m_font = TextFont;
264                     m_boldFont = new Font(TextFont, FontStyle.Bold);
265                 }
266                 else if (m_font != TextFont)
267                 {
268                     m_boldFont.Dispose();
269                     m_font = TextFont;
270                     m_boldFont = new Font(TextFont, FontStyle.Bold);
271                 }
272 
273                 return m_boldFont;
274             }
275         }
276 
277         private int StartDisplayingTab
278         {
279             get { return m_startDisplayingTab; }
280             set
281             {
282                 m_startDisplayingTab = value;
283                 Invalidate();
284             }
285         }
286 
287         private int EndDisplayingTab
288         {
289             get { return m_endDisplayingTab; }
290             set { m_endDisplayingTab = value; }
291         }
292 
293         private int FirstDisplayingTab
294         {
295             get { return m_firstDisplayingTab; }
296             set { m_firstDisplayingTab = value; }
297         }
298 
299         private bool DocumentTabsOverflow
300         {
301             set
302             {
303                 if (m_documentTabsOverflow == value)
304                     return;
305 
306                 m_documentTabsOverflow = value;
307                 SetInertButtons();
308             }
309         }
310 
311         #region Customizable Properties
312 
313         private static int ToolWindowStripGapTop
314         {
315             get { return _ToolWindowStripGapTop; }
316         }
317 
318         private static int ToolWindowStripGapBottom
319         {
320             get { return _ToolWindowStripGapBottom; }
321         }
322 
323         private static int ToolWindowStripGapLeft
324         {
325             get { return _ToolWindowStripGapLeft; }
326         }
327 
328         private static int ToolWindowStripGapRight
329         {
330             get { return _ToolWindowStripGapRight; }
331         }
332 
333         private static int ToolWindowImageHeight
334         {
335             get { return _ToolWindowImageHeight; }
336         }
337 
338         private static int ToolWindowImageWidth
339         {
340             get { return _ToolWindowImageWidth; }
341         }
342 
343         private static int ToolWindowImageGapTop
344         {
345             get { return _ToolWindowImageGapTop; }
346         }
347 
348         private static int ToolWindowImageGapBottom
349         {
350             get { return _ToolWindowImageGapBottom; }
351         }
352 
353         private static int ToolWindowImageGapLeft
354         {
355             get { return _ToolWindowImageGapLeft; }
356         }
357 
358         private static int ToolWindowImageGapRight
359         {
360             get { return _ToolWindowImageGapRight; }
361         }
362 
363         private static int ToolWindowTextGapRight
364         {
365             get { return _ToolWindowTextGapRight; }
366         }
367 
368         private static int ToolWindowTabSeperatorGapTop
369         {
370             get { return _ToolWindowTabSeperatorGapTop; }
371         }
372 
373         private static int ToolWindowTabSeperatorGapBottom
374         {
375             get { return _ToolWindowTabSeperatorGapBottom; }
376         }
377 
378         private static string ToolTipSelect
379         {
380             get
381             {
382                 if (m_toolTipSelect == null)
383                     m_toolTipSelect = Strings.DockPaneStrip_ToolTipWindowList;
384                 return m_toolTipSelect;
385             }
386         }
387 
388         private TextFormatFlags ToolWindowTextFormat
389         {
390             get
391             {
392                 TextFormatFlags textFormat = TextFormatFlags.EndEllipsis |
393                     TextFormatFlags.HorizontalCenter |
394                     TextFormatFlags.SingleLine |
395                     TextFormatFlags.VerticalCenter;
396                 if (RightToLeft == RightToLeft.Yes)
397                     return textFormat | TextFormatFlags.RightToLeft | TextFormatFlags.Right;
398                 else
399                     return textFormat;
400             }
401         }
402 
403         private static int DocumentStripGapTop
404         {
405             get { return _DocumentStripGapTop; }
406         }
407 
408         private static int DocumentStripGapBottom
409         {
410             get { return _DocumentStripGapBottom; }
411         }
412 
413         private TextFormatFlags DocumentTextFormat
414         {
415             get
416             {
417                 TextFormatFlags textFormat = TextFormatFlags.EndEllipsis |
418                     TextFormatFlags.SingleLine |
419                     TextFormatFlags.VerticalCenter |
420                     TextFormatFlags.HorizontalCenter;
421                 if (RightToLeft == RightToLeft.Yes)
422                     return textFormat | TextFormatFlags.RightToLeft;
423                 else
424                     return textFormat;
425             }
426         }
427 
428         private static int DocumentTabMaxWidth
429         {
430             get { return _DocumentTabMaxWidth; }
431         }
432 
433         private static int DocumentButtonGapTop
434         {
435             get { return _DocumentButtonGapTop; }
436         }
437 
438         private static int DocumentButtonGapBottom
439         {
440             get { return _DocumentButtonGapBottom; }
441         }
442 
443         private static int DocumentButtonGapBetween
444         {
445             get { return _DocumentButtonGapBetween; }
446         }
447 
448         private static int DocumentButtonGapRight
449         {
450             get { return _DocumentButtonGapRight; }
451         }
452 
453         private static int DocumentTabGapTop
454         {
455             get { return _DocumentTabGapTop; }
456         }
457 
458         private static int DocumentTabGapLeft
459         {
460             get { return _DocumentTabGapLeft; }
461         }
462 
463         private static int DocumentTabGapRight
464         {
465             get { return _DocumentTabGapRight; }
466         }
467 
468         private static int DocumentIconGapBottom
469         {
470             get { return _DocumentIconGapBottom; }
471         }
472 
473         private static int DocumentIconGapLeft
474         {
475             get { return _DocumentIconGapLeft; }
476         }
477 
478         private static int DocumentIconGapRight
479         {
480             get { return _DocumentIconGapRight; }
481         }
482 
483         private static int DocumentIconWidth
484         {
485             get { return _DocumentIconWidth; }
486         }
487 
488         private static int DocumentIconHeight
489         {
490             get { return _DocumentIconHeight; }
491         }
492 
493         private static int DocumentTextGapRight
494         {
495             get { return _DocumentTextGapRight; }
496         }
497 
498         #endregion
499 
500         #endregion
501 
VS2013DockPaneStrip(DockPane pane)502         public VS2013DockPaneStrip(DockPane pane)
503             : base(pane)
504         {
505             SetStyle(ControlStyles.ResizeRedraw |
506                 ControlStyles.UserPaint |
507                 ControlStyles.AllPaintingInWmPaint |
508                 ControlStyles.OptimizedDoubleBuffer, true);
509 
510             SuspendLayout();
511 
512             m_components = new Container();
513             m_toolTip = new ToolTip(Components);
514             m_selectMenu = new ContextMenuStrip(Components);
515             pane.DockPanel.Theme.ApplyTo(m_selectMenu);
516 
517             ResumeLayout();
518         }
519 
Dispose(bool disposing)520         protected override void Dispose(bool disposing)
521         {
522             if (disposing)
523             {
524                 Components.Dispose();
525                 if (m_boldFont != null)
526                 {
527                     m_boldFont.Dispose();
528                     m_boldFont = null;
529                 }
530             }
531             base.Dispose(disposing);
532         }
533 
MeasureHeight()534         protected override int MeasureHeight()
535         {
536             if (Appearance == DockPane.AppearanceStyle.ToolWindow)
537                 return MeasureHeight_ToolWindow();
538             else
539                 return MeasureHeight_Document();
540         }
541 
MeasureHeight_ToolWindow()542         private int MeasureHeight_ToolWindow()
543         {
544             if (DockPane.IsAutoHide || Tabs.Count <= 1)
545                 return 0;
546 
547             int height = Math.Max(TextFont.Height + (PatchController.EnableHighDpi == true ? DocumentIconGapBottom : 0),
548                 ToolWindowImageHeight + ToolWindowImageGapTop + ToolWindowImageGapBottom)
549                 + ToolWindowStripGapTop + ToolWindowStripGapBottom;
550 
551             return height;
552         }
553 
MeasureHeight_Document()554         private int MeasureHeight_Document()
555         {
556             int height = Math.Max(TextFont.Height + DocumentTabGapTop + (PatchController.EnableHighDpi == true ? DocumentIconGapBottom : 0),
557                 ButtonOverflow.Height + DocumentButtonGapTop + DocumentButtonGapBottom)
558                 + DocumentStripGapBottom + DocumentStripGapTop;
559 
560             return height;
561         }
562 
OnPaint(PaintEventArgs e)563         protected override void OnPaint(PaintEventArgs e)
564         {
565             base.OnPaint(e);
566             CalculateTabs();
567             if (Appearance == DockPane.AppearanceStyle.Document && DockPane.ActiveContent != null)
568             {
569                 if (EnsureDocumentTabVisible(DockPane.ActiveContent, false))
570                     CalculateTabs();
571             }
572 
573             DrawTabStrip(e.Graphics);
574         }
575 
OnRefreshChanges()576         protected override void OnRefreshChanges()
577         {
578             SetInertButtons();
579             Invalidate();
580         }
581 
GetOutline(int index)582         public override GraphicsPath GetOutline(int index)
583         {
584             if (Appearance == DockPane.AppearanceStyle.Document)
585                 return GetOutline_Document(index);
586             else
587                 return GetOutline_ToolWindow(index);
588         }
589 
GetOutline_Document(int index)590         private GraphicsPath GetOutline_Document(int index)
591         {
592             Rectangle rectTab = Tabs[index].Rectangle.Value;
593             rectTab.X -= rectTab.Height / 2;
594             rectTab.Intersect(TabsRectangle);
595             rectTab = RectangleToScreen(DrawHelper.RtlTransform(this, rectTab));
596             Rectangle rectPaneClient = DockPane.RectangleToScreen(DockPane.ClientRectangle);
597 
598             GraphicsPath path = new GraphicsPath();
599             GraphicsPath pathTab = GetTabOutline_Document(Tabs[index], true, true, true);
600             path.AddPath(pathTab, true);
601 
602             if (DockPane.DockPanel.DocumentTabStripLocation == DocumentTabStripLocation.Bottom)
603             {
604                 path.AddLine(rectTab.Right, rectTab.Top, rectPaneClient.Right, rectTab.Top);
605                 path.AddLine(rectPaneClient.Right, rectTab.Top, rectPaneClient.Right, rectPaneClient.Top);
606                 path.AddLine(rectPaneClient.Right, rectPaneClient.Top, rectPaneClient.Left, rectPaneClient.Top);
607                 path.AddLine(rectPaneClient.Left, rectPaneClient.Top, rectPaneClient.Left, rectTab.Top);
608                 path.AddLine(rectPaneClient.Left, rectTab.Top, rectTab.Right, rectTab.Top);
609             }
610             else
611             {
612                 path.AddLine(rectTab.Right, rectTab.Bottom, rectPaneClient.Right, rectTab.Bottom);
613                 path.AddLine(rectPaneClient.Right, rectTab.Bottom, rectPaneClient.Right, rectPaneClient.Bottom);
614                 path.AddLine(rectPaneClient.Right, rectPaneClient.Bottom, rectPaneClient.Left, rectPaneClient.Bottom);
615                 path.AddLine(rectPaneClient.Left, rectPaneClient.Bottom, rectPaneClient.Left, rectTab.Bottom);
616                 path.AddLine(rectPaneClient.Left, rectTab.Bottom, rectTab.Right, rectTab.Bottom);
617             }
618             return path;
619         }
620 
GetOutline_ToolWindow(int index)621         private GraphicsPath GetOutline_ToolWindow(int index)
622         {
623             Rectangle rectTab = Tabs[index].Rectangle.Value;
624             rectTab.Intersect(TabsRectangle);
625             rectTab = RectangleToScreen(DrawHelper.RtlTransform(this, rectTab));
626             Rectangle rectPaneClient = DockPane.RectangleToScreen(DockPane.ClientRectangle);
627 
628             GraphicsPath path = new GraphicsPath();
629             GraphicsPath pathTab = GetTabOutline(Tabs[index], true, true);
630             path.AddPath(pathTab, true);
631             path.AddLine(rectTab.Left, rectTab.Top, rectPaneClient.Left, rectTab.Top);
632             path.AddLine(rectPaneClient.Left, rectTab.Top, rectPaneClient.Left, rectPaneClient.Top);
633             path.AddLine(rectPaneClient.Left, rectPaneClient.Top, rectPaneClient.Right, rectPaneClient.Top);
634             path.AddLine(rectPaneClient.Right, rectPaneClient.Top, rectPaneClient.Right, rectTab.Top);
635             path.AddLine(rectPaneClient.Right, rectTab.Top, rectTab.Right, rectTab.Top);
636             return path;
637         }
638 
CalculateTabs()639         private void CalculateTabs()
640         {
641             if (Appearance == DockPane.AppearanceStyle.ToolWindow)
642                 CalculateTabs_ToolWindow();
643             else
644                 CalculateTabs_Document();
645         }
646 
CalculateTabs_ToolWindow()647         private void CalculateTabs_ToolWindow()
648         {
649             if (Tabs.Count <= 1 || DockPane.IsAutoHide)
650                 return;
651 
652             Rectangle rectTabStrip = TabStripRectangle;
653 
654             // Calculate tab widths
655             int countTabs = Tabs.Count;
656             foreach (TabVS2013 tab in Tabs)
657             {
658                 tab.MaxWidth = GetMaxTabWidth(Tabs.IndexOf(tab));
659                 tab.Flag = false;
660             }
661 
662             // Set tab whose max width less than average width
663             bool anyWidthWithinAverage = true;
664             int totalWidth = rectTabStrip.Width - ToolWindowStripGapLeft - ToolWindowStripGapRight;
665             int totalAllocatedWidth = 0;
666             int averageWidth = totalWidth / countTabs;
667             int remainedTabs = countTabs;
668             for (anyWidthWithinAverage = true; anyWidthWithinAverage && remainedTabs > 0; )
669             {
670                 anyWidthWithinAverage = false;
671                 foreach (TabVS2013 tab in Tabs)
672                 {
673                     if (tab.Flag)
674                         continue;
675 
676                     if (tab.MaxWidth <= averageWidth)
677                     {
678                         tab.Flag = true;
679                         tab.TabWidth = tab.MaxWidth;
680                         totalAllocatedWidth += tab.TabWidth;
681                         anyWidthWithinAverage = true;
682                         remainedTabs--;
683                     }
684                 }
685                 if (remainedTabs != 0)
686                     averageWidth = (totalWidth - totalAllocatedWidth) / remainedTabs;
687             }
688 
689             // If any tab width not set yet, set it to the average width
690             if (remainedTabs > 0)
691             {
692                 int roundUpWidth = (totalWidth - totalAllocatedWidth) - (averageWidth * remainedTabs);
693                 foreach (TabVS2013 tab in Tabs)
694                 {
695                     if (tab.Flag)
696                         continue;
697 
698                     tab.Flag = true;
699                     if (roundUpWidth > 0)
700                     {
701                         tab.TabWidth = averageWidth + 1;
702                         roundUpWidth--;
703                     }
704                     else
705                         tab.TabWidth = averageWidth;
706                 }
707             }
708 
709             // Set the X position of the tabs
710             int x = rectTabStrip.X + ToolWindowStripGapLeft;
711             foreach (TabVS2013 tab in Tabs)
712             {
713                 tab.TabX = x;
714                 x += tab.TabWidth;
715             }
716         }
717 
CalculateDocumentTab(Rectangle rectTabStrip, ref int x, int index)718         private bool CalculateDocumentTab(Rectangle rectTabStrip, ref int x, int index)
719         {
720             bool overflow = false;
721 
722             var tab = Tabs[index] as TabVS2013;
723             tab.MaxWidth = GetMaxTabWidth(index);
724             int width = Math.Min(tab.MaxWidth, DocumentTabMaxWidth);
725             if (x + width < rectTabStrip.Right || index == StartDisplayingTab)
726             {
727                 tab.TabX = x;
728                 tab.TabWidth = width;
729                 EndDisplayingTab = index;
730             }
731             else
732             {
733                 tab.TabX = 0;
734                 tab.TabWidth = 0;
735                 overflow = true;
736             }
737             x += width;
738 
739             return overflow;
740         }
741 
742         /// <summary>
743         /// Calculate which tabs are displayed and in what order.
744         /// </summary>
CalculateTabs_Document()745         private void CalculateTabs_Document()
746         {
747             if (m_startDisplayingTab >= Tabs.Count)
748                 m_startDisplayingTab = 0;
749 
750             Rectangle rectTabStrip = TabsRectangle;
751 
752             int x = rectTabStrip.X; //+ rectTabStrip.Height / 2;
753             bool overflow = false;
754 
755             // Originally all new documents that were considered overflow
756             // (not enough pane strip space to show all tabs) were added to
757             // the far left (assuming not right to left) and the tabs on the
758             // right were dropped from view. If StartDisplayingTab is not 0
759             // then we are dealing with making sure a specific tab is kept in focus.
760             if (m_startDisplayingTab > 0)
761             {
762                 int tempX = x;
763                 var tab = Tabs[m_startDisplayingTab] as TabVS2013;
764                 tab.MaxWidth = GetMaxTabWidth(m_startDisplayingTab);
765 
766                 // Add the active tab and tabs to the left
767                 for (int i = StartDisplayingTab; i >= 0; i--)
768                     CalculateDocumentTab(rectTabStrip, ref tempX, i);
769 
770                 // Store which tab is the first one displayed so that it
771                 // will be drawn correctly (without part of the tab cut off)
772                 FirstDisplayingTab = EndDisplayingTab;
773 
774                 tempX = x; // Reset X location because we are starting over
775 
776                 // Start with the first tab displayed - name is a little misleading.
777                 // Loop through each tab and set its location. If there is not enough
778                 // room for all of them overflow will be returned.
779                 for (int i = EndDisplayingTab; i < Tabs.Count; i++)
780                     overflow = CalculateDocumentTab(rectTabStrip, ref tempX, i);
781 
782                 // If not all tabs are shown then we have an overflow.
783                 if (FirstDisplayingTab != 0)
784                     overflow = true;
785             }
786             else
787             {
788                 for (int i = StartDisplayingTab; i < Tabs.Count; i++)
789                     overflow = CalculateDocumentTab(rectTabStrip, ref x, i);
790                 for (int i = 0; i < StartDisplayingTab; i++)
791                     overflow = CalculateDocumentTab(rectTabStrip, ref x, i);
792 
793                 FirstDisplayingTab = StartDisplayingTab;
794             }
795 
796             if (!overflow)
797             {
798                 m_startDisplayingTab = 0;
799                 FirstDisplayingTab = 0;
800                 x = rectTabStrip.X;
801                 foreach (TabVS2013 tab in Tabs)
802                 {
803                     tab.TabX = x;
804                     x += tab.TabWidth;
805                 }
806             }
807 
808             DocumentTabsOverflow = overflow;
809         }
810 
EnsureTabVisible(IDockContent content)811         protected override void EnsureTabVisible(IDockContent content)
812         {
813             if (Appearance != DockPane.AppearanceStyle.Document || !Tabs.Contains(content))
814                 return;
815 
816             CalculateTabs();
817             EnsureDocumentTabVisible(content, true);
818         }
819 
EnsureDocumentTabVisible(IDockContent content, bool repaint)820         private bool EnsureDocumentTabVisible(IDockContent content, bool repaint)
821         {
822             int index = Tabs.IndexOf(content);
823             if (index == -1) // TODO: should prevent it from being -1;
824                 return false;
825 
826             var tab = Tabs[index] as TabVS2013;
827             if (tab.TabWidth != 0)
828                 return false;
829 
830             StartDisplayingTab = index;
831             if (repaint)
832                 Invalidate();
833 
834             return true;
835         }
836 
GetMaxTabWidth(int index)837         private int GetMaxTabWidth(int index)
838         {
839             if (Appearance == DockPane.AppearanceStyle.ToolWindow)
840                 return GetMaxTabWidth_ToolWindow(index);
841             else
842                 return GetMaxTabWidth_Document(index);
843         }
844 
GetMaxTabWidth_ToolWindow(int index)845         private int GetMaxTabWidth_ToolWindow(int index)
846         {
847             IDockContent content = Tabs[index].Content;
848             Size sizeString = TextRenderer.MeasureText(content.DockHandler.TabText, TextFont);
849             return ToolWindowImageWidth + sizeString.Width + ToolWindowImageGapLeft
850                 + ToolWindowImageGapRight + ToolWindowTextGapRight;
851         }
852 
853         private const int TAB_CLOSE_BUTTON_WIDTH = 30;
854 
GetMaxTabWidth_Document(int index)855         private int GetMaxTabWidth_Document(int index)
856         {
857             IDockContent content = Tabs[index].Content;
858             int height = GetTabRectangle_Document(index).Height;
859             Size sizeText = TextRenderer.MeasureText(content.DockHandler.TabText, BoldFont, new Size(DocumentTabMaxWidth, height), DocumentTextFormat);
860 
861             int width;
862             if (DockPane.DockPanel.ShowDocumentIcon)
863                 width = sizeText.Width + DocumentIconWidth + DocumentIconGapLeft + DocumentIconGapRight + DocumentTextGapRight;
864             else
865                 width = sizeText.Width + DocumentIconGapLeft + DocumentTextGapRight;
866 
867             width += TAB_CLOSE_BUTTON_WIDTH;
868             return width;
869         }
870 
DrawTabStrip(Graphics g)871         private void DrawTabStrip(Graphics g)
872         {
873             // IMPORTANT: fill background.
874             Rectangle rectTabStrip = TabStripRectangle;
875             g.FillRectangle(DockPane.DockPanel.Theme.PaintingService.GetBrush(DockPane.DockPanel.Theme.ColorPalette.MainWindowActive.Background), rectTabStrip);
876 
877             if (Appearance == DockPane.AppearanceStyle.Document)
878                 DrawTabStrip_Document(g);
879             else
880                 DrawTabStrip_ToolWindow(g);
881         }
882 
DrawTabStrip_Document(Graphics g)883         private void DrawTabStrip_Document(Graphics g)
884         {
885             int count = Tabs.Count;
886             if (count == 0)
887                 return;
888 
889             Rectangle rectTabStrip = new Rectangle(TabStripRectangle.Location, TabStripRectangle.Size);
890             rectTabStrip.Height += 1;
891 
892             // Draw the tabs
893             Rectangle rectTabOnly = TabsRectangle;
894             Rectangle rectTab = Rectangle.Empty;
895             TabVS2013 tabActive = null;
896             g.SetClip(DrawHelper.RtlTransform(this, rectTabOnly));
897             for (int i = 0; i < count; i++)
898             {
899                 rectTab = GetTabRectangle(i);
900                 if (Tabs[i].Content == DockPane.ActiveContent)
901                 {
902                     tabActive = Tabs[i] as TabVS2013;
903                     tabActive.Rectangle = rectTab;
904                     continue;
905                 }
906 
907                 if (rectTab.IntersectsWith(rectTabOnly))
908                 {
909                     var tab = Tabs[i] as TabVS2013;
910                     tab.Rectangle = rectTab;
911                     DrawTab(g, tab);
912                 }
913             }
914 
915             g.SetClip(rectTabStrip);
916 
917             if (DockPane.DockPanel.DocumentTabStripLocation == DocumentTabStripLocation.Bottom)
918             {
919             }
920             else
921             {
922                 Color tabUnderLineColor;
923                 if (tabActive != null && DockPane.IsActiveDocumentPane)
924                     tabUnderLineColor = DockPane.DockPanel.Theme.ColorPalette.TabSelectedActive.Background;
925                 else
926                     tabUnderLineColor = DockPane.DockPanel.Theme.ColorPalette.TabSelectedInactive.Background;
927 
928                 g.DrawLine(DockPane.DockPanel.Theme.PaintingService.GetPen(tabUnderLineColor, 4), rectTabStrip.Left, rectTabStrip.Bottom, rectTabStrip.Right, rectTabStrip.Bottom);
929             }
930 
931             g.SetClip(DrawHelper.RtlTransform(this, rectTabOnly));
932             if (tabActive != null)
933             {
934                 rectTab = tabActive.Rectangle.Value;
935                 if (rectTab.IntersectsWith(rectTabOnly))
936                 {
937                     rectTab.Intersect(rectTabOnly);
938                     tabActive.Rectangle = rectTab;
939                     DrawTab(g, tabActive);
940                 }
941             }
942         }
943 
DrawTabStrip_ToolWindow(Graphics g)944         private void DrawTabStrip_ToolWindow(Graphics g)
945         {
946             var rect = TabStripRectangle_ToolWindow;
947             Color borderColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowBorder;
948 
949             g.DrawLine(DockPane.DockPanel.Theme.PaintingService.GetPen(borderColor), rect.Left, rect.Top,
950                 rect.Right, rect.Top);
951 
952             for (int i = 0; i < Tabs.Count; i++)
953             {
954                 var tab = Tabs[i] as TabVS2013;
955                 tab.Rectangle = GetTabRectangle(i);
956                 DrawTab(g, tab);
957             }
958         }
959 
GetTabRectangle(int index)960         private Rectangle GetTabRectangle(int index)
961         {
962             if (Appearance == DockPane.AppearanceStyle.ToolWindow)
963                 return GetTabRectangle_ToolWindow(index);
964             else
965                 return GetTabRectangle_Document(index);
966         }
967 
GetTabRectangle_ToolWindow(int index)968         private Rectangle GetTabRectangle_ToolWindow(int index)
969         {
970             Rectangle rectTabStrip = TabStripRectangle;
971 
972             TabVS2013 tab = (TabVS2013)Tabs[index];
973             return new Rectangle(tab.TabX, rectTabStrip.Y, tab.TabWidth, rectTabStrip.Height);
974         }
975 
GetTabRectangle_Document(int index)976         private Rectangle GetTabRectangle_Document(int index)
977         {
978             Rectangle rectTabStrip = TabStripRectangle;
979             var tab = (TabVS2013)Tabs[index];
980 
981             Rectangle rect = new Rectangle();
982             rect.X = tab.TabX;
983             rect.Width = tab.TabWidth;
984             rect.Height = rectTabStrip.Height - DocumentTabGapTop;
985 
986             if (DockPane.DockPanel.DocumentTabStripLocation == DocumentTabStripLocation.Bottom)
987                 rect.Y = rectTabStrip.Y + DocumentStripGapBottom;
988             else
989                 rect.Y = rectTabStrip.Y + DocumentTabGapTop;
990 
991             return rect;
992         }
993 
DrawTab(Graphics g, TabVS2013 tab)994         private void DrawTab(Graphics g, TabVS2013 tab)
995         {
996             if (Appearance == DockPane.AppearanceStyle.ToolWindow)
997                 DrawTab_ToolWindow(g, tab);
998             else
999                 DrawTab_Document(g, tab);
1000         }
1001 
GetTabOutline(Tab tab, bool rtlTransform, bool toScreen)1002         private GraphicsPath GetTabOutline(Tab tab, bool rtlTransform, bool toScreen)
1003         {
1004             if (Appearance == DockPane.AppearanceStyle.ToolWindow)
1005                 return GetTabOutline_ToolWindow(tab, rtlTransform, toScreen);
1006             else
1007                 return GetTabOutline_Document(tab, rtlTransform, toScreen, false);
1008         }
1009 
GetTabOutline_ToolWindow(Tab tab, bool rtlTransform, bool toScreen)1010         private GraphicsPath GetTabOutline_ToolWindow(Tab tab, bool rtlTransform, bool toScreen)
1011         {
1012             Rectangle rect = GetTabRectangle(Tabs.IndexOf(tab));
1013             if (rtlTransform)
1014                 rect = DrawHelper.RtlTransform(this, rect);
1015             if (toScreen)
1016                 rect = RectangleToScreen(rect);
1017 
1018             DrawHelper.GetRoundedCornerTab(GraphicsPath, rect, false);
1019             return GraphicsPath;
1020         }
1021 
GetTabOutline_Document(Tab tab, bool rtlTransform, bool toScreen, bool full)1022         private GraphicsPath GetTabOutline_Document(Tab tab, bool rtlTransform, bool toScreen, bool full)
1023         {
1024             GraphicsPath.Reset();
1025             Rectangle rect = GetTabRectangle(Tabs.IndexOf(tab));
1026 
1027             // Shorten TabOutline so it doesn't get overdrawn by icons next to it
1028             rect.Intersect(TabsRectangle);
1029             rect.Width--;
1030 
1031             if (rtlTransform)
1032                 rect = DrawHelper.RtlTransform(this, rect);
1033             if (toScreen)
1034                 rect = RectangleToScreen(rect);
1035 
1036             GraphicsPath.AddRectangle(rect);
1037             return GraphicsPath;
1038         }
1039 
DrawTab_ToolWindow(Graphics g, TabVS2013 tab)1040         private void DrawTab_ToolWindow(Graphics g, TabVS2013 tab)
1041         {
1042             var rect = tab.Rectangle.Value;
1043             Rectangle rectIcon = new Rectangle(
1044                 rect.X + ToolWindowImageGapLeft,
1045                 rect.Y + rect.Height - ToolWindowImageGapBottom - ToolWindowImageHeight,
1046                 ToolWindowImageWidth, ToolWindowImageHeight);
1047             Rectangle rectText = PatchController.EnableHighDpi == true
1048                 ? new Rectangle(
1049                     rect.X + ToolWindowImageGapLeft,
1050                     rect.Y + rect.Height - ToolWindowImageGapBottom - TextFont.Height,
1051                     ToolWindowImageWidth, TextFont.Height)
1052                 : rectIcon;
1053             rectText.X += rectIcon.Width + ToolWindowImageGapRight;
1054             rectText.Width = rect.Width - rectIcon.Width - ToolWindowImageGapLeft -
1055                 ToolWindowImageGapRight - ToolWindowTextGapRight;
1056 
1057             Rectangle rectTab = DrawHelper.RtlTransform(this, rect);
1058             rectText = DrawHelper.RtlTransform(this, rectText);
1059             rectIcon = DrawHelper.RtlTransform(this, rectIcon);
1060             Color borderColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowBorder;
1061 
1062             Color separatorColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowSeparator;
1063             if (DockPane.ActiveContent == tab.Content)
1064             {
1065                 Color textColor;
1066                 Color backgroundColor;
1067                 if (DockPane.IsActiveDocumentPane)
1068                 {
1069                     textColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowTabSelectedActive.Text;
1070                     backgroundColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowTabSelectedActive.Background;
1071                 }
1072                 else
1073                 {
1074                     textColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowTabSelectedInactive.Text;
1075                     backgroundColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowTabSelectedInactive.Background;
1076                 }
1077 
1078                 g.FillRectangle(DockPane.DockPanel.Theme.PaintingService.GetBrush(backgroundColor), rect);
1079                 g.DrawLine(DockPane.DockPanel.Theme.PaintingService.GetPen(borderColor), rect.Left, rect.Top,
1080                     rect.Left, rect.Bottom);
1081                 g.DrawLine(DockPane.DockPanel.Theme.PaintingService.GetPen(borderColor), rect.Left, rect.Bottom - 1,
1082                     rect.Right, rect.Bottom -1);
1083                 g.DrawLine(DockPane.DockPanel.Theme.PaintingService.GetPen(borderColor), rect.Right - 1, rect.Top,
1084                     rect.Right - 1, rect.Bottom);
1085                 TextRenderer.DrawText(g, tab.Content.DockHandler.TabText, TextFont, rectText, textColor, ToolWindowTextFormat);
1086             }
1087             else
1088             {
1089                 Color textColor;
1090                 Color backgroundColor;
1091                 if (tab.Content == DockPane.MouseOverTab)
1092                 {
1093                     textColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowTabUnselectedHovered.Text;
1094                     backgroundColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowTabUnselectedHovered.Background;
1095                 }
1096                 else
1097                 {
1098                     textColor = DockPane.DockPanel.Theme.ColorPalette.ToolWindowTabUnselected.Text;
1099                     backgroundColor = DockPane.DockPanel.Theme.ColorPalette.MainWindowActive.Background;
1100                 }
1101 
1102                 g.FillRectangle(DockPane.DockPanel.Theme.PaintingService.GetBrush(backgroundColor), rect);
1103                 g.DrawLine(DockPane.DockPanel.Theme.PaintingService.GetPen(borderColor), rect.Left, rect.Top,
1104                    rect.Right, rect.Top);
1105                 TextRenderer.DrawText(g, tab.Content.DockHandler.TabText, TextFont, rectText, textColor, ToolWindowTextFormat);
1106             }
1107 
1108             if (rectTab.Contains(rectIcon))
1109                 g.DrawIcon(tab.Content.DockHandler.Icon, rectIcon);
1110         }
1111 
DrawTab_Document(Graphics g, TabVS2013 tab)1112         private void DrawTab_Document(Graphics g, TabVS2013 tab)
1113         {
1114             var rect = tab.Rectangle.Value;
1115             if (tab.TabWidth == 0)
1116                 return;
1117 
1118             var rectCloseButton = GetCloseButtonRect(rect);
1119             Rectangle rectIcon = new Rectangle(
1120                 rect.X + DocumentIconGapLeft,
1121                 rect.Y + rect.Height - DocumentIconGapBottom - DocumentIconHeight,
1122                 DocumentIconWidth, DocumentIconHeight);
1123             Rectangle rectText = PatchController.EnableHighDpi == true
1124                 ? new Rectangle(
1125                     rect.X + DocumentIconGapLeft,
1126                     rect.Y + rect.Height - DocumentIconGapBottom - TextFont.Height,
1127                     DocumentIconWidth, TextFont.Height)
1128                 : rectIcon;
1129             if (DockPane.DockPanel.ShowDocumentIcon)
1130             {
1131                 rectText.X += rectIcon.Width + DocumentIconGapRight;
1132                 rectText.Y = rect.Y;
1133                 rectText.Width = rect.Width - rectIcon.Width - DocumentIconGapLeft - DocumentIconGapRight - DocumentTextGapRight - rectCloseButton.Width;
1134                 rectText.Height = rect.Height;
1135             }
1136             else
1137                 rectText.Width = rect.Width - DocumentIconGapLeft - DocumentTextGapRight - rectCloseButton.Width;
1138 
1139             Rectangle rectTab = DrawHelper.RtlTransform(this, rect);
1140             Rectangle rectBack = DrawHelper.RtlTransform(this, rect);
1141             rectBack.Width += DocumentIconGapLeft;
1142             rectBack.X -= DocumentIconGapLeft;
1143 
1144             rectText = DrawHelper.RtlTransform(this, rectText);
1145             rectIcon = DrawHelper.RtlTransform(this, rectIcon);
1146 
1147             Color activeColor = DockPane.DockPanel.Theme.ColorPalette.TabSelectedActive.Background;
1148             Color lostFocusColor = DockPane.DockPanel.Theme.ColorPalette.TabSelectedInactive.Background;
1149             Color inactiveColor = DockPane.DockPanel.Theme.ColorPalette.MainWindowActive.Background;
1150             Color mouseHoverColor = DockPane.DockPanel.Theme.ColorPalette.TabUnselectedHovered.Background;
1151 
1152             Color activeText = DockPane.DockPanel.Theme.ColorPalette.TabSelectedActive.Text;
1153             Color lostFocusText = DockPane.DockPanel.Theme.ColorPalette.TabSelectedInactive.Text;
1154             Color inactiveText = DockPane.DockPanel.Theme.ColorPalette.TabUnselected.Text;
1155             Color mouseHoverText = DockPane.DockPanel.Theme.ColorPalette.TabUnselectedHovered.Text;
1156 
1157             Color text;
1158             Image image = null;
1159             Color paint;
1160             var imageService = DockPane.DockPanel.Theme.ImageService;
1161             if (DockPane.ActiveContent == tab.Content)
1162             {
1163                 if (DockPane.IsActiveDocumentPane)
1164                 {
1165                     paint = activeColor;
1166                     text = activeText;
1167                     image = IsMouseDown
1168                         ? imageService.TabPressActive_Close
1169                         : rectCloseButton == ActiveClose
1170                             ? imageService.TabHoverActive_Close
1171                             : imageService.TabActive_Close;
1172                 }
1173                 else
1174                 {
1175                     paint = lostFocusColor;
1176                     text = lostFocusText;
1177                     image = IsMouseDown
1178                         ? imageService.TabPressLostFocus_Close
1179                         : rectCloseButton == ActiveClose
1180                             ? imageService.TabHoverLostFocus_Close
1181                             : imageService.TabLostFocus_Close;
1182                 }
1183             }
1184             else
1185             {
1186                 if (tab.Content == DockPane.MouseOverTab)
1187                 {
1188                     paint = mouseHoverColor;
1189                     text = mouseHoverText;
1190                     image = IsMouseDown
1191                         ? imageService.TabPressInactive_Close
1192                         : rectCloseButton == ActiveClose
1193                             ? imageService.TabHoverInactive_Close
1194                             : imageService.TabInactive_Close;
1195                 }
1196                 else
1197                 {
1198                     paint = inactiveColor;
1199                     text = inactiveText;
1200                 }
1201             }
1202 
1203             g.FillRectangle(DockPane.DockPanel.Theme.PaintingService.GetBrush(paint), rect);
1204             TextRenderer.DrawText(g, tab.Content.DockHandler.TabText, TextFont, rectText, text, DocumentTextFormat);
1205             if (image != null)
1206                 g.DrawImage(image, rectCloseButton);
1207 
1208             if (rectTab.Contains(rectIcon) && DockPane.DockPanel.ShowDocumentIcon)
1209                 g.DrawIcon(tab.Content.DockHandler.Icon, rectIcon);
1210         }
1211 
1212         private bool m_isMouseDown = false;
1213         protected bool IsMouseDown
1214         {
1215             get { return m_isMouseDown; }
1216             private set
1217             {
1218                 if (m_isMouseDown == value)
1219                     return;
1220 
1221                 m_isMouseDown = value;
1222                 Invalidate();
1223             }
1224         }
1225 
OnMouseUp(MouseEventArgs e)1226         protected override void OnMouseUp(MouseEventArgs e)
1227         {
1228             base.OnMouseUp(e);
1229             if (IsMouseDown)
1230                 IsMouseDown = false;
1231         }
1232 
OnMouseDown(MouseEventArgs e)1233         protected override void OnMouseDown(MouseEventArgs e)
1234         {
1235             base.OnMouseDown(e);
1236             // suspend drag if mouse is down on active close button.
1237             this.m_suspendDrag = ActiveCloseHitTest(e.Location);
1238             if (!IsMouseDown)
1239                 IsMouseDown = true;
1240         }
1241 
OnMouseMove(MouseEventArgs e)1242         protected override void OnMouseMove(MouseEventArgs e)
1243         {
1244             if (!this.m_suspendDrag)
1245                 base.OnMouseMove(e);
1246 
1247             int index = HitTest(PointToClient(MousePosition));
1248             string toolTip = string.Empty;
1249 
1250             bool tabUpdate = false;
1251             bool buttonUpdate = false;
1252             if (index != -1)
1253             {
1254                 var tab = Tabs[index] as TabVS2013;
1255                 if (Appearance == DockPane.AppearanceStyle.ToolWindow || Appearance == DockPane.AppearanceStyle.Document)
1256                 {
1257                     tabUpdate = SetMouseOverTab(tab.Content == DockPane.ActiveContent ? null : tab.Content);
1258                 }
1259 
1260                 if (!String.IsNullOrEmpty(tab.Content.DockHandler.ToolTipText))
1261                     toolTip = tab.Content.DockHandler.ToolTipText;
1262                 else if (tab.MaxWidth > tab.TabWidth)
1263                     toolTip = tab.Content.DockHandler.TabText;
1264 
1265                 var mousePos = PointToClient(MousePosition);
1266                 var tabRect = tab.Rectangle.Value;
1267                 var closeButtonRect = GetCloseButtonRect(tabRect);
1268                 var mouseRect = new Rectangle(mousePos, new Size(1, 1));
1269                 buttonUpdate = SetActiveClose(closeButtonRect.IntersectsWith(mouseRect) ? closeButtonRect : Rectangle.Empty);
1270             }
1271             else
1272             {
1273                 tabUpdate = SetMouseOverTab(null);
1274                 buttonUpdate = SetActiveClose(Rectangle.Empty);
1275             }
1276 
1277             if (tabUpdate || buttonUpdate)
1278                 Invalidate();
1279 
1280             if (m_toolTip.GetToolTip(this) != toolTip)
1281             {
1282                 m_toolTip.Active = false;
1283                 m_toolTip.SetToolTip(this, toolTip);
1284                 m_toolTip.Active = true;
1285             }
1286         }
1287 
OnMouseClick(MouseEventArgs e)1288         protected override void OnMouseClick(MouseEventArgs e)
1289         {
1290             base.OnMouseClick(e);
1291             if (e.Button != MouseButtons.Left || Appearance != DockPane.AppearanceStyle.Document)
1292                 return;
1293 
1294             var indexHit = HitTest();
1295             if (indexHit > -1)
1296                 TabCloseButtonHit(indexHit);
1297         }
1298 
TabCloseButtonHit(int index)1299         private void TabCloseButtonHit(int index)
1300         {
1301             var mousePos = PointToClient(MousePosition);
1302             var tabRect = GetTabBounds(Tabs[index]);
1303             if (tabRect.Contains(ActiveClose) && ActiveCloseHitTest(mousePos))
1304                 TryCloseTab(index);
1305         }
1306 
GetCloseButtonRect(Rectangle rectTab)1307         private Rectangle GetCloseButtonRect(Rectangle rectTab)
1308         {
1309             if (Appearance != DockPane.AppearanceStyle.Document)
1310             {
1311                 return Rectangle.Empty;
1312             }
1313 
1314             const int gap = 3;
1315             var imageSize = PatchController.EnableHighDpi == true ? rectTab.Height - gap * 2 : 15;
1316             return new Rectangle(rectTab.X + rectTab.Width - imageSize - gap - 1, rectTab.Y + gap, imageSize, imageSize);
1317         }
1318 
WindowList_Click(object sender, EventArgs e)1319         private void WindowList_Click(object sender, EventArgs e)
1320         {
1321             SelectMenu.Items.Clear();
1322             foreach (TabVS2013 tab in Tabs)
1323             {
1324                 IDockContent content = tab.Content;
1325                 ToolStripItem item = SelectMenu.Items.Add(content.DockHandler.TabText, content.DockHandler.Icon.ToBitmap());
1326                 item.Tag = tab.Content;
1327                 item.Click += new EventHandler(ContextMenuItem_Click);
1328             }
1329 
1330             var workingArea = Screen.GetWorkingArea(ButtonWindowList.PointToScreen(new Point(ButtonWindowList.Width / 2, ButtonWindowList.Height / 2)));
1331             var menu = new Rectangle(ButtonWindowList.PointToScreen(new Point(0, ButtonWindowList.Location.Y + ButtonWindowList.Height)), SelectMenu.Size);
1332             var menuMargined = new Rectangle(menu.X - SelectMenuMargin, menu.Y - SelectMenuMargin, menu.Width + SelectMenuMargin, menu.Height + SelectMenuMargin);
1333             if (workingArea.Contains(menuMargined))
1334             {
1335                 SelectMenu.Show(menu.Location);
1336             }
1337             else
1338             {
1339                 var newPoint = menu.Location;
1340                 newPoint.X = DrawHelper.Balance(SelectMenu.Width, SelectMenuMargin, newPoint.X, workingArea.Left, workingArea.Right);
1341                 newPoint.Y = DrawHelper.Balance(SelectMenu.Size.Height, SelectMenuMargin, newPoint.Y, workingArea.Top, workingArea.Bottom);
1342                 var button = ButtonWindowList.PointToScreen(new Point(0, ButtonWindowList.Height));
1343                 if (newPoint.Y < button.Y)
1344                 {
1345                     // flip the menu up to be above the button.
1346                     newPoint.Y = button.Y - ButtonWindowList.Height;
1347                     SelectMenu.Show(newPoint, ToolStripDropDownDirection.AboveRight);
1348                 }
1349                 else
1350                 {
1351                     SelectMenu.Show(newPoint);
1352                 }
1353             }
1354         }
1355 
ContextMenuItem_Click(object sender, EventArgs e)1356         private void ContextMenuItem_Click(object sender, EventArgs e)
1357         {
1358             ToolStripMenuItem item = sender as ToolStripMenuItem;
1359             if (item != null)
1360             {
1361                 IDockContent content = (IDockContent)item.Tag;
1362                 DockPane.ActiveContent = content;
1363             }
1364         }
1365 
SetInertButtons()1366         private void SetInertButtons()
1367         {
1368             if (Appearance == DockPane.AppearanceStyle.ToolWindow)
1369             {
1370                 if (m_buttonOverflow != null)
1371                     m_buttonOverflow.Left = -m_buttonOverflow.Width;
1372 
1373                 if (m_buttonWindowList != null)
1374                     m_buttonWindowList.Left = -m_buttonWindowList.Width;
1375             }
1376             else
1377             {
1378                 ButtonOverflow.Visible = m_documentTabsOverflow;
1379                 ButtonOverflow.RefreshChanges();
1380 
1381                 ButtonWindowList.Visible = !m_documentTabsOverflow;
1382                 ButtonWindowList.RefreshChanges();
1383             }
1384         }
1385 
OnLayout(LayoutEventArgs levent)1386         protected override void OnLayout(LayoutEventArgs levent)
1387         {
1388             if (Appearance == DockPane.AppearanceStyle.Document)
1389             {
1390                 LayoutButtons();
1391                 OnRefreshChanges();
1392             }
1393 
1394             base.OnLayout(levent);
1395         }
1396 
LayoutButtons()1397         private void LayoutButtons()
1398         {
1399             Rectangle rectTabStrip = TabStripRectangle;
1400 
1401             // Set position and size of the buttons
1402             int buttonWidth = ButtonOverflow.Image.Width;
1403             int buttonHeight = ButtonOverflow.Image.Height;
1404             int height = rectTabStrip.Height - DocumentButtonGapTop - DocumentButtonGapBottom;
1405             if (buttonHeight < height)
1406             {
1407                 buttonWidth = buttonWidth * height / buttonHeight;
1408                 buttonHeight = height;
1409             }
1410             Size buttonSize = new Size(buttonWidth, buttonHeight);
1411 
1412             int x = rectTabStrip.X + rectTabStrip.Width - DocumentTabGapLeft
1413                 - DocumentButtonGapRight - buttonWidth;
1414             int y = rectTabStrip.Y + DocumentButtonGapTop;
1415             Point point = new Point(x, y);
1416             ButtonOverflow.Bounds = DrawHelper.RtlTransform(this, new Rectangle(point, buttonSize));
1417 
1418             // If the close button is not visible draw the window list button overtop.
1419             // Otherwise it is drawn to the left of the close button.
1420             ButtonWindowList.Bounds = DrawHelper.RtlTransform(this, new Rectangle(point, buttonSize));
1421         }
1422 
Close_Click(object sender, EventArgs e)1423         private void Close_Click(object sender, EventArgs e)
1424         {
1425             DockPane.CloseActiveContent();
1426             if (PatchController.EnableMemoryLeakFix == true)
1427             {
1428                 ContentClosed();
1429             }
1430         }
1431 
HitTest(Point point)1432         protected override int HitTest(Point point)
1433         {
1434             if (!TabsRectangle.Contains(point))
1435                 return -1;
1436 
1437             foreach (Tab tab in Tabs)
1438             {
1439                 GraphicsPath path = GetTabOutline(tab, true, false);
1440                 if (path.IsVisible(point))
1441                     return Tabs.IndexOf(tab);
1442             }
1443 
1444             return -1;
1445         }
1446 
MouseDownActivateTest(MouseEventArgs e)1447         protected override bool MouseDownActivateTest(MouseEventArgs e)
1448         {
1449             bool result = base.MouseDownActivateTest(e);
1450             if (result && (e.Button == MouseButtons.Left) && (Appearance == DockPane.AppearanceStyle.Document))
1451             {
1452                 // don't activate if mouse is down on active close button
1453                 result = !ActiveCloseHitTest(e.Location);
1454             }
1455             return result;
1456         }
1457 
ActiveCloseHitTest(Point ptMouse)1458         private bool ActiveCloseHitTest(Point ptMouse)
1459         {
1460             bool result = false;
1461             if (!ActiveClose.IsEmpty)
1462             {
1463                 var mouseRect = new Rectangle(ptMouse, new Size(1, 1));
1464                 result = ActiveClose.IntersectsWith(mouseRect);
1465             }
1466             return result;
1467         }
1468 
GetTabBounds(Tab tab)1469         protected override Rectangle GetTabBounds(Tab tab)
1470         {
1471             GraphicsPath path = GetTabOutline(tab, true, false);
1472             RectangleF rectangle = path.GetBounds();
1473             return new Rectangle((int)rectangle.Left, (int)rectangle.Top, (int)rectangle.Width, (int)rectangle.Height);
1474         }
1475 
1476         private Rectangle ActiveClose
1477         {
1478             get { return _activeClose; }
1479         }
1480 
SetActiveClose(Rectangle rectangle)1481         private bool SetActiveClose(Rectangle rectangle)
1482         {
1483             if (_activeClose == rectangle)
1484                 return false;
1485 
1486             _activeClose = rectangle;
1487             return true;
1488         }
1489 
SetMouseOverTab(IDockContent content)1490         private bool SetMouseOverTab(IDockContent content)
1491         {
1492             if (DockPane.MouseOverTab == content)
1493                 return false;
1494 
1495             DockPane.MouseOverTab = content;
1496             return true;
1497         }
1498 
OnMouseLeave(EventArgs e)1499         protected override void OnMouseLeave(EventArgs e)
1500         {
1501             var tabUpdate = SetMouseOverTab(null);
1502             var buttonUpdate = SetActiveClose(Rectangle.Empty);
1503             if (tabUpdate || buttonUpdate)
1504                 Invalidate();
1505 
1506             base.OnMouseLeave(e);
1507         }
1508 
OnRightToLeftChanged(EventArgs e)1509         protected override void OnRightToLeftChanged(EventArgs e)
1510         {
1511             base.OnRightToLeftChanged(e);
1512             PerformLayout();
1513         }
1514     }
1515 }
1516