5
// Mike KrĆ¼ger <mkrueger@novell.com>
7
// Copyright (c) 2009 Novell, Inc (http://www.novell.com)
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:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
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
28
using System.ComponentModel;
31
namespace MonoDevelop.SourceEditor
33
[Category ("Widgets")]
35
public class DropDownBox : Gtk.Button
38
const int pixbufSpacing = 2;
39
const int leftSpacing = 2;
40
const int ySpacing = 1;
47
layout.SetText (value);
52
public DropDownBoxListWindow.IListDataProvider DataProvider {
57
public bool DrawRightBorder {
62
public Gdk.Pixbuf Pixbuf {
67
public object CurrentItem {
72
int defaultIconHeight, defaultIconWidth;
75
/// This is so that the height doesn't jump around depending whether there's an icon assigned or not.
77
public int DefaultIconHeight {
78
get { return defaultIconHeight; }
80
defaultIconHeight = value;
84
public int DefaultIconWidth {
85
get { return defaultIconWidth; }
87
defaultIconWidth = value;
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;
99
void PositionListWindow ()
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;
109
window.GetSizeRequest (out width, out height);
110
Gdk.Rectangle geometry = Screen.GetMonitorGeometry (Screen.GetMonitorAtPoint (dx, dy));
112
if (dy + height > geometry.Bottom)
113
dy = oy + this.Allocation.Y - height;
114
if (dx + width > geometry.Right)
115
dx = geometry.Right - width;
117
window.Move (dx, dy);
118
window.GetSizeRequest (out width, out height);
122
public void SetItem (string text, Gdk.Pixbuf icon, object currentItem)
124
if (currentItem != CurrentItem) {// don't update when the same item is set.
126
this.CurrentItem = currentItem;
132
ItemSet (this, EventArgs.Empty);
135
public void SetItem (int i)
137
SetItem (DataProvider.GetText (i), DataProvider.GetIcon (i), DataProvider.GetTag (i));
140
protected override void OnDestroyed ()
143
if (layout != null) {
151
protected override void OnSizeRequested (ref Requisition requisition)
154
layout.GetPixelSize (out width, out height);
156
if (Pixbuf != null) {
157
width += Pixbuf.Width + pixbufSpacing * 2;
158
height = System.Math.Max (height, Pixbuf.Height);
160
height = System.Math.Max (height, defaultIconHeight);
165
int arrowHeight = height / 2;
166
int arrowWidth = arrowHeight + 1;
168
requisition.Width = width + arrowWidth + leftSpacing;
169
requisition.Height = height + ySpacing * 2;
172
protected override bool OnFocusOutEvent (Gdk.EventFocus evnt)
175
return base.OnFocusOutEvent (evnt);
179
DropDownBoxListWindow window = null;
180
internal void DestroyWindow ()
182
if (window != null) {
189
protected override bool OnKeyPressEvent (Gdk.EventKey evnt)
191
if (evnt.Key == Gdk.Key.Escape) {
195
if (window != null && window.ProcessKey (evnt.Key, evnt.State))
197
return base.OnKeyPressEvent (evnt);
200
protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing evnt)
203
return base.OnLeaveNotifyEvent (evnt);
206
protected override bool OnButtonPressEvent (Gdk.EventButton e)
209
StatusBox.ShowNavigationBarContextMenu ();
212
if (e.Type == Gdk.EventType.ButtonPress) {
213
if (window != null) {
217
if (DataProvider != null) {
218
DataProvider.Reset ();
219
if (DataProvider.IconCount > 0) {
220
window = new DropDownBoxListWindow (this);
221
PositionListWindow ();
222
window.SelectItem (CurrentItem);
227
return base.OnButtonPressEvent (e);
230
protected override void OnStateChanged (StateType previous_state)
232
base.OnStateChanged (previous_state);
235
protected override bool OnButtonReleaseEvent (Gdk.EventButton e)
237
return base.OnButtonReleaseEvent (e);
240
protected override bool OnMotionNotifyEvent (Gdk.EventMotion e)
243
return base.OnMotionNotifyEvent (e);
246
protected override bool OnExposeEvent (Gdk.EventExpose args)
248
Gdk.Drawable win = args.Window;
251
layout.GetPixelSize (out width, out height);
253
int arrowHeight = height / 2;
254
int arrowWidth = arrowHeight + 1;
255
int arrowXPos = this.Allocation.X + this.Allocation.Width - arrowWidth;
259
//HACK: don't ever draw insensitive, only active/prelight/normal, because insensitive generally looks really ugly
260
//this *might* cause some theme issues with the state of the text/arrows rendering on top of it
261
var state = window != null? StateType.Active
262
: State == StateType.Insensitive? StateType.Normal : State;
264
//HACK: paint the button background as if it were bigger, but it stays clipped to the real area,
265
// so we get the content but not the border. This might break with crazy themes.
266
//FIXME: we can't use the style's actual internal padding because GTK# hasn't wrapped GtkBorder AFAICT
267
// (default-border, inner-border, default-outside-border, etc - see http://git.gnome.org/browse/gtk+/tree/gtk/gtkbutton.c)
268
const int padding = 4;
269
Style.PaintBox (Style, args.Window, state, ShadowType.None, args.Area, this, "button",
270
Allocation.X - padding, Allocation.Y - padding, Allocation.Width + padding * 2, Allocation.Height + padding * 2);
272
int xPos = Allocation.Left;
273
if (Pixbuf != null) {
274
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);
275
xPos += Pixbuf.Width + pixbufSpacing * 2;
278
//constrain the text area so it doesn't get rendered under the arrows
279
var textArea = new Gdk.Rectangle (xPos, Allocation.Y + ySpacing, arrowXPos - xPos - 2, Allocation.Height - ySpacing);
280
Style.PaintLayout (Style, win, state, true, textArea, this, "", textArea.X, textArea.Y, layout);
282
state = Sensitive ? StateType.Normal : StateType.Insensitive;
283
Gtk.Style.PaintArrow (this.Style, win, state, ShadowType.None, args.Area, this, "", ArrowType.Up, true, arrowXPos, Allocation.Y + (Allocation.Height) / 2 - arrowHeight, arrowWidth, arrowHeight);
284
Gtk.Style.PaintArrow (this.Style, win, state, ShadowType.None, args.Area, this, "", ArrowType.Down, true, arrowXPos, Allocation.Y + (Allocation.Height) / 2, arrowWidth, arrowHeight);
286
win.DrawLine (this.Style.DarkGC (StateType.Normal), Allocation.X + Allocation.Width - 1, Allocation.Y, Allocation.X + Allocation.Width - 1, Allocation.Y + Allocation.Height);
290
public EventHandler ItemSet;