1 // 2 // MenuButton.cs 3 // 4 // Author: 5 // Michael Hutchinson <mhutchinson@novell.com> 6 // 7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com) 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining 10 // a copy of this software and associated documentation files (the 11 // "Software"), to deal in the Software without restriction, including 12 // without limitation the rights to use, copy, modify, merge, publish, 13 // distribute, sublicense, and/or sell copies of the Software, and to 14 // permit persons to whom the Software is furnished to do so, subject to 15 // the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be 18 // included in all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 // 28 29 using System; 30 using Gtk; 31 32 namespace MonoDevelop.Components 33 { 34 [System.ComponentModel.Category ("MonoDevelop.Components")] 35 [System.ComponentModel.ToolboxItem (true)] 36 class MenuButton : Button 37 { 38 MenuCreator creator; 39 Label label; 40 Image image; 41 Arrow arrow; 42 bool isOpen; 43 MenuButton()44 public MenuButton () 45 : base () 46 { 47 HBox box = new HBox (); 48 box.Spacing = 6; 49 Add (box); 50 51 image = new Image (); 52 image.NoShowAll = true; 53 box.PackStart (image, false, false, 0); 54 label = new Label (); 55 label.NoShowAll = true; 56 box.PackStart (label, false, false, 0); 57 ArrowType = Gtk.ArrowType.Down; 58 base.Label = null; 59 } 60 MenuButton(IntPtr raw)61 protected MenuButton (IntPtr raw) 62 : base (raw) 63 { 64 65 } 66 67 public MenuCreator MenuCreator { 68 get { return creator; } 69 set { creator = value; } 70 } 71 OnClicked()72 protected override void OnClicked () 73 { 74 base.OnClicked (); 75 76 if (creator != null) { 77 Menu menu = creator (this); 78 79 if (menu != null) { 80 isOpen = true; 81 82 //make sure the button looks depressed 83 ReliefStyle oldRelief = this.Relief; 84 this.Relief = ReliefStyle.Normal; 85 86 //clean up after the menu's done 87 menu.Hidden += delegate { 88 this.Relief = oldRelief ; 89 isOpen = false; 90 this.State = StateType.Normal; 91 92 //FIXME: for some reason the menu's children don't get activated if we destroy 93 //directly here, so use a timeout to delay it 94 GLib.Timeout.Add (100, delegate { 95 menu.Destroy (); 96 return false; 97 }); 98 }; 99 menu.Popup (null, null, PositionFunc, 0, Gtk.Global.CurrentEventTime); 100 } 101 } 102 103 } 104 OnStateChanged(StateType previous_state)105 protected override void OnStateChanged(StateType previous_state) 106 { 107 base.OnStateChanged (previous_state); 108 109 //while the menu's open, make sure the button looks depressed 110 if (isOpen && this.State != StateType.Active) 111 this.State = StateType.Active; 112 } 113 PositionFunc(Menu mn, out int x, out int y, out bool push_in)114 void PositionFunc (Menu mn, out int x, out int y, out bool push_in) 115 { 116 this.GdkWindow.GetOrigin (out x, out y); 117 Gdk.Rectangle rect = this.Allocation; 118 x += rect.X; 119 y += rect.Y + rect.Height; 120 121 //if the menu would be off the bottom of the screen, "drop" it upwards 122 if (y + mn.Requisition.Height > this.Screen.Height) { 123 y -= mn.Requisition.Height; 124 y -= rect.Height; 125 } 126 127 //let GTK reposition the button if it still doesn't fit on the screen 128 push_in = true; 129 } 130 131 public ArrowType? ArrowType { 132 get { return arrow == null? (Gtk.ArrowType?) null : arrow.ArrowType; } 133 set { 134 if (value == null) { 135 if (arrow != null) { 136 ((HBox)arrow.Parent).Remove (arrow); 137 arrow.Destroy (); 138 arrow = null; 139 } 140 } else { 141 if (arrow == null ) { 142 arrow = new Arrow (Gtk.ArrowType.Down, ShadowType.Out); 143 arrow.Show (); 144 ((HBox)label.Parent).PackEnd (arrow, false, false, 0); 145 } 146 arrow.ArrowType = value?? Gtk.ArrowType.Down; 147 } 148 } 149 } 150 OnDestroyed()151 protected override void OnDestroyed () 152 { 153 creator = null; 154 base.OnDestroyed (); 155 } 156 157 public new string Label { 158 get { return label.Text; } 159 set { 160 label.Text = value; 161 label.Visible = !string.IsNullOrEmpty (value); 162 } 163 } 164 165 public new bool UseUnderline { 166 get { return label.UseUnderline; } 167 set { label.UseUnderline = value; } 168 } 169 170 public string StockImage { 171 set { 172 image.Pixbuf = RenderIcon (value, IconSize.Button, null); 173 image.Show (); 174 } 175 } 176 177 public bool UseMarkup 178 { 179 get { return label.UseMarkup; } 180 set { label.UseMarkup = value; } 181 } 182 183 public string Markup { 184 set { label.Markup = value; } 185 } 186 } 187 MenuCreator(MenuButton button)188 delegate Menu MenuCreator (MenuButton button); 189 } 190