1 // System.Windows.Forms.ToolBar.cs
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // Ravindra (rkumar@novell.com)
24 // Mike Kestner <mkestner@novell.com>
29 // Copyright (C) 2004-2006 Novell, Inc. (http://www.novell.com)
35 using System.Collections;
36 using System.ComponentModel;
37 using System.ComponentModel.Design;
39 using System.Drawing.Imaging;
40 using System.Runtime.InteropServices;
42 namespace System.Windows.Forms
44 [DefaultEvent ("ButtonClick")]
45 [DefaultProperty ("Buttons")]
46 [Designer ("System.Windows.Forms.Design.ToolBarDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
47 public class ToolBar : Control
49 #region Instance Variables
50 bool size_specified = false;
51 ToolBarButton current_button;
52 #endregion Instance Variables
56 [EditorBrowsable (EditorBrowsableState.Never)]
57 public new event EventHandler BackColorChanged {
58 add { base.BackColorChanged += value; }
59 remove { base.BackColorChanged -= value; }
63 [EditorBrowsable (EditorBrowsableState.Never)]
64 public new event EventHandler BackgroundImageChanged {
65 add { base.BackgroundImageChanged += value; }
66 remove { base.BackgroundImageChanged -= value; }
69 public event ToolBarButtonClickEventHandler ButtonClick;
70 public event ToolBarButtonClickEventHandler ButtonDropDown;
73 [EditorBrowsable (EditorBrowsableState.Never)]
74 public new event EventHandler ForeColorChanged {
75 add { base.ForeColorChanged += value; }
76 remove { base.ForeColorChanged -= value; }
80 [EditorBrowsable (EditorBrowsableState.Never)]
81 public new event EventHandler ImeModeChanged {
82 add { base.ImeModeChanged += value; }
83 remove { base.ImeModeChanged -= value; }
87 [EditorBrowsable (EditorBrowsableState.Never)]
88 public new event PaintEventHandler Paint {
89 add { base.Paint += value; }
90 remove { base.Paint -= value; }
94 [EditorBrowsable (EditorBrowsableState.Never)]
95 public new event EventHandler RightToLeftChanged {
96 add { base.RightToLeftChanged += value; }
97 remove { base.RightToLeftChanged -= value; }
101 [EditorBrowsable (EditorBrowsableState.Never)]
102 public new event EventHandler TextChanged {
103 add { base.TextChanged += value; }
104 remove { base.TextChanged -= value; }
111 background_color = ThemeEngine.Current.DefaultControlBackColor;
112 foreground_color = ThemeEngine.Current.DefaultControlForeColor;
113 buttons = new ToolBarButtonCollection (this);
114 dock_style = DockStyle.Top;
116 GotFocus += new EventHandler (FocusChanged);
117 LostFocus += new EventHandler (FocusChanged);
118 MouseDown += new MouseEventHandler (ToolBar_MouseDown);
119 MouseHover += new EventHandler (ToolBar_MouseHover);
120 MouseLeave += new EventHandler (ToolBar_MouseLeave);
121 MouseMove += new MouseEventHandler (ToolBar_MouseMove);
122 MouseUp += new MouseEventHandler (ToolBar_MouseUp);
126 SetStyle (ControlStyles.UserPaint, false);
127 SetStyle (ControlStyles.FixedHeight, true);
129 #endregion Constructor
131 #region protected Properties
132 protected override CreateParams CreateParams
134 get { return base.CreateParams; }
137 protected override ImeMode DefaultImeMode {
138 get { return ImeMode.Disable; }
141 protected override Size DefaultSize {
142 get { return ThemeEngine.Current.ToolBarDefaultSize; }
146 ToolBarAppearance appearance = ToolBarAppearance.Normal;
148 #region Public Properties
149 [DefaultValue (ToolBarAppearance.Normal)]
151 public ToolBarAppearance Appearance {
152 get { return appearance; }
154 if (value == appearance)
162 bool autosize = true;
164 [DefaultValue (true)]
166 public bool AutoSize {
167 get { return autosize; }
169 if (value == autosize)
178 [EditorBrowsable (EditorBrowsableState.Never)]
179 public override Color BackColor {
180 get { return background_color; }
182 if (value == background_color)
185 background_color = value;
186 OnBackColorChanged (EventArgs.Empty);
192 [EditorBrowsable (EditorBrowsableState.Never)]
193 public override Image BackgroundImage {
194 get { return background_image; }
196 if (value == background_image)
199 background_image = value;
200 OnBackgroundImageChanged (EventArgs.Empty);
205 [DefaultValue (BorderStyle.None)]
206 [DispIdAttribute (-504)]
207 public BorderStyle BorderStyle {
208 get { return InternalBorderStyle; }
209 set { InternalBorderStyle = value; }
212 ToolBarButtonCollection buttons;
214 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
216 [MergableProperty (false)]
217 public ToolBarButtonCollection Buttons {
218 get { return buttons; }
224 [RefreshProperties (RefreshProperties.All)]
225 public Size ButtonSize {
227 if (button_size.IsEmpty) {
228 if (buttons.Count == 0)
229 return new Size (24, 22);
230 Size result = CalcButtonSize ();
232 return new Size (24, 22);
239 size_specified = value != Size.Empty;
240 if (button_size == value)
250 [DefaultValue (true)]
251 public bool Divider {
252 get { return divider; }
254 if (value == divider)
262 [DefaultValue (DockStyle.Top)]
264 public override DockStyle Dock {
265 get { return base.Dock; }
266 set { base.Dock = value; }
269 bool drop_down_arrows = true;
271 [DefaultValue (false)]
273 public bool DropDownArrows {
274 get { return drop_down_arrows; }
276 if (value == drop_down_arrows)
279 drop_down_arrows = value;
285 [EditorBrowsable (EditorBrowsableState.Never)]
286 public override Color ForeColor {
287 get { return foreground_color; }
289 if (value == foreground_color)
292 foreground_color = value;
293 OnForeColorChanged (EventArgs.Empty);
298 ImageList image_list;
300 [DefaultValue (null)]
301 public ImageList ImageList {
302 get { return image_list; }
304 if (image_list == value)
312 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
313 [EditorBrowsable (EditorBrowsableState.Advanced)]
314 public Size ImageSize {
316 if (ImageList == null)
319 return ImageList.ImageSize;
326 [EditorBrowsable (EditorBrowsableState.Never)]
327 public new ImeMode ImeMode {
328 get { return ime_mode; }
330 if (value == ime_mode)
334 OnImeModeChanged (EventArgs.Empty);
339 [EditorBrowsable (EditorBrowsableState.Never)]
340 public override RightToLeft RightToLeft {
341 get { return base.RightToLeft; }
343 if (value == base.RightToLeft)
346 base.RightToLeft = value;
347 OnRightToLeftChanged (EventArgs.Empty);
351 bool show_tooltips = false;
353 [DefaultValue (false)]
355 public bool ShowToolTips {
356 get { return show_tooltips; }
357 set { show_tooltips = value; }
360 [DefaultValue (false)]
361 public new bool TabStop {
362 get { return base.TabStop; }
363 set { base.TabStop = value; }
368 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
369 [EditorBrowsable (EditorBrowsableState.Never)]
370 public override string Text {
378 OnTextChanged (EventArgs.Empty);
382 ToolBarTextAlign text_alignment = ToolBarTextAlign.Underneath;
384 [DefaultValue (ToolBarTextAlign.Underneath)]
386 public ToolBarTextAlign TextAlign {
387 get { return text_alignment; }
389 if (value == text_alignment)
392 text_alignment = value;
397 bool wrappable = true;
399 [DefaultValue (true)]
401 public bool Wrappable {
402 get { return wrappable; }
404 if (value == wrappable)
411 #endregion Public Properties
413 #region Public Methods
414 public override string ToString ()
416 int count = this.Buttons.Count;
419 return string.Format ("System.Windows.Forms.ToolBar, Buttons.Count: 0");
421 return string.Format ("System.Windows.Forms.ToolBar, Buttons.Count: {0}, Buttons[0]: {1}",
422 count, this.Buttons [0].ToString ());
424 #endregion Public Methods
426 #region Protected Methods
427 protected override void CreateHandle ()
429 base.CreateHandle ();
432 protected override void Dispose (bool disposing)
437 base.Dispose (disposing);
440 protected virtual void OnButtonClick (ToolBarButtonClickEventArgs e)
442 if (e.Button.Style == ToolBarButtonStyle.ToggleButton) {
443 if (! e.Button.Pushed)
444 e.Button.Pushed = true;
446 e.Button.Pushed = false;
448 e.Button.pressed = false;
450 e.Button.Invalidate ();
452 if (ButtonClick != null)
453 ButtonClick (this, e);
456 protected virtual void OnButtonDropDown (ToolBarButtonClickEventArgs e)
458 if (ButtonDropDown != null)
459 ButtonDropDown (this, e);
461 if (e.Button.DropDownMenu == null)
464 Point loc = new Point (e.Button.Rectangle.X + 1, e.Button.Rectangle.Bottom + 1);
465 ((ContextMenu) e.Button.DropDownMenu).Show (this, loc);
467 e.Button.dd_pressed = false;
468 Invalidate (e.Button.Rectangle);
471 protected override void OnFontChanged (EventArgs e)
473 base.OnFontChanged (e);
477 protected override void OnHandleCreated (EventArgs e)
479 base.OnHandleCreated (e);
482 protected override void OnResize (EventArgs e)
486 if (Width <= 0 || Height <= 0 || !Visible)
489 Redraw (true, background_image != null);
492 int requested_height = -1;
494 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
496 // New height requested
497 if (!AutoSize && (requested_height != height) && ((specified & BoundsSpecified.Height) != BoundsSpecified.None))
498 requested_height = height;
500 base.SetBoundsCore (x, y, width, height, specified);
503 protected override void WndProc (ref Message m)
505 base.WndProc (ref m);
508 internal override bool InternalPreProcessMessage (ref Message msg)
510 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
511 Keys key_data = (Keys)msg.WParam.ToInt32();
512 if (HandleKeyDown (key_data))
515 return base.InternalPreProcessMessage (ref msg);
518 #endregion Protected Methods
520 #region Private Methods
521 private void FocusChanged (object sender, EventArgs args)
523 if (Appearance != ToolBarAppearance.Flat || Buttons.Count == 0)
526 ToolBarButton prelit = null;
527 foreach (ToolBarButton b in Buttons)
533 if (Focused && prelit == null)
534 foreach (ToolBarButton btn in Buttons) {
540 else if (prelit != null)
541 prelit.Hilight = false;
544 private bool HandleKeyDown (Keys key_data)
546 if (Appearance != ToolBarAppearance.Flat || Buttons.Count == 0)
552 HighlightButton (-1);
563 void HighlightButton (int offset)
565 ArrayList enabled = new ArrayList ();
568 ToolBarButton curr_button = null;
569 foreach (ToolBarButton btn in Buttons) {
581 int next = (start + offset) % count;
588 if (curr_button != null)
589 curr_button.Hilight = false;
590 (enabled [next] as ToolBarButton).Hilight = true;
593 private void ToolBar_MouseDown (object sender, MouseEventArgs me)
595 if ((!Enabled) || ((me.Button & MouseButtons.Left) == 0))
598 Point loc = new Point (me.X, me.Y);
600 if (ButtonAtPoint (loc) == null)
603 // Hide tooltip when left mouse button
604 if ((tip_window != null) && (tip_window.Visible) && ((me.Button & MouseButtons.Left) == MouseButtons.Left)) {
605 TipDownTimer.Stop ();
609 // draw the pushed button
610 foreach (ToolBarButton button in buttons) {
611 if (button.Enabled && button.Rectangle.Contains (loc)) {
612 // Mark the DropDown rect as pressed.
613 // We don't redraw the dropdown rect.
614 if (button.Style == ToolBarButtonStyle.DropDownButton) {
615 Rectangle rect = button.Rectangle;
616 if (DropDownArrows) {
617 rect.Width = ThemeEngine.Current.ToolBarDropDownWidth;
618 rect.X = button.Rectangle.Right - rect.Width;
621 if (rect.Contains (loc)) {
622 if (button.DropDownMenu != null) {
623 button.dd_pressed = true;
629 button.pressed = true;
630 button.inside = true;
631 button.Invalidate ();
637 private void ToolBar_MouseUp (object sender, MouseEventArgs me)
639 if ((!Enabled) || ((me.Button & MouseButtons.Left) == 0))
642 Point loc = new Point (me.X, me.Y);
644 // draw the normal button
645 // Make a copy in case the list is modified during enumeration
646 ArrayList buttons = new ArrayList (this.buttons);
647 foreach (ToolBarButton button in buttons) {
648 if (button.Enabled && button.Rectangle.Contains (loc)) {
649 if (button.Style == ToolBarButtonStyle.DropDownButton) {
650 Rectangle ddRect = button.Rectangle;
651 ddRect.Width = ThemeEngine.Current.ToolBarDropDownWidth;
652 ddRect.X = button.Rectangle.Right - ddRect.Width;
653 if (ddRect.Contains (loc)) {
654 if (button.dd_pressed)
655 OnButtonDropDown (new ToolBarButtonClickEventArgs (button));
659 // Fire a ButtonClick
660 if ((button.pressed) && ((me.Button & MouseButtons.Left) == MouseButtons.Left))
661 OnButtonClick (new ToolBarButtonClickEventArgs (button));
662 } else if (button.pressed) {
663 button.pressed = false;
664 button.Invalidate ();
669 private ToolBarButton ButtonAtPoint (Point pt)
671 foreach (ToolBarButton button in buttons)
672 if (button.Rectangle.Contains (pt))
678 ToolTip.ToolTipWindow tip_window = null;
679 Timer tipdown_timer = null;
681 private void PopDownTip (object o, EventArgs args)
686 private Timer TipDownTimer {
688 if (tipdown_timer == null) {
689 tipdown_timer = new Timer ();
690 tipdown_timer.Enabled = false;
691 tipdown_timer.Interval = 5000;
692 tipdown_timer.Tick += new EventHandler (PopDownTip);
694 return tipdown_timer;
698 private void ToolBar_MouseHover (object sender, EventArgs e)
703 if (tip_window == null)
704 tip_window = new ToolTip.ToolTipWindow ();
706 ToolBarButton btn = ButtonAtPoint (PointToClient (Control.MousePosition));
707 current_button = btn;
709 if (btn == null || btn.ToolTipText.Length == 0)
712 tip_window.Present (this, btn.ToolTipText);
713 TipDownTimer.Start ();
716 private void ToolBar_MouseLeave (object sender, EventArgs e)
718 if (tipdown_timer != null)
719 tipdown_timer.Dispose ();
720 tipdown_timer = null;
721 if (tip_window != null)
722 tip_window.Dispose ();
725 if (!Enabled || current_button == null)
728 current_button.Hilight = false;
729 current_button = null;
732 private void ToolBar_MouseMove (object sender, MouseEventArgs me)
737 if (tip_window != null && tip_window.Visible) {
738 TipDownTimer.Stop ();
739 TipDownTimer.Start ();
742 Point loc = new Point (me.X, me.Y);
745 // If the button was pressed and we leave, release the
746 // button press and vice versa
747 foreach (ToolBarButton button in buttons) {
748 if (button.pressed &&
749 (button.inside != button.Rectangle.Contains (loc))) {
750 button.inside = button.Rectangle.Contains (loc);
751 button.Hilight = false;
758 if (current_button != null && current_button.Rectangle.Contains (loc)) {
759 if (appearance == ToolBarAppearance.Flat) {
760 if (current_button.Hilight || current_button.Pushed || !current_button.Enabled)
762 current_button.Hilight = true;
765 if (tip_window != null) {
766 if (tip_window.Visible) {
768 TipDownTimer.Stop ();
770 current_button = ButtonAtPoint (loc);
771 if (current_button != null && current_button.ToolTipText.Length > 0) {
772 tip_window.Present (this, current_button.ToolTipText);
773 TipDownTimer.Start ();
777 if (appearance == ToolBarAppearance.Flat) {
778 foreach (ToolBarButton button in buttons) {
779 if (button.Rectangle.Contains (loc) && button.Enabled) {
780 current_button = button;
781 if (current_button.Hilight || current_button.Pushed)
783 current_button.Hilight = true;
785 else if (button.Hilight) {
786 button.Hilight = false;
793 internal override void OnPaintInternal (PaintEventArgs pevent)
795 ThemeEngine.Current.DrawToolBar (pevent.Graphics, pevent.ClipRectangle, this);
798 internal void Redraw (bool recalculate)
800 Redraw (recalculate, true);
803 internal void Redraw (bool recalculate, bool force)
805 bool invalidate = true;
807 invalidate = Layout ();
810 if (force || invalidate)
814 internal bool SizeSpecified {
815 get { return size_specified; }
818 const int text_padding = 3;
820 private Size CalcButtonSize ()
822 if (Buttons.Count == 0)
825 string longest_text = Buttons [0].Text;
826 for (int i = 1; i < Buttons.Count; i++) {
827 if (Buttons[i].Text.Length > longest_text.Length)
828 longest_text = Buttons[i].Text;
831 Size size = Size.Empty;
832 if (longest_text != null && longest_text.Length > 0) {
833 SizeF sz = DeviceContext.MeasureString (longest_text, Font);
834 if (sz != SizeF.Empty)
835 size = new Size ((int) Math.Ceiling (sz.Width) + 2 * text_padding, (int) Math.Ceiling (sz.Height));
838 Size img_size = ImageList == null ? new Size (16, 16) : ImageSize;
840 Theme theme = ThemeEngine.Current;
841 int imgWidth = img_size.Width + 2 * theme.ToolBarImageGripWidth;
842 int imgHeight = img_size.Height + 2 * theme.ToolBarImageGripWidth;
844 if (text_alignment == ToolBarTextAlign.Right) {
845 size.Width = imgWidth + size.Width;
846 size.Height = (size.Height > imgHeight) ? size.Height : imgHeight;
848 size.Height = imgHeight + size.Height;
849 size.Width = (size.Width > imgWidth) ? size.Width : imgWidth;
852 size.Width += theme.ToolBarImageGripWidth;
853 size.Height += theme.ToolBarImageGripWidth;
857 // Flat toolbars disregard specified sizes. Normal toolbars grow the
858 // button size to be at least large enough to show the image.
859 Size AdjustedButtonSize {
861 Size size = ButtonSize;
862 if (size_specified) {
863 if (Appearance == ToolBarAppearance.Flat)
864 size = CalcButtonSize ();
866 int grip = ThemeEngine.Current.ToolBarImageGripWidth;
867 if (size.Width < ImageSize.Width + 2 * grip )
868 size.Width = ImageSize.Width + 2 * grip;
869 if (size.Height < ImageSize.Height + 2 * grip)
870 size.Height = ImageSize.Height + 2 * grip;
882 bool changed = false;
883 Theme theme = ThemeEngine.Current;
884 int x = theme.ToolBarGripWidth;
885 int y = theme.ToolBarGripWidth;
887 int ht = AdjustedButtonSize.Height + theme.ToolBarGripWidth;
889 int separator_index = -1;
891 for (int i = 0; i < buttons.Count; i++) {
892 ToolBarButton button = buttons [i];
897 if (size_specified && (button.Style != ToolBarButtonStyle.Separator)) {
898 if (button.Layout (button_size))
902 if (button.Layout ())
906 bool is_separator = button.Style == ToolBarButtonStyle.Separator;
908 if (x + button.Rectangle.Width < Width || is_separator || !Wrappable) {
909 if (button.Location.X != x || button.Location.Y != y)
911 button.Location = new Point (x, y);
912 x += button.Rectangle.Width;
915 } else if (separator_index > 0) {
917 separator_index = -1;
918 x = theme.ToolBarGripWidth;
921 x = theme.ToolBarGripWidth;
923 if (button.Location.X != x || button.Location.Y != y)
925 button.Location = new Point (x, y);
926 x += button.Rectangle.Width;
930 Height = ht + (Wrappable ? y : 0);
932 Height = requested_height;
936 #endregion Private Methods
939 public class ToolBarButtonCollection : IList, ICollection, IEnumerable
941 #region instance variables
942 private ArrayList list;
943 private ToolBar owner;
948 public ToolBarButtonCollection (ToolBar owner)
951 list = new ArrayList ();
959 get { return list.Count; }
962 public bool IsReadOnly {
963 get { return list.IsReadOnly; }
966 public virtual ToolBarButton this [int index] {
967 get { return (ToolBarButton) list [index]; }
969 value.SetParent (owner);
970 list [index] = value;
975 bool ICollection.IsSynchronized {
976 get { return list.IsSynchronized; }
979 object ICollection.SyncRoot {
980 get { return list.SyncRoot; }
983 bool IList.IsFixedSize {
984 get { return list.IsFixedSize; }
987 object IList.this [int index] {
988 get { return this [index]; }
990 if (! (value is ToolBarButton))
991 throw new ArgumentException("Not of type ToolBarButton", "value");
992 this [index] = (ToolBarButton) value;
998 public int Add (string text)
1000 ToolBarButton button = new ToolBarButton (text);
1001 return this.Add (button);
1004 public int Add (ToolBarButton button)
1007 button.SetParent (owner);
1008 result = list.Add (button);
1010 owner.Redraw (true);
1014 public void AddRange (ToolBarButton [] buttons)
1018 foreach (ToolBarButton button in buttons)
1023 owner.Redraw (true);
1027 public void Clear ()
1030 owner.Redraw (false);
1033 public bool Contains (ToolBarButton button)
1035 return list.Contains (button);
1038 public IEnumerator GetEnumerator ()
1040 return list.GetEnumerator ();
1043 void ICollection.CopyTo (Array dest, int index)
1045 list.CopyTo (dest, index);
1048 int IList.Add (object button)
1050 if (! (button is ToolBarButton)) {
1051 throw new ArgumentException("Not of type ToolBarButton", "button");
1054 return this.Add ((ToolBarButton) button);
1057 bool IList.Contains (object button)
1059 if (! (button is ToolBarButton)) {
1060 throw new ArgumentException("Not of type ToolBarButton", "button");
1063 return this.Contains ((ToolBarButton) button);
1066 int IList.IndexOf (object button)
1068 if (! (button is ToolBarButton)) {
1069 throw new ArgumentException("Not of type ToolBarButton", "button");
1072 return this.IndexOf ((ToolBarButton) button);
1075 void IList.Insert (int index, object button)
1077 if (! (button is ToolBarButton)) {
1078 throw new ArgumentException("Not of type ToolBarButton", "button");
1081 this.Insert (index, (ToolBarButton) button);
1084 void IList.Remove (object button)
1086 if (! (button is ToolBarButton)) {
1087 throw new ArgumentException("Not of type ToolBarButton", "button");
1090 this.Remove ((ToolBarButton) button);
1093 public int IndexOf (ToolBarButton button)
1095 return list.IndexOf (button);
1098 public void Insert (int index, ToolBarButton button)
1100 list.Insert (index, button);
1101 owner.Redraw (true);
1104 public void Remove (ToolBarButton button)
1106 list.Remove (button);
1107 owner.Redraw (true);
1110 public void RemoveAt (int index)
1112 list.RemoveAt (index);
1113 owner.Redraw (true);