/* Copyright (c) Citrix Systems Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using System.Reflection; using System.Runtime.InteropServices; using XenAdmin.Core; namespace XenAdmin.Controls { public class FlickerFreeListBox : ListBox { /// /// These flicker free list boxes must be owner drawn. /// public FlickerFreeListBox() { this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.ResizeRedraw, true); this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); this.SetStyle(ControlStyles.UserPaint, true); this.DoubleBuffered = true; this.IntegralHeight = false; this.DrawMode = DrawMode.OwnerDrawFixed; } /// /// The last seen value of the horizontal scrollbar position (offset in pixels from the lhs). /// private int _scrollLeft = 0; /// /// The handle of the horizontal scrollbar of this ListBox /// private IntPtr _hScrollbarHandle = IntPtr.Zero; public WndProcCancelDelegate CancelWndProc = new WndProcCancelDelegate(delegate(Message msg) { return false; }); protected override void WndProc(ref Message msg) { if (msg.Msg == Win32.WM_HSCROLL) { // Store the handle for use in Invalidate() _hScrollbarHandle = msg.HWnd; // Collect info about the position of the horizontal scrollbar Win32.ScrollInfo si = new Win32.ScrollInfo(); si.fMask = (int)Win32.ScrollInfoMask.SIF_ALL; si.cbSize = (uint)Marshal.SizeOf(si); Win32.GetScrollInfo(msg.HWnd, 0, ref si); if ((msg.WParam.ToInt32() & 0xFF) == Win32.SB_THUMBTRACK) { // If the user is in the middle of dragging the scrollbar, we're interested in // the 'track' position _scrollLeft = si.nTrackPos; } else { // Otherwise just the regular scrollbar position _scrollLeft = si.nPos; } // Force repaint base.Invalidate(); } if(!CancelWndProc(msg)) base.WndProc(ref msg); } public new void Invalidate() { if (_hScrollbarHandle != IntPtr.Zero) { // If we've been invalidated, update our drawing code to the new horizontal scrollbar // position (which is sometimes spontaneously reset to zero and can't be programmatically // set to its former value) - see CA-11405. Win32.ScrollInfo si = new Win32.ScrollInfo(); si.fMask = (int)Win32.ScrollInfoMask.SIF_ALL; si.cbSize = (uint)Marshal.SizeOf(si); Win32.GetScrollInfo(_hScrollbarHandle, 0, ref si); _scrollLeft = si.nTrackPos; // often zero } base.Invalidate(); } protected override void OnMouseWheel(MouseEventArgs e) { Invalidate(); base.OnMouseWheel(e); } private ContextMenuStrip _contextMenuStrip = null; public override ContextMenuStrip ContextMenuStrip { get { return _contextMenuStrip; } set { _contextMenuStrip = value; } } protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (e.KeyCode == Keys.Apps && _contextMenuStrip != null) { if (SelectedIndex < 0 || SelectedIndex >= Items.Count) return; Rectangle r = GetItemRectangle(SelectedIndex); Point p = new Point(r.X + 1, r.Bottom + 1); ContextMenuStrip.Show(this, p); } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (e.Button == MouseButtons.Right && _contextMenuStrip != null) { int i = IndexFromPoint(e.Location); if (i == ListBox.NoMatches) return; SelectedIndex = i; ContextMenuStrip.Show(this, e.Location); } } protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; if(Enabled) { using (SolidBrush backBrush = new SolidBrush(BackColor)) { g.FillRectangle(backBrush, g.ClipBounds); } } else { g.FillRectangle(SystemBrushes.Control, g.ClipBounds); } int top = 0; for (int i = 0; i < TopIndex; i++) { if (i < Items.Count) top += base.GetItemHeight(i); } Rectangle t = new Rectangle(-_scrollLeft, -top, ClientSize.Width, ItemHeight); for (int i = 0; i < this.Items.Count; i++) { int itemHeight = GetItemHeight(i); Rectangle bounds = new Rectangle(t.Left, t.Top, t.Width, itemHeight); // if selected if (SelectedIndices.Contains(i)) { // Enabled & Focused = blue // Enabled & !Focused = control // !Enabled = dark grey if(!Enabled) OnDrawItem(new DrawItemEventArgs(g, Font, bounds, i, DrawItemState.Disabled, SystemColors.HighlightText, SystemColors.ControlDark)); else if (!Focused) OnDrawItem(new DrawItemEventArgs(g, Font, bounds, i, DrawItemState.Inactive, SystemColors.ControlText, SystemColors.Control)); else OnDrawItem(new DrawItemEventArgs(g, Font, bounds, i, DrawItemState.Selected, SystemColors.HighlightText, SystemColors.Highlight)); } else { // If not selected, then we just care if the control is enabled OnDrawItem(new DrawItemEventArgs(g, Font, bounds, i, DrawItemState.Default, SystemColors.ControlText, Enabled ? BackColor : SystemColors.Control)); } t.Offset(0, itemHeight); } } protected override void OnSelectedIndexChanged(EventArgs e) { base.OnSelectedIndexChanged(e); Refresh(); } public const int RIGHT_PADDING = 5; public void WilkieSpecial(Image icon, string text, string extraText, Color extraTextColor, Font extraTextFont, DrawItemEventArgs e) { Graphics g = e.Graphics; WSPaintBG(e, this); // This is the rect where we want to put the text pair Rectangle bounds = new Rectangle(e.Bounds.Height, e.Bounds.Y, e.Bounds.Width - e.Bounds.Height - RIGHT_PADDING, e.Bounds.Height); String display; Size s = WSDrawTextPair(this, text, extraText, extraTextColor, extraTextFont, e, bounds, true, out display); WSDrawSelectRect(e, bounds.Height + s.Width); WSDrawLeftImage(icon, e); // And the text Drawing.DrawText(g, display, e.Font, bounds, e.ForeColor, e.BackColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter); } private static void WSDrawSelectRect(DrawItemEventArgs e, int width) { Graphics g = e.Graphics; Rectangle selectionRectangle = new Rectangle(e.Bounds.Location, e.Bounds.Size); selectionRectangle.Width = width; using (SolidBrush backBrush = new SolidBrush(e.BackColor)) { g.FillRectangle(backBrush, selectionRectangle); } } private static Color GetBackColorFor(Control control) { return !control.Enabled ? SystemColors.Control : control.BackColor; } private static void WSDrawLeftImage(Image icon, DrawItemEventArgs e) { Graphics g = e.Graphics; g.DrawImage(icon, e.Bounds.Left + 1, e.Bounds.Top + 1, e.Bounds.Height - 2, e.Bounds.Height - 2); } private static Size WSDrawTextPair(Control control, String text, String extraText, Color extraTextColor, Font extraTextFont, DrawItemEventArgs e, Rectangle bounds, bool ShowSelectOnExtraText, out String display) { Graphics g = e.Graphics; g.TextRenderingHint = Drawing.TextRenderingHint; display = text; Size s = Drawing.MeasureText(g, display, e.Font, bounds.Size, TextFormatFlags.Right | TextFormatFlags.VerticalCenter); if (extraText == null) return s; // We're going to have the extra text take precedent over the text, // so shrink the text and put ellipses on until it measures up Size t = Drawing.MeasureText(g, extraText, extraTextFont, bounds.Size, TextFormatFlags.Right | TextFormatFlags.VerticalCenter); int trim = text.Length; while (s.Width + t.Width + 10 > bounds.Width && trim > 0) { trim--; display = text.Ellipsise(trim); s = Drawing.MeasureText(g, display, e.Font, bounds.Size, TextFormatFlags.Right | TextFormatFlags.VerticalCenter); } Color backColor = ShowSelectOnExtraText && control.Focused && e.State == DrawItemState.Selected ? Color.LightGray : GetBackColorFor(control); if (ShowSelectOnExtraText) { Rectangle greySelectionRectangle = new Rectangle(bounds.Width - t.Width + bounds.X, bounds.Y + 2, t.Width, t.Height); using (SolidBrush brush = new SolidBrush(backColor)) { g.FillRectangle(brush, greySelectionRectangle); } } Drawing.DrawText(g, extraText, extraTextFont, bounds, extraTextColor, backColor, TextFormatFlags.Right | TextFormatFlags.VerticalCenter); return s; } private static void WSPaintBG(DrawItemEventArgs e, Control control) { Graphics g = e.Graphics; Color color = GetBackColorFor(control); using (SolidBrush backBrush = new SolidBrush(color)) { g.FillRectangle(backBrush, e.Bounds); } } protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); this.Refresh(); } protected override void OnLostFocus(EventArgs e) { base.OnLostFocus(e); this.Refresh(); } } public delegate bool WndProcCancelDelegate(Message msg); }