/* 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.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using XenAdmin.Actions; namespace XenAdmin.Controls { public partial class CustomHistoryContainer : Panel { /// /// List to keep track of refresh Timers to prevent them being GC'd. /// May only be accessed under the protection of the refreshTimersLock. /// private static readonly List refreshTimers = new List(); private static readonly object refreshTimersLock = new object(); public CustomHistoryContainer() { InitializeComponent(); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); BackColor = Color.Red; CustomHistoryPanel.Rows.CollectionChanged += Rows_CollectionChanged; } /// /// When a new row is added, we start a new timer that ticks each second and causes the panel to repaint. This ensures /// the 'time' field ticks smoothly. When the timer detects the Action has completed, it disposes itself. /// /// /// private void Rows_CollectionChanged(object sender, CollectionChangeEventArgs e) { if (Program.MainWindow != null) Program.BeginInvoke(Program.MainWindow, () => RowsChanged(e)); else RowsChanged(e); } private void RowsChanged(CollectionChangeEventArgs e) { ActionRow row = (ActionRow) e.Element; if (e.Action == CollectionChangeAction.Add && !row.Action.IsCompleted) { Timer refreshTimer = new Timer(); refreshTimer.Tick += refreshTick; refreshTimer.Interval = 1000; refreshTimer.Tag = row; // Add to list to prevent GCing lock (refreshTimersLock) { refreshTimers.Add(refreshTimer); } Form f = FindForm(); Program.Invoke(f ?? Program.MainWindow,refreshTimer.Start); } } void refreshTick(object obj, EventArgs args) { Program.AssertOnEventThread(); Timer timer = (Timer)obj; ActionRow row = (ActionRow)timer.Tag; bool redraw = !Disposing && !IsDisposed && !Program.Exiting; bool kill = row.Action == null || row.Action.IsCompleted || !redraw; if (redraw) { // Redraw panel CustomHistoryPanel.Invalidate(); CustomHistoryPanel.Refresh(); } if (kill) { // Kill timer lock (refreshTimersLock) { refreshTimers.Remove(timer); } timer.Stop(); timer.Dispose(); } } protected override void OnScroll(ScrollEventArgs se) { base.OnScroll(se); CustomHistoryPanel.ScrollTop = se.NewValue; } protected override void OnSizeChanged(EventArgs e) { base.OnSizeChanged(e); CustomHistoryPanel.ScrollTop = -this.AutoScrollPosition.Y; CustomHistoryPanel.Refresh(); } protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); CustomHistoryPanel.ScrollTop = -this.AutoScrollPosition.Y; CustomHistoryPanel.Refresh(); } } }