//-------------------------------------------------------------------------------------
// SID Monitor - Utility for Sudden Ionospheric Disturbances Monitoring Stations
// Copyright (C) 2006 - Lionel Loudet
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//-------------------------------------------------------------------------------------


using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.IO;

namespace PictureViewer
{

    /// <summary>
    /// Summary description for Viewer.
    /// </summary>
    public class Viewer : System.Windows.Forms.UserControl
    {
        private System.ComponentModel.IContainer components;

        #region Constants And Variables

        // the outputTextBoxForm to use to display messages
        private IntPtr outputTextBoxFormHandle;

        // stores the last image file that has been displayes in the PictureBox (for the "save as" method)
        private string LastDisplayedImageName = String.Empty;

        // Image Size
        private uint ImageWidth = SID_monitor.Properties.Settings.Default.GraphDefaultWidth;
        private uint ImageHeight = SID_monitor.Properties.Settings.Default.GraphDefaultHeight;

        // Zoom Factor
        private float ZOOMFACTOR = (float)Math.Sqrt(Math.Sqrt(2));	// = smaller or larger coef
        private float MINMAX = SID_monitor.Properties.Settings.Default.MaxZoomRatio; // max zoom value
        private float currentRatio = 1.0F;                          // holds the current zoom value

        // Cursors
        private Cursor openHandCursor;
        private Cursor closedHandCursor;

        // Forms
        private VScrollBar vScrollBar1;
        private HScrollBar hScrollBar1;
        private Panel panelCorner;
        private Label labelZoom;
        private ContextMenuStrip contextMenuStripGraph;
        private ToolStripSeparator toolStripSeparator8;
        private ToolStripMenuItem toolStripMenuItemIncreaseImageSize;
        private ToolStripMenuItem toolStripMenuItemDecreaseImageSize;
        private ToolStripMenuItem toolStripMenuItemDefaultImageSize;
        private ToolStripSeparator toolStripSeparator9;
        private SaveFileDialog saveFileDialogGraphImage;

        private int startMouseMoveX = 0; // initial x position of the mouse when moving image
        private int startMouseMoveY = 0; // initial y position of the mouse when moving image

        // Graph Parameters save
        string OldChannelsDatabaseName = String.Empty;
        string OldGOESDatabaseName = String.Empty;
        string OldGraphStartTime = String.Empty;
        string OldGraphEndTime = String.Empty;
        string OldGraphTitle = String.Empty;
        bool OldShowSun = false;
        bool OldShowForecast = false;
        string[] OldChannelStationCodes = null;
        string[] OldChannelTitles = null;
        string[] OldChannelLineWidths = null;
        string[] OldChannelColors = null;
        string[] OldChannelDev1Colors = null;
        string[] OldChannelDev2Colors = null;
        string[] OldChannelDev3Colors = null;
        bool[] OldChannelsToShow = null;
        string[] OldGOESTitles = null;
        string[] OldGOESLineWidths = null;
        string[] OldGOESColors = null;
        bool[] OldGOESToShow = null;

        private ToolStripMenuItem toolStripMenuItemZoomIn;
        private ToolStripMenuItem toolStripMenuItemZoomOut;
        private ToolStripMenuItem toolStripMenuItemZoomDefault;
        private ToolStripMenuItem toolStripMenuItemSaveAsPNG;
        private PictureBox pictureBox;
        private ToolStripMenuItem updateNowToolStripMenuItem;
        private ToolStripSeparator toolStripSeparator1;

        #endregion

        public Viewer()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();
            this.panelCorner.Visible = false;
            this.labelZoom.Visible = false;
            this.AutoSize = false;
            this.Focus();
            this.pictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
            this.MouseWheel += new MouseEventHandler(Viewer_MouseWheel);
            this.SetLayout(Point.Empty);

            // hand cursors
            MemoryStream openHandStream = new MemoryStream(SID_monitor.Properties.Resources.openhand);
            MemoryStream closedHandStream = new MemoryStream(SID_monitor.Properties.Resources.closedhand);
            openHandCursor = new Cursor(openHandStream);
            closedHandCursor = new Cursor(closedHandStream);


            // contextual menu initialisation
            toolStripMenuItemZoomIn.Enabled = this.OkToZoomIn();
            toolStripMenuItemZoomOut.Enabled = this.OkToZoomOut();
            toolStripMenuItemIncreaseImageSize.Enabled = this.OkToIncreaseImageSize();
            toolStripMenuItemDecreaseImageSize.Enabled = this.OkToDecreaseImageSize();

        }

        #region Viewer Methods
        /// <summary>
        /// Loads default image at init
        /// </summary>
        private void Viewer_Load(object sender, EventArgs e)
        {
            // set the form that will display messages. By default, the MainForm one.
            outputTextBoxFormHandle = SID_monitor.Program.MainForm.outputTextBoxDockablePanel.Handle;

            if (String.IsNullOrEmpty(this.ImageLocation)) // no image location has been specified, we show a default image
            {
                // initialize default Image
                this.pictureBox.Image = SID_monitor.Properties.Resources.no_image_available_300;

                // disable "save" button
                toolStripMenuItemSaveAsPNG.Enabled = false;

                // raise GraphFileChanged event with a "file invalid" status
                GraphFileChangeEventArgs ev = new GraphFileChangeEventArgs(false);
                OnGraphFileChanged(ev);
            }
        }
        #endregion

