~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 
 
2
// DropDownBox.cs
 
3
//  
 
4
// Author:
 
5
//       Mike KrĆ¼ger <mkrueger@novell.com>
 
6
// 
 
7
// Copyright (c) 2009 Novell, Inc (http://www.novell.com)
 
8
// 
 
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
// of this software and associated documentation files (the "Software"), to deal
 
11
// in the Software without restriction, including without limitation the rights
 
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
// copies of the Software, and to permit persons to whom the Software is
 
14
// furnished to do so, subject to the following conditions:
 
15
// 
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
// 
 
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
25
// THE SOFTWARE.
 
26
 
 
27
using System;
 
28
using System.ComponentModel;
 
29
using Gtk;
 
30
 
 
31
namespace MonoDevelop.Components
 
32
{
 
33
        [Category ("Widgets")]
 
34
        [ToolboxItem (true)]
 
35
        public class DropDownBox : Gtk.Button
 
36
        {
 
37
                Pango.Layout layout;
 
38
                const int pixbufSpacing = 2;
 
39
                const int leftSpacing   = 2;
 
40
                const int ySpacing   = 1;
 
41
                
 
42
                public string Text {
 
43
                        get {
 
44
                                return layout.Text;
 
45
                        }
 
46
                        set {
 
47
                                layout.SetText (value);
 
48
//                              QueueResize ();
 
49
                        }
 
50
                }
 
51
                
 
52
                public DropDownBoxListWindow.IListDataProvider DataProvider {
 
53
                        get;
 
54
                        set;
 
55
                }
 
56
                
 
57
                public bool DrawRightBorder {
 
58
                        get;
 
59
                        set;
 
60
                }
 
61
                
 
62
                public Gdk.Pixbuf Pixbuf {
 
63
                        get;
 
64
                        set;
 
65
                }
 
66
                
 
67
                public object CurrentItem {
 
68
                        get;
 
69
                        set;
 
70
                }
 
71
                
 
72
                int defaultIconHeight, defaultIconWidth;
 
73
                
 
74
                /// <summary>
 
75
                /// This is so that the height doesn't jump around depending whether there's an icon assigned or not.
 
76
                /// </summary>
 
77
                public int DefaultIconHeight {
 
78
                        get { return defaultIconHeight; }
 
79
                        set {
 
80
                                defaultIconHeight = value;
 
81
                        }
 
82
                }
 
83
                
 
84
                public int DefaultIconWidth  {
 
85
                        get { return defaultIconWidth; }
 
86
                        set {
 
87
                                defaultIconWidth = value;
 
88
                        }
 
89
                }
 
90
                
 
91
                public DropDownBox ()
 
92
                {
 
93
                        layout = new Pango.Layout (this.PangoContext);
 
94
                        this.Events = Gdk.EventMask.KeyPressMask | Gdk.EventMask.FocusChangeMask | Gdk.EventMask.ButtonPressMask | Gdk.EventMask.ButtonReleaseMask | Gdk.EventMask.PointerMotionMask | Gdk.EventMask.LeaveNotifyMask;
 
95
                        this.CanFocus = true;
 
96
                        BorderWidth = 0;
 
97
                }
 
98
                
 
99
                void PositionListWindow ()
 
100
                {
 
101
                        if (window == null)
 
102
                                return;
 
103
                        int ox, oy;
 
104
                        ParentWindow.GetOrigin (out ox, out oy);
 
105
                        int dx = ox + this.Allocation.X;
 
106
                        int dy = oy + this.Allocation.Bottom;
 
107
                        window.WidthRequest = Allocation.Width;
 
108
                        int width, height;
 
109
                        window.GetSizeRequest (out width, out height);
 
110
                        Gdk.Rectangle geometry = Screen.GetMonitorGeometry (Screen.GetMonitorAtPoint (dx, dy));
 
111
                        
 
112
                        if (dy + height > geometry.Bottom)
 
113
                                dy = oy + this.Allocation.Y - height;
 
114
                        if (dx + width > geometry.Right)
 
115
                                dx = geometry.Right - width;
 
116
                        
 
117
                        window.Move (dx, dy);
 
118
                        window.GetSizeRequest (out width, out height);
 
119
                        window.GrabFocus ();
 
120
                }
 
121
                
 
122
                public void SetItem (string text, Gdk.Pixbuf icon, object currentItem)
 
123
                {
 
124
                        if (currentItem != CurrentItem) {// don't update when the same item is set.
 
125
                                this.Text = text;
 
126
                                this.CurrentItem = currentItem;
 
127
                                this.Pixbuf = icon;
 
128
                                this.QueueDraw ();
 
129
                        }
 
130
                        
 
131
                        if (ItemSet != null)
 
132
                                ItemSet (this, EventArgs.Empty);
 
133
                }
 
134
                
 
135
                public void SetItem (int i)
 
136
                {
 
137
                        SetItem (DataProvider.GetText (i), DataProvider.GetIcon (i), DataProvider.GetTag (i));
 
138
                }
 
139
                
 
140
                protected override void OnDestroyed ()
 
141
                {
 
142
                        DestroyWindow ();
 
143
                        if (layout != null) {
 
144
                                layout.Dispose ();
 
145
                                layout = null;
 
146
                        }
 
147
                        base.OnDestroyed ();
 
148
                }
 
149
                
 
150
                
 
151
                protected override void OnSizeRequested (ref Requisition requisition)
 
152
                {
 
153
                        int width, height;
 
154
                        layout.GetPixelSize (out width, out height);
 
155
                        
 
156
                        if (Pixbuf != null) {
 
157
                                width += Pixbuf.Width + pixbufSpacing * 2;
 
158
                                height = System.Math.Max (height, Pixbuf.Height);
 
159
                        } else {
 
160
                                height = System.Math.Max (height, defaultIconHeight);
 
161
                        }
 
162
                        
 
163
                        if (DrawRightBorder)
 
164
                                width += 2;
 
165
                        int arrowHeight = height / 2; 
 
166
                        int arrowWidth = arrowHeight + 1;
 
167
                        
 
168
                        requisition.Width = width + arrowWidth + leftSpacing;
 
169
                        requisition.Height = height + ySpacing * 2;
 
170
                }
 
171
                
 
172
                protected override bool OnFocusOutEvent (Gdk.EventFocus evnt)
 
173
                {
 
174
                        DestroyWindow ();
 
175
                        return base.OnFocusOutEvent (evnt);
 
176
                }
 
177
 
 
178
                
 
179
                DropDownBoxListWindow window = null;
 
180
                internal void DestroyWindow ()
 
181
                {
 
182
                        if (window != null) {
 
183
                                window.Destroy ();
 
184
                                window = null;
 
185
                                QueueDraw ();
 
186
                        }
 
187
                }
 
188
                
 
189
                protected override bool OnKeyPressEvent (Gdk.EventKey evnt)
 
190
                {
 
191
                        if (evnt.Key == Gdk.Key.Escape) {
 
192
                                DestroyWindow (); 
 
193
                                return true;
 
194
                        }
 
195
                        if (window != null && window.ProcessKey (evnt.Key, evnt.State))
 
196
                                return true;
 
197
                        return base.OnKeyPressEvent (evnt);
 
198
                }
 
199
                
 
200
                protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing evnt)
 
201
                {
 
202
                        QueueDraw ();
 
203
                        return base.OnLeaveNotifyEvent (evnt);
 
204
                }
 
205
                
 
206
                protected override bool OnButtonPressEvent (Gdk.EventButton e)
 
207
                {
 
208
                        if (e.Button == 3) {
 
209
                                return true;
 
210
                        }
 
211
                        if (e.Type == Gdk.EventType.ButtonPress) {
 
212
                                if (window != null) {
 
213
                                        DestroyWindow ();
 
214
                                } else {
 
215
                                        this.GrabFocus ();
 
216
                                        if (DataProvider != null) {
 
217
                                                DataProvider.Reset ();
 
218
                                                if (DataProvider.IconCount > 0) {
 
219
                                                        window = new DropDownBoxListWindow (DataProvider);
 
220
                                                        window.list.SelectItem += delegate {
 
221
                                                                SetItem (window.list.Selection);
 
222
                                                        };
 
223
                                                        PositionListWindow ();
 
224
                                                        window.SelectItem (CurrentItem);
 
225
                                                }
 
226
                                        }
 
227
                                }
 
228
                        }
 
229
                        return base.OnButtonPressEvent (e);
 
230
                }
 
231
                
 
232
                protected override void OnStateChanged (StateType previous_state)
 
233
                {
 
234
                        base.OnStateChanged (previous_state);
 
235
                }
 
236
 
 
237
                protected override bool OnButtonReleaseEvent (Gdk.EventButton e)
 
238
                {
 
239
                        return base.OnButtonReleaseEvent (e);
 
240
                }
 
241
                
 
242
                protected override bool OnMotionNotifyEvent (Gdk.EventMotion e)
 
243
                {
 
244
                        QueueDraw ();
 
245
                        return base.OnMotionNotifyEvent (e);
 
246
                }
 
247
        
 
248
                protected override bool OnExposeEvent (Gdk.EventExpose args)
 
249
                {
 
250
                        Gdk.Drawable win = args.Window;
 
251
                
 
252
                        int width, height;
 
253
                        layout.GetPixelSize (out width, out height);
 
254
                        
 
255
                        int arrowHeight = height / 2; 
 
256
                        int arrowWidth = arrowHeight + 1;
 
257
                        int arrowXPos = this.Allocation.X + this.Allocation.Width - arrowWidth;
 
258
                        if (DrawRightBorder)
 
259
                                arrowXPos -= 2;
 
260
                        
 
261
                        //HACK: don't ever draw insensitive, only active/prelight/normal, because insensitive generally looks really ugly
 
262
                        //this *might* cause some theme issues with the state of the text/arrows rendering on top of it
 
263
                        var state = window != null? StateType.Active
 
264
                                : State == StateType.Insensitive? StateType.Normal : State;
 
265
                        
 
266
                        //HACK: paint the button background as if it were bigger, but it stays clipped to the real area,
 
267
                        // so we get the content but not the border. This might break with crazy themes.
 
268
                        //FIXME: we can't use the style's actual internal padding because GTK# hasn't wrapped GtkBorder AFAICT
 
269
                        // (default-border, inner-border, default-outside-border, etc - see http://git.gnome.org/browse/gtk+/tree/gtk/gtkbutton.c)
 
270
                        const int padding = 4;
 
271
                        Style.PaintBox (Style, args.Window, state, ShadowType.None, args.Area, this, "button", 
 
272
                                        Allocation.X - padding, Allocation.Y - padding, Allocation.Width + padding * 2, Allocation.Height + padding * 2);
 
273
                        
 
274
                        int xPos = Allocation.Left;
 
275
                        if (Pixbuf != null) {
 
276
                                win.DrawPixbuf (this.Style.BaseGC (StateType.Normal), Pixbuf, 0, 0, xPos + pixbufSpacing, Allocation.Y + (Allocation.Height - Pixbuf.Height) / 2, Pixbuf.Width, Pixbuf.Height, Gdk.RgbDither.None, 0, 0);
 
277
                                xPos += Pixbuf.Width + pixbufSpacing * 2;
 
278
                        }
 
279
                        
 
280
                        //constrain the text area so it doesn't get rendered under the arrows
 
281
                        var textArea = new Gdk.Rectangle (xPos, Allocation.Y + ySpacing, arrowXPos - xPos - 2, Allocation.Height - ySpacing);
 
282
                        Style.PaintLayout (Style, win, state, true, textArea, this, "", textArea.X, textArea.Y, layout);
 
283
                        
 
284
                        state = Sensitive ? StateType.Normal : StateType.Insensitive;
 
285
                        Gtk.Style.PaintArrow (this.Style, win, state, ShadowType.None, args.Area, this, "", ArrowType.Up, true, arrowXPos, Allocation.Y + (Allocation.Height) / 2 - arrowHeight, arrowWidth, arrowHeight);
 
286
                        Gtk.Style.PaintArrow (this.Style, win, state, ShadowType.None, args.Area, this, "", ArrowType.Down, true, arrowXPos, Allocation.Y + (Allocation.Height) / 2, arrowWidth, arrowHeight);
 
287
                        if (DrawRightBorder)
 
288
                                win.DrawLine (this.Style.DarkGC (StateType.Normal), Allocation.X + Allocation.Width - 1, Allocation.Y, Allocation.X + Allocation.Width - 1, Allocation.Y + Allocation.Height);                  
 
289
                        return false;
 
290
                }
 
291
                
 
292
                public EventHandler ItemSet;
 
293
        }
 
294
}