Merge pull request #704 from jgagnon/master
[mono.git] / mcs / class / System.Windows.Forms / System.Windows.Forms / ToolStripSplitButton.cs
1 //
2 // ToolStripSplitButton.cs
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Copyright (c) 2006 Jonathan Pobst
24 //
25 // Authors:
26 //      Jonathan Pobst (monkey@jpobst.com)
27 //
28
29 using System;
30 using System.Drawing;
31 using System.ComponentModel;
32 using System.Windows.Forms.Design;
33
34 namespace System.Windows.Forms
35 {
36         [DefaultEvent ("ButtonClick")]
37         [ToolStripItemDesignerAvailability (ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
38         public class ToolStripSplitButton : ToolStripDropDownItem
39         {
40                 private bool button_pressed;
41                 private ToolStripItem default_item;
42                 private bool drop_down_button_selected;
43                 private int drop_down_button_width;
44                 
45                 #region Public Constructors
46                 public ToolStripSplitButton()
47                         : this (string.Empty, null, null, string.Empty)
48                 {
49                 }
50                 
51                 public ToolStripSplitButton (Image image)
52                         : this (string.Empty, image, null, string.Empty)
53                 {
54                 }
55                 
56                 public ToolStripSplitButton (string text)
57                         : this (text, null, null, string.Empty)
58                 {
59                 }
60                 
61                 public ToolStripSplitButton (string text, Image image)
62                         : this (text, image, null, string.Empty)
63                 {
64                 }
65                 
66                 public ToolStripSplitButton (string text, Image image, EventHandler onClick)
67                         : this (text, image, onClick, string.Empty)
68                 {
69                 }
70                 
71                 public ToolStripSplitButton (string text, Image image, params ToolStripItem[] dropDownItems)
72                         : base (text, image, dropDownItems)
73                 {
74                         this.ResetDropDownButtonWidth ();
75                 }
76
77                 public ToolStripSplitButton (string text, Image image, EventHandler onClick, string name)
78                         : base (text, image, onClick, name)
79                 {
80                         this.ResetDropDownButtonWidth ();
81                 }
82                 #endregion
83
84                 #region Public Properties
85                 [DefaultValue (true)]
86                 public new bool AutoToolTip {
87                         get { return base.AutoToolTip; }
88                         set { base.AutoToolTip = value; }
89                 }
90
91                 [Browsable (false)]
92                 public Rectangle ButtonBounds {
93                         get { return new Rectangle (Bounds.Left, Bounds.Top, this.Bounds.Width - this.drop_down_button_width - 1, this.Height); }
94                 }
95
96                 [Browsable (false)]
97                 public bool ButtonPressed {
98                         get { return this.button_pressed; }
99                 }
100
101                 [Browsable (false)]
102                 public bool ButtonSelected {
103                         get { return base.Selected; }
104                 }
105
106                 [Browsable (false)]
107                 [DefaultValue (null)]
108                 public ToolStripItem DefaultItem {
109                         get { return this.default_item; }
110                         set {
111                                 if (this.default_item != value) {
112                                         this.default_item = value;
113                                         this.OnDefaultItemChanged (EventArgs.Empty);
114                                 }
115                         }
116                 }
117                 
118                 [Browsable (false)]
119                 public Rectangle DropDownButtonBounds {
120                         get { return new Rectangle (this.Bounds.Right - this.drop_down_button_width, 0, this.drop_down_button_width, this.Bounds.Height); }
121                 }
122
123                 [Browsable (false)]
124                 public bool DropDownButtonPressed {
125                         get { return this.drop_down_button_selected || (this.HasDropDownItems && this.DropDown.Visible); }
126                 }
127
128                 [Browsable (false)]
129                 public bool DropDownButtonSelected {
130                         get { return base.Selected; }
131                 }
132                 
133                 public int DropDownButtonWidth {
134                         get { return this.drop_down_button_width; }
135                         set { 
136                                 if (value < 0)
137                                         throw new ArgumentOutOfRangeException ();
138                                 if (this.drop_down_button_width != value) {
139                                         this.drop_down_button_width = value;
140                                         CalculateAutoSize ();
141                                 }
142                         }
143                 }
144
145                 [Browsable (false)]
146                 public Rectangle SplitterBounds {
147                         get { return new Rectangle (this.Bounds.Width - this.drop_down_button_width - 1, 0, 1, this.Height); }
148                 }
149                 #endregion
150
151                 #region Protected Properties
152                 protected override bool DefaultAutoToolTip {
153                         get { return true; }
154                 }
155
156                 protected internal override bool DismissWhenClicked {
157                         get { return true; }
158                 }
159                 #endregion
160
161                 #region Public Methods
162                 public override Size GetPreferredSize (Size constrainingSize)
163                 {
164                         // base should calculate the button part for us, add the splitter
165                         // and drop down arrow part to that
166                         Size s = base.GetPreferredSize (constrainingSize);
167
168                         if (s.Width < 23)
169                                 s.Width = 23;
170
171                         // If we are a fixed size, we can't add more in for the drop down
172                         // button, but we can for autosize
173                         if (AutoSize)
174                                 s.Width += (this.drop_down_button_width - 2);
175                         
176                         return s;
177                 }
178                 
179                 public virtual void OnButtonDoubleClick (EventArgs e)
180                 {
181                         EventHandler eh = (EventHandler)(Events [ButtonDoubleClickEvent]);
182                         if (eh != null)
183                                 eh (this, e);
184                 }
185                 
186                 public void PerformButtonClick ()
187                 {
188                         if (this.Enabled)
189                                 this.OnButtonClick (EventArgs.Empty);
190                 }
191                 
192                 [EditorBrowsable (EditorBrowsableState.Never)]
193                 public virtual void ResetDropDownButtonWidth ()
194                 {
195                         this.DropDownButtonWidth = 11;
196                 }
197                 #endregion
198
199                 #region Protected Methods
200                 protected override AccessibleObject CreateAccessibilityInstance ()
201                 {
202                         return new ToolStripSplitButtonAccessibleObject (this);
203                 }
204                 
205                 protected override ToolStripDropDown CreateDefaultDropDown ()
206                 {
207                         ToolStripDropDownMenu tsddm = new ToolStripDropDownMenu ();
208                         tsddm.OwnerItem = this;
209                         return tsddm;
210                 }
211                 
212                 protected virtual void OnButtonClick (EventArgs e)
213                 {
214                         EventHandler eh = (EventHandler)Events [ButtonClickEvent];
215                         if (eh != null)
216                                 eh (this, e);
217                 }
218                 
219                 protected virtual void OnDefaultItemChanged (EventArgs e)
220                 {
221                         EventHandler eh = (EventHandler)Events [DefaultItemChangedEvent];
222                         if (eh != null)
223                                 eh (this, e);
224                 }
225
226                 protected override void OnMouseDown (MouseEventArgs e)
227                 {
228                         if (this.ButtonBounds.Contains (e.Location))
229                         {
230                                 this.button_pressed = true;
231                                 this.Invalidate ();
232                                 base.OnMouseDown (e);
233                         }
234                         else if (this.DropDownButtonBounds.Contains (e.Location))
235                         {
236                                 if (this.DropDown.Visible)
237                                         this.HideDropDown (ToolStripDropDownCloseReason.ItemClicked);
238                                 else
239                                         this.ShowDropDown ();
240                         
241                                 this.Invalidate ();
242                                 base.OnMouseDown (e);
243                         }
244                 }
245
246                 protected override void OnMouseLeave (EventArgs e)
247                 {
248                         this.drop_down_button_selected = false;
249                         this.button_pressed = false;
250                         
251                         this.Invalidate ();
252                         
253                         base.OnMouseLeave (e);
254                 }
255
256                 protected override void OnMouseUp (MouseEventArgs e)
257                 {
258                         this.button_pressed = false;
259                         this.Invalidate ();
260                         
261                         base.OnMouseUp (e);
262                 }
263                 
264                 protected override void OnPaint (PaintEventArgs e)
265                 {
266                         base.OnPaint (e);
267
268                         if (this.Owner != null) {
269                                 Color font_color = this.Enabled ? this.ForeColor : SystemColors.GrayText;
270                                 Image draw_image = this.Enabled ? this.Image : ToolStripRenderer.CreateDisabledImage (this.Image);
271
272                                 this.Owner.Renderer.DrawSplitButton (new System.Windows.Forms.ToolStripItemRenderEventArgs (e.Graphics, this));
273
274                                 Rectangle text_layout_rect;
275                                 Rectangle image_layout_rect;
276
277                                 Rectangle r = this.ContentRectangle;
278                                 r.Width -= (this.drop_down_button_width + 1);
279                                 
280                                 this.CalculateTextAndImageRectangles (r, out text_layout_rect, out image_layout_rect);
281
282                                 if (text_layout_rect != Rectangle.Empty)
283                                         this.Owner.Renderer.DrawItemText (new System.Windows.Forms.ToolStripItemTextRenderEventArgs (e.Graphics, this, this.Text, text_layout_rect, font_color, this.Font, this.TextAlign));
284                                 if (image_layout_rect != Rectangle.Empty)
285                                         this.Owner.Renderer.DrawItemImage (new System.Windows.Forms.ToolStripItemImageRenderEventArgs (e.Graphics, this, draw_image, image_layout_rect));
286
287                                 this.Owner.Renderer.DrawArrow (new ToolStripArrowRenderEventArgs (e.Graphics, this, new Rectangle (this.Width - 9, 1, 6, this.Height), Color.Black, ArrowDirection.Down));
288                                 
289                                 return;
290                         }
291                 }
292
293                 protected override void OnRightToLeftChanged (EventArgs e)
294                 {
295                         base.OnRightToLeftChanged (e);
296                 }
297                 
298                 protected internal override bool ProcessDialogKey (Keys keyData)
299                 {
300                         if (this.Selected && keyData == Keys.Enter && this.DefaultItem != null) {
301                                 this.DefaultItem.FireEvent (EventArgs.Empty, ToolStripItemEventType.Click);
302                                 return true;
303                         }
304
305                         return base.ProcessDialogKey (keyData);
306                 }
307
308                 protected internal override bool ProcessMnemonic (char charCode)
309                 {
310                         if (!this.Selected)
311                                 this.Parent.ChangeSelection (this);
312
313                         if (this.HasDropDownItems)
314                                 this.ShowDropDown ();
315                         else
316                                 this.PerformClick ();
317
318                         return true;
319                 }
320                 #endregion
321
322                 #region Internal Methods
323                 internal override void HandleClick (int mouse_clicks, EventArgs e)
324                 {
325                         base.HandleClick (mouse_clicks, e);
326
327                         MouseEventArgs mea = e as MouseEventArgs;
328                         
329                         if (mea != null)
330                                 if (ButtonBounds.Contains (mea.Location))
331                                         OnButtonClick (EventArgs.Empty);
332                 }
333                 #endregion
334                 
335                 #region Public Events
336                 static object ButtonClickEvent = new object ();
337                 static object ButtonDoubleClickEvent = new object ();
338                 static object DefaultItemChangedEvent = new object ();
339
340                 public event EventHandler ButtonClick {
341                         add { Events.AddHandler (ButtonClickEvent, value); }
342                         remove {Events.RemoveHandler (ButtonClickEvent, value); }
343                 }
344                 public event EventHandler ButtonDoubleClick {
345                         add { Events.AddHandler (ButtonDoubleClickEvent, value); }
346                         remove {Events.RemoveHandler (ButtonDoubleClickEvent, value); }
347                 }
348                 public event EventHandler DefaultItemChanged {
349                         add { Events.AddHandler (DefaultItemChangedEvent, value); }
350                         remove {Events.RemoveHandler (DefaultItemChangedEvent, value); }
351                 }
352                 #endregion
353
354                 #region ToolStripSplitButtonAccessibleObject Class
355                 public class ToolStripSplitButtonAccessibleObject : ToolStripItemAccessibleObject
356                 {
357                         #region Public Constructor
358                         public ToolStripSplitButtonAccessibleObject (ToolStripSplitButton item) : base (item)
359                         {
360                         }
361                         #endregion
362
363                         #region Public Method
364                         public override void DoDefaultAction ()
365                         {
366                                 (owner_item as ToolStripSplitButton).PerformButtonClick ();
367                         }
368                         #endregion
369                 }
370                 #endregion
371         }
372 }