        #region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Viewer));
            this.vScrollBar1 = new System.Windows.Forms.VScrollBar();
            this.hScrollBar1 = new System.Windows.Forms.HScrollBar();
            this.panelCorner = new System.Windows.Forms.Panel();
            this.labelZoom = new System.Windows.Forms.Label();
            this.contextMenuStripGraph = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.toolStripMenuItemZoomIn = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemZoomOut = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemZoomDefault = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator();
            this.toolStripMenuItemIncreaseImageSize = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemDecreaseImageSize = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemDefaultImageSize = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripSeparator9 = new System.Windows.Forms.ToolStripSeparator();
            this.updateNowToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
            this.toolStripMenuItemSaveAsPNG = new System.Windows.Forms.ToolStripMenuItem();
            this.saveFileDialogGraphImage = new System.Windows.Forms.SaveFileDialog();
            this.pictureBox = new System.Windows.Forms.PictureBox();
            this.contextMenuStripGraph.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit();
            this.SuspendLayout();
            // 
            // vScrollBar1
            // 
            this.vScrollBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.vScrollBar1.Location = new System.Drawing.Point(680, 0);
            this.vScrollBar1.Name = "vScrollBar1";
            this.vScrollBar1.Size = new System.Drawing.Size(18, 491);
            this.vScrollBar1.TabIndex = 1;
            this.vScrollBar1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vScrollBar1_Scroll);
            // 
            // hScrollBar1
            // 
            this.hScrollBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.hScrollBar1.Location = new System.Drawing.Point(0, 492);
            this.hScrollBar1.Name = "hScrollBar1";
            this.hScrollBar1.Size = new System.Drawing.Size(681, 18);
            this.hScrollBar1.TabIndex = 2;
            this.hScrollBar1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.hScrollBar1_Scroll);
            // 
            // panelCorner
            // 
            this.panelCorner.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.panelCorner.Location = new System.Drawing.Point(680, 492);
            this.panelCorner.Name = "panelCorner";
            this.panelCorner.Size = new System.Drawing.Size(18, 18);
            this.panelCorner.TabIndex = 3;
            // 
            // labelZoom
            // 
            this.labelZoom.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.labelZoom.AutoSize = true;
            this.labelZoom.Location = new System.Drawing.Point(606, 473);
            this.labelZoom.Name = "labelZoom";
            this.labelZoom.Size = new System.Drawing.Size(69, 13);
            this.labelZoom.TabIndex = 4;
            this.labelZoom.Text = " Zoom: 1.00x";
            this.labelZoom.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
            // 
            // contextMenuStripGraph
            // 
            this.contextMenuStripGraph.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.toolStripMenuItemZoomIn,
            this.toolStripMenuItemZoomOut,
            this.toolStripMenuItemZoomDefault,
            this.toolStripSeparator8,
            this.toolStripMenuItemIncreaseImageSize,
            this.toolStripMenuItemDecreaseImageSize,
            this.toolStripMenuItemDefaultImageSize,
            this.toolStripSeparator9,
            this.updateNowToolStripMenuItem,
            this.toolStripSeparator1,
            this.toolStripMenuItemSaveAsPNG});
            this.contextMenuStripGraph.Name = "contextMenuStripGraph";
            this.contextMenuStripGraph.Size = new System.Drawing.Size(217, 198);
            // 
            // toolStripMenuItemZoomIn
            // 
            this.toolStripMenuItemZoomIn.AutoToolTip = true;
            this.toolStripMenuItemZoomIn.Image = ((System.Drawing.Image)(resources.GetObject("toolStripMenuItemZoomIn.Image")));
            this.toolStripMenuItemZoomIn.Name = "toolStripMenuItemZoomIn";
            this.toolStripMenuItemZoomIn.Size = new System.Drawing.Size(216, 22);
            this.toolStripMenuItemZoomIn.Text = "&Zoom In";
            this.toolStripMenuItemZoomIn.ToolTipText = "Zoom In the graph";
            this.toolStripMenuItemZoomIn.Click += new System.EventHandler(this.toolStripMenuItemZoomIn_Click);
            // 
            // toolStripMenuItemZoomOut
            // 
            this.toolStripMenuItemZoomOut.AutoToolTip = true;
            this.toolStripMenuItemZoomOut.Image = global::SID_monitor.Properties.Resources.zoom_out;
            this.toolStripMenuItemZoomOut.Name = "toolStripMenuItemZoomOut";
            this.toolStripMenuItemZoomOut.Size = new System.Drawing.Size(216, 22);
            this.toolStripMenuItemZoomOut.Text = "Zoom &Out";
            this.toolStripMenuItemZoomOut.ToolTipText = "Zoom Out the graph";
            this.toolStripMenuItemZoomOut.Click += new System.EventHandler(this.toolStripMenuItemZoomOut_Click);
            // 
            // toolStripMenuItemZoomDefault
            // 
            this.toolStripMenuItemZoomDefault.AutoToolTip = true;
            this.toolStripMenuItemZoomDefault.Image = global::SID_monitor.Properties.Resources.zoom_default;
            this.toolStripMenuItemZoomDefault.Name = "toolStripMenuItemZoomDefault";
            this.toolStripMenuItemZoomDefault.Size = new System.Drawing.Size(216, 22);
            this.toolStripMenuItemZoomDefault.Text = "Zoom &1:1";
            this.toolStripMenuItemZoomDefault.ToolTipText = "Restores the zoom factor to 1:1";
            this.toolStripMenuItemZoomDefault.Click += new System.EventHandler(this.toolStripMenuItemZoomDefault_Click);
            // 
            // toolStripSeparator8
            // 
            this.toolStripSeparator8.Name = "toolStripSeparator8";
            this.toolStripSeparator8.Size = new System.Drawing.Size(213, 6);
            // 
            // toolStripMenuItemIncreaseImageSize
            // 
            this.toolStripMenuItemIncreaseImageSize.AutoToolTip = true;
            this.toolStripMenuItemIncreaseImageSize.Image = global::SID_monitor.Properties.Resources.size_increase;
            this.toolStripMenuItemIncreaseImageSize.Name = "toolStripMenuItemIncreaseImageSize";
            this.toolStripMenuItemIncreaseImageSize.Size = new System.Drawing.Size(216, 22);
            this.toolStripMenuItemIncreaseImageSize.Text = "&Increase Image Size";
            this.toolStripMenuItemIncreaseImageSize.ToolTipText = "Increase the graph size";
            this.toolStripMenuItemIncreaseImageSize.Click += new System.EventHandler(this.toolStripMenuItemIncreaseImageSize_Click);
            // 
            // toolStripMenuItemDecreaseImageSize
            // 
            this.toolStripMenuItemDecreaseImageSize.AutoToolTip = true;
            this.toolStripMenuItemDecreaseImageSize.Image = global::SID_monitor.Properties.Resources.size_decrease;
            this.toolStripMenuItemDecreaseImageSize.Name = "toolStripMenuItemDecreaseImageSize";
            this.toolStripMenuItemDecreaseImageSize.Size = new System.Drawing.Size(216, 22);
            this.toolStripMenuItemDecreaseImageSize.Text = "&Decrease Image Size";
            this.toolStripMenuItemDecreaseImageSize.ToolTipText = "Decrease the graph size";
            this.toolStripMenuItemDecreaseImageSize.Click += new System.EventHandler(this.toolStripMenuItemDecreaseImageSize_Click);
            // 
            // toolStripMenuItemDefaultImageSize
            // 
            this.toolStripMenuItemDefaultImageSize.AutoToolTip = true;
            this.toolStripMenuItemDefaultImageSize.Image = global::SID_monitor.Properties.Resources.size_default;
            this.toolStripMenuItemDefaultImageSize.Name = "toolStripMenuItemDefaultImageSize";
            this.toolStripMenuItemDefaultImageSize.Size = new System.Drawing.Size(216, 22);
            this.toolStripMenuItemDefaultImageSize.Text = "D&efault Image Size";
            this.toolStripMenuItemDefaultImageSize.ToolTipText = "Restores the graph size to the default size";
            this.toolStripMenuItemDefaultImageSize.Click += new System.EventHandler(this.toolStripMenuItemDefaultImageSize_Click);
            // 
            // toolStripSeparator9
            // 
            this.toolStripSeparator9.Name = "toolStripSeparator9";
            this.toolStripSeparator9.Size = new System.Drawing.Size(213, 6);
            // 
            // updateNowToolStripMenuItem
            // 
            this.updateNowToolStripMenuItem.Image = global::SID_monitor.Properties.Resources.icon_refresh;
            this.updateNowToolStripMenuItem.Name = "updateNowToolStripMenuItem";
            this.updateNowToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.U)));
            this.updateNowToolStripMenuItem.Size = new System.Drawing.Size(216, 22);
            this.updateNowToolStripMenuItem.Text = "&Update Now";
            this.updateNowToolStripMenuItem.ToolTipText = "Update the graph now";
            this.updateNowToolStripMenuItem.Click += new System.EventHandler(this.updateNowToolStripMenuItem_Click);
            // 
            // toolStripSeparator1
            // 
            this.toolStripSeparator1.Name = "toolStripSeparator1";
            this.toolStripSeparator1.Size = new System.Drawing.Size(213, 6);
            // 
            // toolStripMenuItemSaveAsPNG
            // 
            this.toolStripMenuItemSaveAsPNG.AutoToolTip = true;
            this.toolStripMenuItemSaveAsPNG.Image = global::SID_monitor.Properties.Resources.save;
            this.toolStripMenuItemSaveAsPNG.Name = "toolStripMenuItemSaveAsPNG";
            this.toolStripMenuItemSaveAsPNG.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S)));
            this.toolStripMenuItemSaveAsPNG.Size = new System.Drawing.Size(216, 22);
            this.toolStripMenuItemSaveAsPNG.Text = "&Save Graph as PNG";
            this.toolStripMenuItemSaveAsPNG.ToolTipText = "Save the current graph as a PNG file";
            this.toolStripMenuItemSaveAsPNG.Click += new System.EventHandler(this.toolStripMenuItemSaveAsPNG_Click);
            // 
            // saveFileDialogGraphImage
            // 
            this.saveFileDialogGraphImage.DefaultExt = "png";
            this.saveFileDialogGraphImage.Filter = "PNG files|*.png|All files|*.*";
            this.saveFileDialogGraphImage.Title = "Select PNG filename";
            // 
            // pictureBox
            // 
            this.pictureBox.Cursor = System.Windows.Forms.Cursors.Default;
            this.pictureBox.Location = new System.Drawing.Point(0, 0);
            this.pictureBox.Name = "pictureBox";
            this.pictureBox.Size = new System.Drawing.Size(352, 272);
            this.pictureBox.TabIndex = 0;
            this.pictureBox.TabStop = false;
            this.pictureBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseDown);
            this.pictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseMove);
            this.pictureBox.LoadCompleted += new System.ComponentModel.AsyncCompletedEventHandler(this.pictureBox1_LoadCompleted);
            this.pictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseUp);
            this.pictureBox.MouseEnter += new System.EventHandler(this.Viewer_MouseEnter);
            // 
            // Viewer
            // 
            this.BackColor = System.Drawing.SystemColors.Control;
            this.ContextMenuStrip = this.contextMenuStripGraph;
            this.Controls.Add(this.panelCorner);
            this.Controls.Add(this.hScrollBar1);
            this.Controls.Add(this.vScrollBar1);
            this.Controls.Add(this.labelZoom);
            this.Controls.Add(this.pictureBox);
            this.Name = "Viewer";
            this.Size = new System.Drawing.Size(698, 510);
            this.Load += new System.EventHandler(this.Viewer_Load);
            this.Resize += new System.EventHandler(this.Viewer_Resize);
            this.MouseEnter += new System.EventHandler(this.Viewer_MouseEnter);
            this.contextMenuStripGraph.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }
        #endregion

        #region Properties
        public IntPtr OutputTextBoxFormHandle
        {
            get
            {
                return this.outputTextBoxFormHandle;
            }
            set
            {
                if (value != IntPtr.Zero)
                {
                    this.outputTextBoxFormHandle = value;
                }
                else
                {
                    this.outputTextBoxFormHandle = SID_monitor.Program.MainForm.outputTextBoxDockablePanel.Handle;
                }
            }
        }

        public Image Image
        {
            get { return this.pictureBox.Image; }
            set
            {
                this.pictureBox.Image = value;
                this.SetLayout(Point.Empty);
            }
        }

        public string ImageLocation
        {
            get { return this.pictureBox.ImageLocation; }
            set
            {
                this.pictureBox.ImageLocation = value;
                this.SetLayout(Point.Empty);
            }
        }

        #endregion

        #region Layout Methods

        private void SetLayout(Point p)
        {
            if (this.pictureBox.Image == null)
                return;

            this.pictureBox.Width = Convert.ToInt32(this.pictureBox.Image.Width * currentRatio);
            this.pictureBox.Height = Convert.ToInt32(this.pictureBox.Image.Height * currentRatio);

            if (Math.Round(currentRatio * 100) != 100)
            {
                this.labelZoom.Text = " Zoom: " + currentRatio.ToString("f2") + "x";
                this.labelZoom.Visible = true;
            }
            else
            {
                this.labelZoom.Visible = false;
            }

            this.SetScrollBars();
            this.CenterImage(p);
            this.SetCursor();
        }


        private void SetScrollBars()
        {
            bool wide1 = false; // wider than panel
            bool wide2 = false; // wider than panel with vscrollbar
            bool tall1 = false; // taller than panel
            bool tall2 = false; // taller than panel with hscrollbar

            if (this.pictureBox.Width >= this.Width) { wide1 = true; }
            if (this.pictureBox.Width >= this.Width - this.vScrollBar1.Width) { wide2 = true; }
            if (this.pictureBox.Height >= this.Height) { tall1 = true; }
            if (this.pictureBox.Height >= this.Height - this.hScrollBar1.Height) { tall2 = true; }

            this.hScrollBar1.Visible = false;
            this.vScrollBar1.Visible = false;
            this.panelCorner.Visible = false;

            if ((wide1 && tall2) || (wide2 && tall1))
            {
                this.hScrollBar1.Width = this.Width - this.vScrollBar1.Width;
                this.vScrollBar1.Height = this.Height - this.hScrollBar1.Height;

                this.hScrollBar1.Minimum = 0;
                this.hScrollBar1.Maximum = this.pictureBox.Width + this.vScrollBar1.Width;
                this.hScrollBar1.LargeChange = this.Width;
                this.hScrollBar1.SmallChange = this.hScrollBar1.LargeChange / 20;
                this.vScrollBar1.Minimum = 0;
                this.vScrollBar1.Maximum = this.pictureBox.Height + this.hScrollBar1.Height;
                this.vScrollBar1.LargeChange = this.Height;
                this.vScrollBar1.SmallChange = this.vScrollBar1.LargeChange / 20;

                this.hScrollBar1.Visible = true;
                this.vScrollBar1.Visible = true;
                this.panelCorner.Visible = true;
            }
            else if (wide1 && !tall2)
            {
                this.hScrollBar1.Width = this.Width;
                this.hScrollBar1.Visible = true;

                this.hScrollBar1.Minimum = 0;
                this.hScrollBar1.Maximum = this.pictureBox.Width;
                this.hScrollBar1.LargeChange = this.Width;
                this.hScrollBar1.SmallChange = this.hScrollBar1.LargeChange / 20;
            }
            else if (!wide2 && tall1)
            {
                this.vScrollBar1.Height = this.Height;
                this.vScrollBar1.Visible = true;

                this.vScrollBar1.Minimum = 0;
                this.vScrollBar1.Maximum = this.pictureBox.Height;
                this.vScrollBar1.LargeChange = this.Height;
                this.vScrollBar1.SmallChange = this.vScrollBar1.LargeChange / 20;
            }


        }

        /// <summary>
        /// Centers the whole image in the Viewer panel if it is smaller, or around the given point if wider
        /// </summary>
        /// <param name="p"></param>
        private void CenterImage(Point p)
        {
            if (!((this.hScrollBar1.Visible == true) && (this.vScrollBar1.Visible == true)))
            { // image is smaller than Viewer panel
                // we center it in the panel
                int top;
                int left;
                if (this.hScrollBar1.Visible == true)
                {
                    top = Convert.ToInt32((this.Height - this.hScrollBar1.Height - this.pictureBox.Height) / 2.0);
                }
                else
                {
                    top = Convert.ToInt32((this.Height - this.pictureBox.Height) / 2.0);
                }
                if (this.vScrollBar1.Visible == true)
                {
                    left = Convert.ToInt32((this.Width - this.vScrollBar1.Width - this.pictureBox.Width) / 2.0);
                }
                else
                {
                    left = Convert.ToInt32((this.Width - this.pictureBox.Width) / 2.0);
                }

                if (top < 0)
                    top = 0;
                if (left < 0)
                    left = 0;
                this.pictureBox.Top = top;
                this.pictureBox.Left = left;

            }
            else
            {
                if (p != Point.Empty) // reference point is provided
                {
                    // image is larger, we scroll it so that the image point p is at top left of the image

                    int scrollX = p.X;
                    int scrollY = p.Y;

                    // check for minimum value
                    if (scrollX < this.hScrollBar1.Minimum) { scrollX = this.hScrollBar1.Minimum; }
                    if (scrollY < this.vScrollBar1.Minimum) { scrollY = this.vScrollBar1.Minimum; }

                    // check for maximum value
                    if ((scrollX > this.pictureBox.Width - this.Width + this.vScrollBar1.Width) && (this.vScrollBar1.Visible == true))
                    {
                        scrollX = this.pictureBox.Width - this.Width + this.vScrollBar1.Width;
                    }
                    else if ((scrollX > this.pictureBox.Width - this.Width) && (this.vScrollBar1.Visible == false))
                    {
                        scrollX = this.pictureBox.Width - this.Width;
                    }
                    if ((scrollY > this.pictureBox.Height - this.Height + this.hScrollBar1.Height) && (this.hScrollBar1.Visible == true))
                    {
                        scrollY = this.pictureBox.Height - this.Height + this.hScrollBar1.Height;
                    }
                    else if ((scrollY > this.pictureBox.Height - this.Height) && (this.hScrollBar1.Visible == false))
                    {
                        scrollY = this.pictureBox.Height - this.Height;
                    }

                    // scroll the image
                    this.hScrollBar1.Value = scrollX;
                    this.vScrollBar1.Value = scrollY;
                    this.pictureBox.Left = -this.hScrollBar1.Value;
                    this.pictureBox.Top = -this.vScrollBar1.Value;
                }
                else
                {
                    this.pictureBox.Left = -this.hScrollBar1.Value;
                    this.pictureBox.Top = -this.vScrollBar1.Value;
                }
            }

        }


        /// <summary>
        /// Set the cursor shape according to the scrollbars
        /// </summary>
        private void SetCursor()
        {
            // set the cursor
            if ((this.hScrollBar1.Visible == true) || (this.vScrollBar1.Visible == true))
            {
                this.pictureBox.Cursor = openHandCursor;
            }
            else  // ( this.hScrollBar1.Visible == false ) && ( this.vScrollBar1.Visible == false ) )
            {
                this.pictureBox.Cursor = Cursors.Cross;
            }
        }
        #endregion

        #region Viewer Events
        /// <summary>
        /// Relayouts when size changes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Viewer_Resize(object sender, System.EventArgs e)
        {
            this.SetLayout(Point.Empty);
        }
        #endregion

        #region PictureBox Events

        /// <summary>
        /// Relayout when a new image is loaded
        /// </summary>
        private void pictureBox1_LoadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            this.SetLayout(Point.Empty);
        }
        #endregion

        #region Zooming Methods

        /// <summary>
        /// Indicates wether or not the graph can be zoomed in
        /// Maximum 5 times bigger.
        /// </summary>
        /// <returns></returns>
        public bool OkToZoomIn()
        {
            return (currentRatio * ZOOMFACTOR <= MINMAX);
        }

        /// <summary>
        /// Indicates wether or not the graph can be zoomed out
        /// Minimum 5 times smaller. Image cannot be smaller than panel, except if already zoomed.
        /// </summary>
        /// <returns></returns>
        public bool OkToZoomOut()
        {
            return ((currentRatio > 1.0) || ((currentRatio / ZOOMFACTOR >= 1 / MINMAX) &&
                            ((this.pictureBox.Width > this.Width) || (this.pictureBox.Height > this.Height))));
        }

        /// <summary>
        /// Make the PictureBox dimensions larger to effect the Zoom.
        /// </summary>
        public void ZoomIn(Point CursorPosition)
        {
            if (OkToZoomIn() == true)
            {
                currentRatio *= ZOOMFACTOR;
                this.SetLayout(new Point(Convert.ToInt32(CursorPosition.X * (ZOOMFACTOR - 1) - this.pictureBox.Left * ZOOMFACTOR),
                                         Convert.ToInt32(CursorPosition.Y * (ZOOMFACTOR - 1) - this.pictureBox.Top * ZOOMFACTOR)));
            }
        }

        /// <summary>
        /// Make the PictureBox dimensions smaller to effect the Zoom.
        /// </summary>
        public void ZoomOut(Point CursorPosition)
        {
            if (OkToZoomOut() == true)
            {
                currentRatio /= ZOOMFACTOR;
                this.SetLayout(new Point(Convert.ToInt32(CursorPosition.X * (1 / ZOOMFACTOR - 1) - this.pictureBox.Left / ZOOMFACTOR),
                                         Convert.ToInt32(CursorPosition.Y * (1 / ZOOMFACTOR - 1) - this.pictureBox.Top / ZOOMFACTOR)));
            }
        }


        /// <summary>
        /// Resets the zoom value to default (no zoom)
        /// </summary>
        public void ZoomDefault()
        {
            this.currentRatio = 1.0F;
            this.SetLayout(Point.Empty);
        }

        #endregion

        #region ImageSize Methods

        /// <summary>
        /// Indicates wether or not the graph size can be increased
        /// </summary>
        public bool OkToIncreaseImageSize()
        {
            return (ImageWidth * ZOOMFACTOR <= SID_monitor.Properties.Settings.Default.MaxGraphWidth);
        }

        /// <summary>
        /// Indicates wether or not the graph size can be decreased
        /// </summary>
        public bool OkToDecreaseImageSize()
        {
            return (ImageWidth / ZOOMFACTOR >= SID_monitor.Properties.Settings.Default.MinGraphWidth);
        }

        /// <summary>
        /// Increases Image Size.
        /// </summary>
        public void IncreaseImageSize()
        {
            if (OkToIncreaseImageSize() == true)
            {
                ImageWidth = (uint)(ImageWidth * ZOOMFACTOR);
                ImageHeight = (uint)(ImageHeight * ZOOMFACTOR);
                UpdateGraph();
            }
        }

        /// <summary>
        /// Decreases Image Size
        /// </summary>
        public void DecreaseImageSize()
        {
            if (OkToDecreaseImageSize() == true)
            {
                ImageWidth = (uint)(ImageWidth / ZOOMFACTOR);
                ImageHeight = (uint)(ImageHeight / ZOOMFACTOR);
                UpdateGraph();
            }
        }


        /// <summary>
        /// Resets the image size to default
        /// </summary>
        public void DefaultImageSize()
        {
            ImageWidth = SID_monitor.Properties.Settings.Default.GraphDefaultWidth;
            ImageHeight = SID_monitor.Properties.Settings.Default.GraphDefaultHeight;
            UpdateGraph();
        }

        #endregion

        #region Scrollbars Events
        private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            this.pictureBox.Top = -e.NewValue;
        }

        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            this.pictureBox.Left = -e.NewValue;
        }
        #endregion

        #region Mouse events

        /// <summary>
        /// We use the mousewheel to zoom the picture in or out
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Viewer_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta < 0)
            {
                ZoomIn(e.Location);
            }
            else
            {
                ZoomOut(e.Location);
            }
        }


        /// <summary>
        /// Make sure that the pictureBox1 has the focus, otherwise it doesnīt receive 
        /// mousewheel events.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Viewer_MouseEnter(object sender, EventArgs e)
        {
            if (this.Focused == false)
            {
                this.Focus();
            }
        }

        /// <summary>
        /// Scrolls the the image to follow the mouse when left button is pressed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (this.hScrollBar1.Visible == true) // horizontal scrolling
                {
                    if ((this.hScrollBar1.Value - (e.X - this.startMouseMoveX) < 0))
                    {
                        this.hScrollBar1.Value = 0;
                    }
                    else if ((this.hScrollBar1.Value - (e.X - this.startMouseMoveX) > (this.pictureBox.Width - this.Width + this.vScrollBar1.Width))
                              && (this.vScrollBar1.Visible == true))
                    {
                        this.hScrollBar1.Value = this.pictureBox.Width - this.Width + this.vScrollBar1.Width;
                    }
                    else if ((this.hScrollBar1.Value - (e.X - this.startMouseMoveX) > (this.pictureBox.Width - this.Width))
                         && (this.vScrollBar1.Visible == false))
                    {
                        this.hScrollBar1.Value = this.pictureBox.Width - this.Width;
                    }
                    else
                    {
                        this.hScrollBar1.Value -= e.X - this.startMouseMoveX;
                    }

                    this.pictureBox.Left = -this.hScrollBar1.Value;
                }

                if (this.vScrollBar1.Visible == true)
                {
                    if ((this.vScrollBar1.Value - (e.Y - this.startMouseMoveY) < 0))
                    {
                        this.vScrollBar1.Value = 0;
                    }
                    else if ((this.vScrollBar1.Value - (e.Y - this.startMouseMoveY) > (this.pictureBox.Height - this.Height + this.hScrollBar1.Height))
                              && (this.hScrollBar1.Visible == true))
                    {
                        this.vScrollBar1.Value = this.pictureBox.Height - this.Height + this.hScrollBar1.Height;
                    }
                    else if ((this.vScrollBar1.Value - (e.Y - this.startMouseMoveY) > (this.pictureBox.Height - this.Height))
                         && (this.hScrollBar1.Visible == false))
                    {
                        this.vScrollBar1.Value = this.pictureBox.Height - this.Height;
                    }
                    else
                    {
                        this.vScrollBar1.Value -= e.Y - this.startMouseMoveY;
                    }

                    this.pictureBox.Top = -this.vScrollBar1.Value;
                }
            }
        }

        /// <summary>
        /// sets the reference scroll when moving mouse with button pressed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.startMouseMoveX = e.X;
                this.startMouseMoveY = e.Y;

                // set the cursor
                if ((this.hScrollBar1.Visible == true) || (this.vScrollBar1.Visible == true))
                {
                    this.pictureBox.Cursor = closedHandCursor;
                }
            }
        }


        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            // set the cursor
            if ((this.hScrollBar1.Visible == true) || (this.vScrollBar1.Visible == true))
            {
                this.pictureBox.Cursor = openHandCursor;
            }

        }


        #endregion

        #region Graph Generation

        // creates a new graph file
        public String GenerateGraphFile(string ChannelsDatabaseName, string GOESDatabaseName, string GraphStartTime, string GraphEndTime, string GraphTitle, bool ShowSun, bool ShowForecast, string[] ChannelStationCodes, string[] ChannelTitles, string[] ChannelLineWidths, string[] ChannelColors, string[] ChannelDev1Colors, string[] ChannelDev2Colors, string[] ChannelDev3Colors, bool[] channelsToShow, string[] GOESTitles, string[] GOESLineWidths, string[] GOESColors, bool[] GOESToShow)
        {
            string graph_string = "graph ";

            // Creates a Unique temp file for the image
            string ImageName = Path.GetTempFileName();

            string NightColor = Color.FromArgb(SID_monitor.Properties.Settings.Default.NightColor).Name.Substring(2);
            string AstronomicalTwilightColor = Color.FromArgb(SID_monitor.Properties.Settings.Default.AstronomicalTwilightColor).Name.Substring(2);
            string NauticalTwilightColor = Color.FromArgb(SID_monitor.Properties.Settings.Default.NauticalTwilightColor).Name.Substring(2);
            string CivilTwilightColor = Color.FromArgb(SID_monitor.Properties.Settings.Default.CivilTwilightColor).Name.Substring(2);


            ChannelsDatabaseName = ChannelsDatabaseName.Replace(":", "\\:");
            GOESDatabaseName = GOESDatabaseName.Replace(":", "\\:");

            graph_string += "\"" + ImageName + "\" ";
            graph_string += "--imgformat PNG ";
            graph_string += "--interlaced ";

            graph_string += "--width " + ImageWidth.ToString() + " ";
            graph_string += "--height " + ImageHeight.ToString() + " ";
            graph_string += "--title \"" + GraphTitle + "\" ";

            graph_string += "--vertical-label \"Signal level (V)\" ";
            graph_string += "--upper-limit 4.095 ";
            graph_string += "--lower-limit -0.05 ";
            graph_string += "--rigid ";
            graph_string += "--y-grid 0.5:2 ";

            graph_string += "--units-exponent 0 ";
            graph_string += "--units-length 4 ";
            graph_string += "--start " + GraphStartTime + " ";
            graph_string += "--end " + GraphEndTime + " ";

            graph_string += "DEF:_sun=\"" + ChannelsDatabaseName + "\":sun:AVERAGE ";
            graph_string += "CDEF:sun=_sun,UN,-1,_sun,FLOOR,IF ";
            graph_string += "CDEF:daytime=sun,0,EQ,4.095,0,IF ";

            // validity defined from the sun value (different or not from unknown)
            graph_string += "CDEF:validbar=_sun,UN,0,-0.05,IF ";
            graph_string += "AREA:validbar#00FF00 ";
            graph_string += "CDEF:invalidbar=_sun,UN,-0.05,0,IF ";
            graph_string += "AREA:invalidbar#FF0000 ";
            graph_string += "CDEF:invalid=_sun,UN,4.095,0,IF ";
            graph_string += "AREA:invalid#FFEEEE ";


            if (ShowSun)
            {
                graph_string += "AREA:daytime#FFFFFF ";
                graph_string += "CDEF:civiltw=sun,1,EQ,sun,7,EQ,+,0,GT,4.095,0,IF ";
                graph_string += "AREA:civiltw#" + CivilTwilightColor + " ";
                graph_string += "CDEF:nautitw=sun,2,EQ,sun,6,EQ,+,0,GT,4.095,0,IF ";
                graph_string += "AREA:nautitw#" + NauticalTwilightColor + " ";
                graph_string += "CDEF:astrotw=sun,3,EQ,sun,5,EQ,+,0,GT,4.095,0,IF ";
                graph_string += "AREA:astrotw#" + AstronomicalTwilightColor + " ";
                graph_string += "CDEF:nighttime=sun,4,EQ,4.095,0,IF ";
                graph_string += "AREA:nighttime#" + NightColor + " ";

            }

            for (int i = 0; i < channelsToShow.Length; i++)
            {
                if (channelsToShow[i])
                {

                    if (ShowForecast)
                    {

                        // Failures (shown only during daytime)
                        graph_string += "DEF:_failures-" + ChannelStationCodes[i] + "=\"" + ChannelsDatabaseName + "\":" + ChannelStationCodes[i] + ":FAILURES ";
                        graph_string += "CDEF:failures-" + ChannelStationCodes[i] + "=daytime,_failures-" + ChannelStationCodes[i] + ",UN,0,_failures-" + ChannelStationCodes[i] + ",IF,1,EQ,4.095,0,IF,0,IF ";
                        graph_string += "AREA:failures-" + ChannelStationCodes[i] + "#" + Color.FromArgb(SID_monitor.Properties.Settings.Default.FailuresColor).Name.Substring(2) + ": ";

                        graph_string += "DEF:_predicted-" + ChannelStationCodes[i] + "=\"" + ChannelsDatabaseName + "\":" + ChannelStationCodes[i] + ":HWPREDICT ";
                        graph_string += "CDEF:predicted-" + ChannelStationCodes[i] + "=_predicted-" + ChannelStationCodes[i] + ",UN,0,_predicted-" + ChannelStationCodes[i] + ",1000,/,IF ";

                        // Forecast Deviation areas (shown only during daytime)
                        graph_string += "DEF:_devpredicted-" + ChannelStationCodes[i] + "=\"" + ChannelsDatabaseName + "\":" + ChannelStationCodes[i] + ":DEVPREDICT ";
                        graph_string += "CDEF:supdevpredicted-" + ChannelStationCodes[i] + "=daytime,_devpredicted-" + ChannelStationCodes[i] + ",1000,/," + SID_monitor.Properties.Settings.Default.DeltaPos.ToString() + ",*,0,IF ";
                        graph_string += "CDEF:infdevpredicted-" + ChannelStationCodes[i] + "=daytime,_devpredicted-" + ChannelStationCodes[i] + ",-1000,/," + SID_monitor.Properties.Settings.Default.DeltaNeg.ToString() + ",*,0,IF ";
                        graph_string += "LINE0:predicted-" + ChannelStationCodes[i] + ": ";
                        graph_string += "AREA:supdevpredicted-" + ChannelStationCodes[i] + "#" + ChannelDev1Colors[i] + "::STACK ";
                        graph_string += "AREA:supdevpredicted-" + ChannelStationCodes[i] + "#" + ChannelDev2Colors[i] + "::STACK ";
                        graph_string += "AREA:supdevpredicted-" + ChannelStationCodes[i] + "#" + ChannelDev3Colors[i] + "::STACK ";
                        graph_string += "LINE0:predicted-" + ChannelStationCodes[i] + ": ";
                        graph_string += "AREA:infdevpredicted-" + ChannelStationCodes[i] + "#" + ChannelDev1Colors[i] + "::STACK ";
                        graph_string += "AREA:infdevpredicted-" + ChannelStationCodes[i] + "#" + ChannelDev2Colors[i] + "::STACK ";
                        graph_string += "AREA:infdevpredicted-" + ChannelStationCodes[i] + "#" + ChannelDev3Colors[i] + "::STACK ";

                    }

                    graph_string += "DEF:_" + ChannelStationCodes[i] + "=\"" + ChannelsDatabaseName + "\":" + ChannelStationCodes[i] + ":AVERAGE ";
                    graph_string += "CDEF:" + ChannelStationCodes[i] + "=_" + ChannelStationCodes[i] + ",1000,/ ";
                    graph_string += "LINE" + ChannelLineWidths[i] + ":" + ChannelStationCodes[i] + "#" + ChannelColors[i] + ":\"" + ChannelStationCodes[i].Replace(":", "\\:") + " (" + ChannelTitles[i].Replace(":", "\\:") + ")\" ";


                }
            }

            if (GOESToShow[0] || GOESToShow[1])
            {
                graph_string += "COMMENT:\"\\n\" ";
            }

            if (GOESToShow[0])
            {
                graph_string += "DEF:_goesp=\"" + GOESDatabaseName + "\":goesp:AVERAGE ";
                graph_string += "CDEF:goesp=_goesp,LOG,10,LOG,/,0.5,*,4.5,+ ";
                graph_string += "LINE" + GOESLineWidths[0] + ":goesp#" + GOESColors[0] + ":\"" + GOESTitles[0].Replace(":", "\\:") + "\\n\" ";
            }

            if (GOESToShow[1])
            {
                graph_string += "DEF:_goess=\"" + GOESDatabaseName + "\":goess:AVERAGE ";
                graph_string += "CDEF:goess=_goess,LOG,10,LOG,/,0.5,*,4.5,+ ";
                graph_string += "LINE" + GOESLineWidths[1] + ":goess#" + GOESColors[1] + ":\"" + GOESTitles[1].Replace(":", "\\:") + "\\n\" ";
            }

            if (GOESToShow[0] || GOESToShow[1]) // second legend indicated in the legend since RRDTool does not support dual y axis
            {
                graph_string += "COMMENT:\"X-ray class\\: A below   1   (10-7 W/mē)\\n\" ";
                graph_string += "COMMENT:\"             B between 1   (10-7 W/mē) and 1.5 (10-6 W/mē)\\n\" ";
                graph_string += "COMMENT:\"             C between 1.5 (10-6 W/mē) and 2   (10-5 W/mē)\\n\" ";
                graph_string += "COMMENT:\"             M between 2   (10-5 W/mē) and 2.5 (10-4 W/mē)\\n\" ";
                graph_string += "COMMENT:\"             X above   2.5 (10-4 W/mē)\\n\" ";
            }

            SID_monitor.RRDToolConnection.ExecuteRRDTool(graph_string, SID_monitor.RRDToolProcess.ShowOptions.ErrorsOnly, outputTextBoxFormHandle);

            return ImageName;
        }

        private void TempFileCleanup(string filename)
        {
            if (!String.IsNullOrEmpty(filename) && File.Exists(filename))
            {
                try
                {
                    File.Delete(filename);
                }
                catch (Exception ex)
                {
                   ((SID_monitor.OutputTextBoxDockablePanel)Control.FromHandle(outputTextBoxFormHandle)).AddOutputTextWarningMessage(" Cannot clean-up graph image file (" + filename + ").\n*** " + ex.ToString().Split('\n')[0] + "\n");
                }
            }
        }

        /// <summary>
        /// Updates the pictureBox with a new graph
        /// </summary>
        public void GenerateGraph(string ChannelsDatabaseName, string GOESDatabaseName, string GraphStartTime, string GraphEndTime, string GraphTitle, bool ShowSun, bool ShowForecast, string[] ChannelStationCodes, string[] ChannelTitles, string[] ChannelLineWidths, string[] ChannelColors, string[] ChannelDev1Colors, string[] ChannelDev2Colors, string[] ChannelDev3Colors, bool[] channelsToShow, string[] GOESTitles, string[] GOESLineWidths, string[] GOESColors, bool[] GOESToShow)
        {
            // save image parameters in case they are necessary when updating the graph
            OldChannelsDatabaseName = ChannelsDatabaseName;
            OldGOESDatabaseName = GOESDatabaseName;
            OldGraphStartTime = GraphStartTime;
            OldGraphEndTime = GraphEndTime;
            OldGraphTitle = GraphTitle;
            OldShowSun = ShowSun;
            OldShowForecast = ShowForecast;
            OldChannelStationCodes = ChannelStationCodes;
            OldChannelTitles = ChannelTitles;
            OldChannelLineWidths = ChannelLineWidths;
            OldChannelColors = ChannelColors;
            OldChannelDev1Colors = ChannelDev1Colors;
            OldChannelDev2Colors = ChannelDev2Colors;
            OldChannelDev3Colors = ChannelDev3Colors;
            OldChannelsToShow = channelsToShow;
            OldGOESTitles = GOESTitles;
            OldGOESLineWidths = GOESLineWidths;
            OldGOESColors = GOESColors;
            OldGOESToShow = GOESToShow;

            // clean-up old temporary filename
            TempFileCleanup(LastDisplayedImageName);

            // stores the current displayed image name
            LastDisplayedImageName = GenerateGraphFile(ChannelsDatabaseName, GOESDatabaseName, GraphStartTime, GraphEndTime, GraphTitle, ShowSun, ShowForecast, ChannelStationCodes, ChannelTitles, ChannelLineWidths, ChannelColors, ChannelDev1Colors, ChannelDev2Colors, ChannelDev3Colors, channelsToShow, GOESTitles, GOESLineWidths, GOESColors, GOESToShow);

            if (!String.IsNullOrEmpty(LastDisplayedImageName) && File.Exists(LastDisplayedImageName))
            {
                // updates the PictureBox
                this.pictureBox.ImageLocation = LastDisplayedImageName;

                // raise an event to indicate the file is OK
                GraphFileChangeEventArgs e = new GraphFileChangeEventArgs(true);
                OnGraphFileChanged(e);
                toolStripMenuItemSaveAsPNG.Enabled = true;
            }
            else
            {
                // raise an event to indicate the file is NOK
                GraphFileChangeEventArgs e = new GraphFileChangeEventArgs(false);
                OnGraphFileChanged(e);
                toolStripMenuItemSaveAsPNG.Enabled = false;

            }
        }

        private void UpdateGraph()
        {
            if (!String.IsNullOrEmpty(OldChannelsDatabaseName))
            {
                GenerateGraph(OldChannelsDatabaseName, OldGOESDatabaseName, OldGraphStartTime, OldGraphEndTime, OldGraphTitle, OldShowSun, OldShowForecast, OldChannelStationCodes, OldChannelTitles, OldChannelLineWidths, OldChannelColors, OldChannelDev1Colors, OldChannelDev2Colors, OldChannelDev3Colors, OldChannelsToShow, OldGOESTitles, OldGOESLineWidths, OldGOESColors, OldGOESToShow);
            }
        }
        #endregion

        public void SaveGraphFile()
        {
            if (!String.IsNullOrEmpty(LastDisplayedImageName) && File.Exists(LastDisplayedImageName))
            {
                if (this.saveFileDialogGraphImage.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        File.Copy(LastDisplayedImageName, this.saveFileDialogGraphImage.FileName, true);
                    }
                    catch (Exception ex)
                    {
                        ((SID_monitor.OutputTextBoxDockablePanel)Control.FromHandle(outputTextBoxFormHandle)).AddOutputTextErrorMessage(" Cannot Save Graph as PNG.\n*** " + ex.ToString().Split('\n')[0] + "\n");
                    }
                }
            }
            else
            {
                ((SID_monitor.OutputTextBoxDockablePanel)Control.FromHandle(outputTextBoxFormHandle)).AddOutputTextWarningMessage(" Cannot Save Graph as PNG. No image to save.\n");
            }
        }

        #region Contextual Menu Methods
        public void toolStripMenuItemSaveAsPNG_Click(object sender, EventArgs e)
        {
            SaveGraphFile();
        }

        private void toolStripMenuItemZoomIn_Click(object sender, EventArgs e)
        {
            this.ZoomIn(Cursor.Position);
            toolStripMenuItemZoomIn.Enabled = this.OkToZoomIn();
            toolStripMenuItemZoomOut.Enabled = this.OkToZoomOut();
        }

        private void toolStripMenuItemZoomOut_Click(object sender, EventArgs e)
        {
            this.ZoomOut(Cursor.Position);
            toolStripMenuItemZoomIn.Enabled = this.OkToZoomIn();
            toolStripMenuItemZoomOut.Enabled = this.OkToZoomOut();
        }

        private void toolStripMenuItemZoomDefault_Click(object sender, EventArgs e)
        {
            this.ZoomDefault();
            toolStripMenuItemZoomIn.Enabled = this.OkToZoomIn();
            toolStripMenuItemZoomOut.Enabled = this.OkToZoomOut();
        }


        private void toolStripMenuItemIncreaseImageSize_Click(object sender, EventArgs e)
        {
            this.IncreaseImageSize();
            toolStripMenuItemIncreaseImageSize.Enabled = this.OkToIncreaseImageSize();
            toolStripMenuItemDecreaseImageSize.Enabled = this.OkToDecreaseImageSize();
        }


        private void toolStripMenuItemDecreaseImageSize_Click(object sender, EventArgs e)
        {
            this.DecreaseImageSize();
            toolStripMenuItemIncreaseImageSize.Enabled = this.OkToIncreaseImageSize();
            toolStripMenuItemDecreaseImageSize.Enabled = this.OkToDecreaseImageSize();
        }


        private void toolStripMenuItemDefaultImageSize_Click(object sender, EventArgs e)
        {
            this.DefaultImageSize();
            toolStripMenuItemIncreaseImageSize.Enabled = this.OkToIncreaseImageSize();
            toolStripMenuItemDecreaseImageSize.Enabled = this.OkToDecreaseImageSize();
        }

        // requests update of the graph by sending event
        private void updateNowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OnUpdateRequested(e);
        }
        #endregion

        #region Disposing
        protected override void Dispose(bool disposing)
        {
            // If disposing equals true, dispose all managed and unmanaged resources.
            if (disposing)
            {
                // Dispose managed resources.
                components.Dispose();
            }

            // clean-up old temporary filename
            TempFileCleanup(LastDisplayedImageName);

            base.Dispose(disposing);
        }
        #endregion

        #region Events

        /// <summary>
        /// GraphFileChanged Event
        /// </summary>

        public class GraphFileChangeEventArgs : EventArgs
        {
            private bool fileValid = false;

            public GraphFileChangeEventArgs(bool validity)
            {
                this.fileValid = validity;
            }

            public bool FileValid
            {
                get
                {
                    return fileValid;
                }
            }
        }

        public delegate void GraphFileChangeEventHandler(object sender, GraphFileChangeEventArgs e);

        public event GraphFileChangeEventHandler GraphFileChanged; // the displayed graph file has changed

        protected virtual void OnGraphFileChanged(GraphFileChangeEventArgs e)
        {
            GraphFileChangeEventHandler handler = GraphFileChanged;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }


        /// <summary>
        /// UpdateRequested Event
        /// </summary>

        public event EventHandler UpdateRequested;  // graph update is requested

        protected virtual void OnUpdateRequested(EventArgs e)
        {
            EventHandler handler = UpdateRequested;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }
        #endregion




    }
}
