// UI.Toolbar.cs created with MonoDevelop
// User: klose at 10:34 15.01.2009
// CVS release: $Id: UI.Toolbar.cs,v 1.75 2011-06-24 13:48:23 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009  Thomas Klose, Hiperscan GmbH Dresden, Germany
//
//    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 3 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, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

using Glade;

using Gtk;

using Hiperscan.Extensions;
using Hiperscan.NPlotEngine;
using Hiperscan.NPlotEngine.Overlay.Annotation;
using Hiperscan.QuickStep.Dialog;
using Hiperscan.QuickStep.Figure;
using Hiperscan.QuickStep.Integration;
using Hiperscan.QuickStep.IO;
using Hiperscan.SGS;
using Hiperscan.SGS.Common;
using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.IO;
using Hiperscan.Unix;


namespace Hiperscan.QuickStep
{

    public partial class UserInterface
    {
        private const float EXPORT_IMAGE_RESOLUTION = 300f;

        [Widget] Gtk.HandleBox standard_handlebox = null;
        [Widget] Gtk.HandleBox figure_handlebox   = null;

        [Widget] Gtk.Toolbar standard_toolbar = null;

        [Widget] Gtk.ImageMenuItem save_item          = null;
        [Widget] Gtk.ImageMenuItem print_item         = null;
        [Widget] Gtk.ImageMenuItem clear_item         = null;
        [Widget] Gtk.ImageMenuItem zoom_fit_item      = null;
        [Widget] Gtk.ImageMenuItem zoom_standard_item = null;
        [Widget] Gtk.ImageMenuItem zoom_in_item       = null;
        [Widget] Gtk.ImageMenuItem zoom_out_item      = null;
        [Widget] Gtk.ImageMenuItem copy_item          = null;
        [Widget] Gtk.ImageMenuItem export_item        = null;
        [Widget] Gtk.ImageMenuItem undo_item          = null;
        [Widget] Gtk.ImageMenuItem redo_item          = null;
        [Widget] Gtk.ImageMenuItem start_item         = null;
        [Widget] Gtk.ImageMenuItem quickstream_item   = null;
        [Widget] Gtk.ImageMenuItem stop_item          = null;
        [Widget] Gtk.ImageMenuItem single_item        = null;
        [Widget] Gtk.ImageMenuItem disconnect_remote_item = null;
        [Widget] Gtk.ImageMenuItem close_tab_item = null;
        
        [Widget] Gtk.MenuItem reopen_item             = null;
        [Widget] Gtk.MenuItem save_container_item     = null;
        [Widget] Gtk.MenuItem send_item               = null;
        [Widget] Gtk.MenuItem insert_annotations_item = null;
        [Widget] Gtk.MenuItem clear_annotations_item  = null;
        [Widget] Gtk.MenuItem copy_plot_data_item     = null;

        [Widget] Gtk.MenuItem clear_background_item   = null;
        [Widget] Gtk.MenuItem sync_axes_item          = null;
        [Widget] Gtk.CheckMenuItem lock_axes_item     = null;
        
        [Widget] Gtk.Image lock_axes_image = null;
        
        [Widget] Gtk.CheckMenuItem data_sources_item       = null;
        [Widget] Gtk.CheckMenuItem properties_item         = null;
        [Widget] Gtk.CheckMenuItem plot_browser_item       = null;
        [Widget] Gtk.CheckMenuItem standard_toolbar_item   = null;
        [Widget] Gtk.CheckMenuItem figure_toolbar_item     = null;
        [Widget] Gtk.CheckMenuItem extensions_toolbar_item = null;
        
        [Widget] Gtk.RadioMenuItem english_item = null;
        [Widget] Gtk.RadioMenuItem german_item  = null;
        [Widget] Gtk.RadioMenuItem system_item = null;
        
        [Widget] Gtk.RadioMenuItem axis_type_wavelength_item = null;
        [Widget] Gtk.RadioMenuItem axis_type_wavecount_item  = null;

        [Widget] Gtk.MenuItem plot_type_item   = null;
        [Widget] Gtk.MenuItem themes_item      = null;

        [Widget] Gtk.SpinButton x_min_spin = null;
        [Widget] Gtk.SpinButton x_max_spin = null;
        [Widget] Gtk.SpinButton y_min_spin = null;
        [Widget] Gtk.SpinButton y_max_spin = null;
        
        [Widget] Gtk.ComboBox plot_type_combo              = null;

        [Widget] Gtk.HBox x_minmax_hbox         = null;
        [Widget] Gtk.HBox y_minmax_hbox         = null;
        
        [Widget] Gtk.ToolButton save_button          = null;
        [Widget] Gtk.ToolButton print_button         = null;
        [Widget] Gtk.ToolButton clear_button         = null;
        [Widget] Gtk.ToolButton zoom_fit_button      = null;
        [Widget] Gtk.ToolButton single_button        = null;
        [Widget] Gtk.ToolButton start_button         = null;
        [Widget] Gtk.ToolButton stop_button          = null;
        [Widget] Gtk.ToolButton quickstream_button   = null;
        [Widget] Gtk.ToolButton undo_button          = null;
        [Widget] Gtk.ToolButton redo_button          = null;
        [Widget] Gtk.ToolButton zoom_standard_button = null;
        [Widget] Gtk.ToolButton zoom_in_button       = null;
        [Widget] Gtk.ToolButton zoom_out_button      = null;
        [Widget] Gtk.ToolButton copy_button          = null;
        [Widget] Gtk.ToolButton export_button        = null;
        
        [Widget] Gtk.ToolButton insert_text_button        = null;
        [Widget] Gtk.ToolButton insert_line_button        = null;
        [Widget] Gtk.ToolButton insert_rectangle_button   = null;
        [Widget] Gtk.ToolButton insert_ellipse_button     = null;
        [Widget] Gtk.ToolButton insert_arrow_button       = null;
        [Widget] Gtk.ToolButton insert_doublearrow_button = null;

        [Widget] Gtk.ToolButton plot_tv_save_button       = null;
        [Widget] Gtk.ToolButton plot_tv_copy_button       = null;
        [Widget] Gtk.ToolButton plot_tv_delete_button     = null;
        [Widget] Gtk.ToolButton plot_tv_auto_color_button = null;
        [Widget] Gtk.ToolButton plot_tv_move_up_button    = null;
        [Widget] Gtk.ToolButton plot_tv_move_down_button  = null;
        [Widget] Gtk.ToolButton plot_tv_add_button        = null;
        [Widget] Gtk.ToolButton plot_tv_remove_button     = null;
        
        [Widget] Gtk.ToggleToolButton data_sources_button    = null;
        [Widget] Gtk.ToggleToolButton property_editor_button = null;
        [Widget] Gtk.ToggleToolButton plot_browser_button    = null;
        [Widget] Gtk.ToggleToolButton edit_x_constraints_button  = null;
        
        [Widget] Gtk.ToolItem  x_constraints_item  = null;
        [Widget] Gtk.Alignment x_constraints_align = null;
        
        [Widget] Gtk.MenuItem extensions_menuitem = null;
        [Widget] Gtk.Menu extensions_menu = null;
        
        [Widget] Gtk.VBox plot_browser_vbox = null;
        [Widget] Gtk.Menu edit_menu         = null;
        [Widget] Gtk.Menu spectrometer_menu = null;
        [Widget] Gtk.Menu figure_menu       = null;
        
        private XConstraintsEntry x_constraints_entry = null;

        private static volatile bool __update_on_plot_type_combo_change = true;

        internal static Gdk.Color entry_error_text_color = new Gdk.Color(255, 0, 0);
        internal static Gdk.Color entry_error_base_color = new Gdk.Color(255, 255, 0);


        private void UIPlotControlsSensitive(bool val)
        {
            this.save_item.Sensitive               = val && this.DeviceList.AllIdleOrDisconnected;
            this.save_container_item.Sensitive     = val && this.DeviceList.AllIdleOrDisconnected;
            this.send_item.Sensitive               = val && this.DeviceList.AllIdleOrDisconnected;
            this.print_item.Sensitive              = val && this.DeviceList.AllIdleOrDisconnected;
            this.export_item.Sensitive             = val && this.DeviceList.AllIdleOrDisconnected;
            this.insert_annotations_item.Sensitive = val;
            this.clear_annotations_item.Sensitive  = val && (this.ScrolledPlot.Plot.Annotations.Count > 0);
            this.clear_item.Sensitive              = val;
            this.zoom_fit_item.Sensitive           = val;
            this.zoom_standard_item.Sensitive      = val;
            this.zoom_in_item.Sensitive            = val;
            this.zoom_out_item.Sensitive           = val;
            this.copy_item.Sensitive               = val;
            this.copy_plot_data_item.Sensitive     = val;
            
            this.undo_button.Sensitive = this.DeviceList.AllIdleOrDisconnected && this.plot_info_list.Current.CurrentSpectra.UndoIsPossible;
            this.undo_item.Sensitive   = this.DeviceList.AllIdleOrDisconnected && this.plot_info_list.Current.CurrentSpectra.UndoIsPossible;
            this.redo_button.Sensitive = this.DeviceList.AllIdleOrDisconnected && this.plot_info_list.Current.CurrentSpectra.RedoIsPossible;
            this.redo_item.Sensitive   = this.DeviceList.AllIdleOrDisconnected && this.plot_info_list.Current.CurrentSpectra.RedoIsPossible;
            
            this.save_button.Sensitive               = val && this.DeviceList.AllIdleOrDisconnected;
            this.print_button.Sensitive              = val && this.DeviceList.AllIdleOrDisconnected;
            this.export_button.Sensitive             = val && this.DeviceList.AllIdleOrDisconnected;
            this.clear_button.Sensitive              = val;
            this.edit_x_constraints_button.Sensitive = val;
            this.zoom_fit_button.Sensitive           = val;
            this.zoom_standard_button.Sensitive      = val;
            this.zoom_in_button.Sensitive            = val;
            this.zoom_out_button.Sensitive           = val;
            this.copy_button.Sensitive               = val;
            
            this.insert_text_button.Sensitive = val;
            this.insert_line_button.Sensitive = val;
            this.insert_rectangle_button.Sensitive = val;
            this.insert_ellipse_button.Sensitive = val;
            this.insert_arrow_button.Sensitive = val;
            this.insert_doublearrow_button.Sensitive = val;

            this.x_minmax_hbox.Sensitive = val;
            this.y_minmax_hbox.Sensitive = val;
            
            if (this.ScrolledPlot != null)
                this.ScrolledPlot.Sensitive = val;
            
            this.plot_browser_vbox.Sensitive = !this.plot_tv.IsEmpty;
        }
        
        private void UpdateFigureControls()
        {
            bool val = (this.plot_info_list.Background != null);
            this.clear_background_item.Sensitive = val;
            this.sync_axes_item.Sensitive  = val;
            this.lock_axes_item.Sensitive  = val;
            this.lock_axes_image.Sensitive = val;
        }
        
        public void UpdateDeviceControls()
        {
            bool single       = false;
            bool quick        = false;
            bool start        = false;
            bool stop         = false;
            bool remote       = false;
            bool unprogrammed = false;
            bool idle         = false;
            
            foreach (IDevice dev in this.active_devices)
            {
                if (dev.CanAcquireSpectra == false)
                    continue;

                if (dev.Info.IsRemote)
                    remote = true;

                Spectrometer.State state = dev.GetState();

                switch (state)
                {
                    
                case Spectrometer.State.Idle:
                    single = true;
                    start  = true;
                    quick  = true;
                    idle   = true;
                    break;
                    
                case Spectrometer.State.Continuous:
                case Spectrometer.State.Stream:
                    single = true;
                    quick  = true;
                    stop   = true;
                    break;

                case Spectrometer.State.QuickStream:
                    single = true;
                    start  = true;
                    stop   = true;
                    break;
                    
                case Spectrometer.State.Unprogrammed:
                    unprogrammed = true;
                    break;

                }
            }
            
            this.single_item.Sensitive            = single;
            this.start_item.Sensitive             = start;
            this.stop_item.Sensitive              = stop;
            this.quickstream_item.Sensitive       = quick;
            this.disconnect_remote_item.Sensitive = remote;
            this.single_button.Sensitive          = single;
            
            foreach (IPlugin plugin in this.plugins.Values)
            {
                if (plugin.MenuInfos != null)
                {
                    foreach (MenuInfo info in plugin.MenuInfos)
                    {
                        info.MenuItem.Sensitive = true;

                        if (!info.IsSensitive)
                        {
                            info.MenuItem.Sensitive = false;
                            continue;
                        }
                        
                        if (info.NeedSingleDevice && this.active_devices.Count != 1)
                        {
                            info.MenuItem.Sensitive = false;
                            continue;
                        }
                        
                        if (info.NeedIdleDevice)
                            info.MenuItem.Sensitive = idle;
                        
                        if (info.NeedUnprogrammedOrIdleDevice)
                            info.MenuItem.Sensitive = unprogrammed || idle;
                    }
                }
                
                if (plugin.ToolbarInfos != null)
                {
                    foreach (ToolbarInfo info in plugin.ToolbarInfos)
                    {
                        info.ToolItem.Sensitive = true;

                        if (info.IsSensitive == false)
                        {
                            info.ToolItem.Sensitive = false;
                            continue;
                        }
                        
                        if (info.NeedSingleDevice && this.active_devices.Count != 1)
                        {
                            info.ToolItem.Sensitive = false;
                            continue;
                        }
                        
                        if (info.NeedIdleDevice)
                            info.ToolItem.Sensitive = idle;
                        
                        if (info.NeedUnprogrammedOrIdleDevice)
                            info.ToolItem.Sensitive = unprogrammed || idle;
                    }
                }
            }
            this.start_button.Sensitive       = start;
            this.stop_button.Sensitive        = stop;
            this.quickstream_button.Sensitive = quick;
            
            bool val = this.DeviceList.AllIdleOrDisconnected && this.ScrolledPlot.Plot.IsEmpty == false;
            this.save_item.Sensitive           = val;
            this.save_container_item.Sensitive = val;
            this.send_item.Sensitive           = val;
            this.print_item.Sensitive          = val;
            this.export_item.Sensitive         = val;
            this.undo_button.Sensitive = val && this.plot_info_list.Current.CurrentSpectra.UndoIsPossible;
            this.undo_item.Sensitive   = val && this.plot_info_list.Current.CurrentSpectra.UndoIsPossible;
            this.redo_button.Sensitive = val && this.plot_info_list.Current.CurrentSpectra.RedoIsPossible;
            this.redo_item.Sensitive   = val && this.plot_info_list.Current.CurrentSpectra.RedoIsPossible;

            this.save_button.Sensitive   = val;
            this.print_button.Sensitive  = val;
            this.export_button.Sensitive = val;
        }
        
        public void UpdatePluginInfos()
        {
            bool hide_edit_shortcuts_item = true;
            bool hide_standard_handlebox = true;
            bool no_extensions_active = true;
            bool plot_types_changed = false;
            
            foreach (Widget w in this.standard_toolbar_widgets)
            {
                if (this.standard_toolbar_item.Active)
                {
                    w.NoShowAll = false;
                    w.ShowAll();
                    hide_standard_handlebox = false;
                }
                else
                {
                    w.NoShowAll = true;
                    w.Visible = false;
                }
            }
            
            foreach (IPlugin plugin in this.plugins.Values)
            {
                if (plugin.MenuInfos != null && plugin.MenuInfos.Length > 0)
                {                
                    if (plugin.Active)
                        hide_edit_shortcuts_item = false;
                    
                    if (plugin.MenuInfos.Length == 1)
                    {
                        plugin.MenuInfos[0].MenuItem.Visible = plugin.Active;
                        plugin.MenuInfos[0].MenuItem.NoShowAll = !plugin.Active;
                    }
                    else if (plugin.MenuInfos.Length > 1)
                    {
                        plugin.MenuInfos[0].SubMenuItem.Visible = plugin.Active;
                        plugin.MenuInfos[0].SubMenuItem.NoShowAll = !plugin.Active;
                        if (plugin.Active)
                            plugin.MenuInfos[0].SubMenuItem.ShowAll();
                    }
                }

                if (plugin.ToolbarInfos != null)
                {
                    if (plugin.Active && this.extensions_toolbar_item.Active)
                        hide_standard_handlebox = false;
                    
                    if (plugin.Active)
                        no_extensions_active = false;

                    bool active = plugin.Active && this.extensions_toolbar_item.Active;
                    
                    foreach (ToolbarInfo info in plugin.ToolbarInfos)
                    {
                        info.ToolItem.Visible = active;
                        info.ToolItem.NoShowAll = !active;
                        if (active)
                            info.ToolItem.ShowAll();
                    }
                }

                if (plugin.PlotTypeInfos != null)
                {
                    plot_types_changed = true;
                    
                    if (plugin.Active)
                    {
                        if (this.plot_types.ContainsKey(plugin.Guid.ToString()))
                            this.plot_types[plugin.Guid.ToString()] = plugin.PlotTypeInfos;
                        else
                            this.plot_types.Add(plugin.Guid.ToString(), plugin.PlotTypeInfos);
                    }
                    else
                    {
                        if (this.plot_types.ContainsKey(plugin.Guid.ToString()))
                            this.plot_types.Remove(plugin.Guid.ToString());
                    }
                }
                
                if (plugin.FileTypeInfos != null)
                {
                    if (plugin.Active)
                    {
                        if (this.file_types.ContainsKey(plugin.Guid.ToString()) == false)
                            this.file_types.Add(plugin.Guid.ToString(), plugin.FileTypeInfos);
                    }
                    else
                    {
                        if (this.file_types.ContainsKey(plugin.Guid.ToString()))
                            this.file_types.Remove(plugin.Guid.ToString());
                    }
                }
            }
            
            bool show_separator = false;
            bool show_first_separator = false;
            SeparatorToolItem first_separator = null;
            SeparatorToolItem last_separator = null;
            foreach (Widget w in this.extension_toolbar_widgets)
            {
                if (w is SeparatorToolItem)
                {
                    if (first_separator == null)
                        first_separator = (SeparatorToolItem)w;
                    
                    w.Visible = show_separator;
                    w.NoShowAll = !show_separator;
                    
                    if (show_separator)
                    {
                        last_separator = (SeparatorToolItem)w;
                        show_separator = false;
                    }
                }
                else 
                {
                    if (w.Visible)
                        show_first_separator = this.standard_toolbar_item.Active;
                    
                    show_separator = w.Visible;
                }
            }
            
            if (show_first_separator && first_separator != null)
            {
                first_separator.NoShowAll = false;
                first_separator.ShowAll();
            }
            
            if (show_separator == false && last_separator != null)
            {
                last_separator.Visible = false;
                last_separator.NoShowAll = true;
            }
            
            if (this.edit_shortcuts_item != null)
            {
                if (hide_edit_shortcuts_item)
                {
                    this.edit_shortcuts_item.NoShowAll = true;
                    this.edit_shortcuts_item.Visible = false;
                }
                else
                {
                    this.edit_shortcuts_item.NoShowAll = false;
                    this.edit_shortcuts_item.ShowAll();
                }
            }
            
            this.extensions_toolbar_item.Sensitive = !no_extensions_active;
            
            if (hide_standard_handlebox)
            {
                this.standard_handlebox.NoShowAll = true;
                this.standard_handlebox.Visible = false;
            }
            else
            {
                this.standard_handlebox.NoShowAll = false;
                this.standard_handlebox.ShowAll();
            }

            if (this.figure_toolbar_item.Active)
            {
                this.figure_handlebox.NoShowAll = false;
                this.figure_handlebox.ShowAll();
            }
            else
            {
                this.figure_handlebox.NoShowAll = true;
                this.figure_handlebox.Visible = false;
            }
                
            if (plot_types_changed)
                this.UpdatePlotTypes();

            // update file list
            foreach (FileFilter filter in this.bg_filechooser.Filters)
            {
                this.bg_filechooser.RemoveFilter(filter);
            }
            foreach (FileTypeInfo type in this.FileTypes)
            {
                if (type.RwType != FileRwType.WriteOnly && !type.IsContainer)
                    this.bg_filechooser.AddFilter(this.GetFileFilter(type));
            }
        }

        private void UpdatePlotTypes()
        {
            ListStore store = new ListStore(typeof(string),
                                            typeof(PlotTypeInfo),
                                            typeof(RadioMenuItem));

            CellRendererText text = new CellRendererText();
            
            this.plot_type_combo.Clear();
            this.plot_type_combo.PackStart(text, false);
            this.plot_type_combo.SetAttributes(text, "text", 0);
            this.plot_type_combo.Model = store;
            
            Menu plot_type_menu = new Menu();
            RadioMenuItem menu_group = null;
            
            foreach (PlotTypeInfo plot_type in this.plot_types.Values.SelectMany(p => p))
            {
                if (plot_type.Active == false)
                    continue;
                    
                RadioMenuItem item;
                if (menu_group == null)
                {
                    item = new RadioMenuItem(plot_type.Name);
                    menu_group = item;
                }
                else
                {
                    item = new RadioMenuItem(menu_group, plot_type.Name);
                }

                this.__update_plot_type_helper(item, plot_type);

                plot_type_menu.Append(item);
                store.AppendValues(plot_type.Name, plot_type, item);
            }
            
            this.plot_type_item.Submenu = plot_type_menu;
            this.plot_type_item.ShowAll();
            
            this.PlotType = this.plot_types[DEFAULT_PLOT_TYPE_ID][0];
        }
        
        private void __update_plot_type_helper(RadioMenuItem item, PlotTypeInfo plot_type)
        {
            item.Activated += (o, e) =>
            {
                RadioMenuItem r = o as RadioMenuItem;
                if (r.Active)
                    this.PlotType = plot_type;
            };
        }
        
        private void InitializeThemes()
        {
            if (MainClass.OS != MainClass.OSType.Windows || Directory.Exists(MainClass.THEMES_DIR) == false)
                return;
            
            string[] dirs = Directory.GetDirectories(MainClass.THEMES_DIR);
            if (dirs.Length < 1)
                return;
            
            this.themes_item.NoShowAll = false;
            
            var themes = new List<string>();
            foreach (string dir in dirs)
            {
                themes.Add(Path.GetFileName(dir));
            }
            themes.Sort();
            themes.Insert(0, Catalog.GetString("System"));
                          
            Menu theme_menu = new Menu();
            RadioMenuItem menu_group = null;
            
            foreach (string theme in themes)
            {
                RadioMenuItem item;

                if (menu_group == null)
                {
                    item = new RadioMenuItem(theme);
                    menu_group = item;
                }
                else
                {
                    item = new RadioMenuItem(menu_group, theme);
                }
                
                item.Activated += (sender, e) =>
                {
                    if (this.initialized == false)
                        return;
                    RadioMenuItem r = sender as RadioMenuItem;
                    if (r.Active == false)
                        return;
                    string t = ((Label)r.Child).Text;
                    if (t == Catalog.GetString("System"))
                        Settings.Current.GtkTheme = MainClass.SYSTEM_GTK_THEME_NAME;
                    else
                        Settings.Current.GtkTheme = t;

                    InfoMessageDialog.Show(this.MainWindow,
                                           Catalog.GetString("In order to apply the new theme, please restart {0}"),
                                           MainClass.ApplicationName);
                };
                    
                theme_menu.Append(item);
                
                if (Settings.Current.GtkTheme == theme)
                    item.Active = true;
            }

            this.themes_item.Submenu = theme_menu;
            this.themes_item.ShowAll();
        }

        internal void OnXMinSpinChanged(object o, EventArgs e)
        {
            SpinButton s = o as SpinButton;
            if (this.ScrolledPlot.Plot.ReverseXAxes)
                this.ScrolledPlot.XMax = s.Value;
            else
                this.ScrolledPlot.XMin = s.Value;
        }

        internal void OnXMaxSpinChanged(object o, EventArgs e)
        {
            SpinButton s = o as SpinButton;
            if (this.ScrolledPlot.Plot.ReverseXAxes)
                this.ScrolledPlot.XMin = s.Value;
            else
                this.ScrolledPlot.XMax = s.Value;
        }

        internal void OnYMinSpinChanged(object o, EventArgs e)
        {
            SpinButton s = o as SpinButton;
            this.ScrolledPlot.YMin = s.Value;
        }

        internal void OnYMaxSpinChanged(object o, EventArgs e)
        {
            SpinButton s = o as SpinButton;
            this.ScrolledPlot.YMax = s.Value;
        }
        
        internal void OnEditXConstraintsButtonToggled(object o, EventArgs e)
        {
            ToggleToolButton b = o as ToggleToolButton;
            if (b.Active)
            {
                this.x_constraints_item.NoShowAll = false;
                this.x_constraints_item.ShowAll();

                GLib.Timeout.Add(500, () =>
                {
                    this.x_constraints_entry.HasFocus = true;
                    return false;
                });
            }
            else
            {
                this.x_constraints_item.HideAll();
                this.x_constraints_item.NoShowAll = true;

                if (this.plot_info_list.Current.XConstraints != null)
                {
                    this.plot_info_list.Current.XConstraints = null;
                    this.UpdatePlot(this.plot_info_list.Current);
                }
            }
        }
        
        internal void OnXConstraintsFocusOut(object o, FocusOutEventArgs e)
        {
            if (this.edit_x_constraints_button.Active == false)
                return;
            
            XConstraintsEntry entry = o as XConstraintsEntry;
            
            if (this.plot_info_list.Current.XConstraints.ToString() == entry.Text)
                return;
            
            this.plot_info_list.Current.XConstraints = DataSet.XConstraints.CreateFromString(entry.Text);
            if (this.plot_info_list.Current.XConstraints == null)
                return;
            
            Console.WriteLine("Setting X-Constraints '{0}' for figure '{1}'.",
                              this.plot_info_list.Current.XConstraints,
                              this.plot_info_list.Current.Name);

            var constraints = Settings.Current.XConstraints;
            string new_constraint = this.plot_info_list.Current.XConstraints.ToString();
            if (constraints.Contains(new_constraint) == false)
                constraints.Add(new_constraint);
            Settings.Current.XConstraints = constraints;
            this.x_constraints_entry.UpdateCompletion();
            this.UpdatePlot(this.plot_info_list.Current);
            this.x_constraints_entry.SelectRegion(0, 0);
        }
        
        internal void OnZoomFitButtonClicked(object o, EventArgs e)
        {
            this.ScrolledPlot.Plot.FitView();
            this.ScrolledPlot.XZoom = 1.0;
            this.ScrolledPlot.YZoom = 1.0;
        }
        
        internal void OnZoomStandardButtonClicked(object o, EventArgs e)
        {
            this.ScrolledPlot.YMin = this.PlotType.StandardMin;
            this.ScrolledPlot.YMax = this.PlotType.StandardMax;
            this.ScrolledPlot.YZoom = 1.0;
        }
        
        internal void OnClearButtonClicked(object o, EventArgs e)
        {
            this.ClearPlot();
        }
        
        internal void OnMinimizeItemClicked(object o, EventArgs e)
        {
            this.MainWindow.Hide();
            Integration.TrayIcon.Initialize();
            Integration.TrayIcon.Update();
        }
        
        internal void OnExitButtonClicked(object o, EventArgs e)
        {
            if (string.IsNullOrEmpty(MainClass.AutoPlugin))
                MainClass.Quit(false);
            else
                MainClass.MainWindow.Hide();
        }

        internal void OnOpenDirectoryClicked(object o, EventArgs e)
        {
            var chooser = new Dialog.FileChooserDialog(Catalog.GetString("Choose directory"),
                                                       this.MainWindow,
                                                       FileChooserAction.SelectFolder,
                                                       Stock.Open,
                                                       ResponseType.Ok,
                                                       Stock.Cancel,
                                                       ResponseType.Cancel);

            if (string.IsNullOrEmpty(Settings.Current.OpenDirectoryPath))
                chooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
            else
                chooser.SetCurrentFolder(Settings.Current.OpenDirectoryPath);

            ResponseType choice = (ResponseType)chooser.Run();
            if (choice == ResponseType.Ok)
            {
                chooser.Hide();

                this.AddFileTv(chooser.Filename);

                var paths = Settings.Current.DataBrowserPaths;
                paths.Add(chooser.Filename);
                Settings.Current.DataBrowserPaths = paths;

                Settings.Current.OpenDirectoryPath = chooser.CurrentFolder;
            }
            chooser.Destroy();
        }
        
        internal void OnOpenButtonClicked(object o, EventArgs e)
        {
            var chooser = new Dialog.FileChooserDialog(Catalog.GetString("Choose file to open"), 
                                                       this.MainWindow,
                                                       FileChooserAction.Open,
                                                       Stock.Open,
                                                       ResponseType.Ok,
                                                       Stock.Cancel,
                                                       ResponseType.Cancel);
            foreach (FileTypeInfo type in this.FileTypes)
            {
                if (type.RwType != FileRwType.WriteOnly && type.IsContainer == false)
                    chooser.AddFilter(this.GetFileFilter(type));
            }

            foreach (FileTypeInfo type in this.FileTypes)
            {
                if (type.RwType != FileRwType.WriteOnly && type.IsContainer)
                    chooser.AddFilter(this.GetFileFilter(type));
            }

            chooser.SelectMultiple = true;

            if (string.IsNullOrEmpty(Settings.Current.OpenFilePath))
                chooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
            else
                chooser.SetCurrentFolder(Settings.Current.OpenFilePath);

            try
            {
                ResponseType choice = (ResponseType)chooser.Run();
                if (choice == ResponseType.Ok)
                {
                    chooser.Hide();
                    UserInterface.Instance.ProcessEvents();

                    FileTypeInfo file_type = this.GetFileTypeInfo(chooser.Filter, true, false);

                    bool first_iteration = true;
                    for (int ix=0; ix < chooser.Filenames.Length; ++ix)
                    {
                        string file = chooser.Filenames[ix];

                        Console.WriteLine("Opening file: " + file);

                        Spectrum[] spectra;
                        List<MetaData> meta_data = null;
                        
                        if (file_type.IsContainer)
                            spectra = file_type.ReadContainer(file, out meta_data);
                        else
                            spectra = new Spectrum[] { file_type.Read(file) };
                        
                        if (first_iteration)
                        {
                            this.plot_info_list.Current.CurrentSpectra.AddHistoryEntry();
                            first_iteration = false;
                        }
                        
                        for (int jx=0; jx < spectra.Length; ++jx)
                        {
                            this.AddSpectrum(spectra[jx], false, false, true);
                        }
                        this.ScrolledPlot.RestoreAnnotations(meta_data);

                        this.ShowProgress((double)ix/(double)chooser.Filenames.Length);
                    }
                    this.HideProgress();
                    this.ScrolledPlot.Plot.RequestUpdate();
                    this.CurrentSpectra.NotifyChanges();

                    if (this.PlotType.Interdependent)
                        this.UpdateCurrentPlot();

                    Settings.Current.OpenFilePath = chooser.CurrentFolder;
                }
            }
            catch (Exception ex)
            {
                InfoMessageDialog.Show(this.MainWindow, 
                                       Catalog.GetString("Error while reading file: {0}"),
                                       ex.Message);
            }
            finally
            {
                chooser.Destroy();
            }
        }
        
        internal void OnReopenItemActivated(object o, EventArgs e)
        {
            UserInterface.Instance.ProcessEvents();
            for (int ix = 0; ix < Settings.Current.ReopenPaths.Count; ++ix)
            {
                string path = Settings.Current.ReopenPaths[ix];
                this.ShowProgress((double)ix / (double)Settings.Current.ReopenPaths.Count);

                try
                {
                    bool ignore_interdependence = ix < Settings.Current.ReopenPaths.Count-1;
                    this.AddSpectrum(CSV.Read(path), false, false, ignore_interdependence);
                }
                catch (Exception ex)
                {
                    InfoMessageDialog.Show(this.MainWindow,
                                           Catalog.GetString("Cannot reopen file: {0}"),
                                           ex.Message);
                }
            }
            this.CurrentSpectra.NotifyChanges();
            this.ScrolledPlot.Plot.RequestUpdate();
            this.HideProgress();
            this.reopen_item.Sensitive = false;
        }

        internal void OnSaveButtonClicked(object o, System.EventArgs e)
        {
            try
            {
                this.SetWatchCursor(null, null);
                this.ProcessEvents();
                this.StopAllStreams();
                this.SaveAll(this.plot_info_list.Current.CurrentSpectra, string.Empty, false);
                this.UpdateProperties();
            }
            finally
            {
                this.SetArrowCursor(null, null);
            }
        }
        
        internal void OnSaveContainerClicked(object o, EventArgs e)
        {
            try
            {
                this.SetWatchCursor(null, null);
                this.ProcessEvents();

                this.StopAllStreams();

                using (var chooser = new Dialog.FileChooserDialog(Catalog.GetString("Specify container file name to save spectra"), 
                                                                  this.MainWindow,
                                                                  FileChooserAction.Save,
                                                                  Stock.Save,
                                                                  ResponseType.Ok,
                                                                  Stock.Cancel,
                                                                  ResponseType.Cancel))
                {
                    foreach (FileTypeInfo type in this.FileTypes)
                    {
                        if (type.RwType != FileRwType.ReadOnly && type.IsContainer)
                            chooser.AddFilter(this.GetFileFilter(type), type.Extensions.First());
                    }
                    FileTypeInfo file_type = new CsvContainerFileTypeInfo();
                    OutputCulture output_culture = Settings.Current.StandardOutputCulture;
                    chooser._CurrentName  = Catalog.GetString("untitled_container");
                    chooser.Filter = this.GetFileFilter(file_type);
                    chooser.OutputCulture = output_culture;
                
                    if (string.IsNullOrEmpty(Settings.Current.ContainerSaveFilePath))
                        chooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
                    else
                        chooser.SetCurrentFolder(Settings.Current.ContainerSaveFilePath);

                    chooser.DoOverwriteConfirmation = true;
                
                    try
                    {
                        int choice = chooser._Run();
                        file_type = this.GetFileTypeInfo(chooser.Filter, false, true);

                        output_culture = chooser.OutputCulture;
                    
                        if (choice == (int)ResponseType.Ok)
                        {
                            chooser.Hide();
                            file_type.WriteContainer(plot_info_list.Current.CurrentSpectra.Values.ToList(),
                                                     this.ScrolledPlot.CreateMetaData(),
                                                     chooser.Filename,
                                                     this.OutputCultureInfo(chooser.OutputCulture));
                            Settings.Current.ContainerSaveFilePath = chooser.CurrentFolder;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                        InfoMessageDialog.Show(this.MainWindow,
                                               Catalog.GetString("Error while saving spectrum container: {0}"),
                                               ex.Message);
                    }
                    finally
                    {
                        chooser.Destroy();
                    }
                }
            }
            finally
            {
                this.SetArrowCursor(null, null);
            }
        }
        
        public void SaveAll(ICurrentSpectra current_spectra, string first_fname, bool save_all)
        {
            bool anticipate = false;
            string last_fname = string.Empty;
            
            // we need a copy because the list can be changed within the loop
            CurrentSpectra spectra = new CurrentSpectra((CurrentSpectra)current_spectra);

            var keys = new List<string>(spectra.Keys);
            keys.Sort();

            OutputCulture output_culture = this.StandardOutputCulture;
            FileTypeInfo file_type = new CsvFileTypeInfo();
            bool? cancel = null;

            for (int ix = 0; ix < keys.Count; ++ix)
            {
                if (ix == keys.Count-1)
                    cancel = false;
                
                string key = keys[ix];
                Spectrum spectrum = spectra[key];
                
                if (spectrum.IsPreview)
                    continue;

                bool new_spectrum = false;
                if ((spectrum.SpectrumType == SpectrumType.Stream ||
                     spectrum.SpectrumType == SpectrumType.Single ||
                     spectrum.SpectrumType == SpectrumType.ProcessedFluidSpecimen ||
                     spectrum.SpectrumType == SpectrumType.ProcessedSolidSpecimen ||
                     spectrum.SpectrumType == SpectrumType.Unknown))
                {
                    new_spectrum = true;
                }

                if (spectrum.FileInfo == null)
                {
                    try
                    {
                        if (new_spectrum)
                            UserInterface._SetCreator(spectrum);
                        
                        string fname;
                        if (string.IsNullOrEmpty(first_fname))
                        {
                            fname = string.Empty;
                            if (anticipate)
                                fname = this.AnticipateFilename(last_fname);
                            if (string.IsNullOrEmpty(fname))
                                fname = this.GenerateFilename(spectrum);
                        }
                        else
                        {
                            fname = first_fname;
                            first_fname = string.Empty;
                            anticipate = true;
                        }

                        if (save_all)
                        {
                            string fullname = Path.Combine(Settings.Current.SaveFilePath, fname);
                            if (fullname.EndsWith("." + file_type.Extensions.First(), StringComparison.InvariantCulture) == false)
                                fullname += $".{file_type.Extensions.First()}";
                            
                            bool do_write = true;
                            if (File.Exists(fullname))
                            {
                                var dialog = new OverwriteSpectrumDialog(this.MainWindow, Path.GetFileName(fullname));
                                ResponseType choice = (ResponseType)dialog.Run();
                                dialog.Destroy();
                                do_write = (choice == ResponseType.Yes);
                            }
                            
                            if (do_write)
                            {
                                file_type.Write(spectrum, fullname, this.OutputCultureInfo(output_culture));
                                spectrum.FileInfo = new FileInfo(fullname);
                                last_fname = fname;
                                continue;
                            }
                        }

                        save_all = keys.Count > 1;
                        bool again = false;
                        string current_fname = fname;
                        do 
                        {
                            last_fname = this.SaveNewSpectrum(spectrum, 
                                                              current_fname,
                                                              ref file_type,
                                                              ref save_all,
                                                              ref again, 
                                                              ref cancel,
                                                              ref output_culture);
                            current_fname = last_fname;
                        }
                        while (again);

                        if (cancel == true)
                            break;

                        if (fname != last_fname)
                                anticipate = true;
                    }
                    catch (NullReferenceException ex)
                    {
                        throw ex;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                        InfoMessageDialog.Show(this.MainWindow,
                                               Catalog.GetString("Cannot save spectrum '{0}': {1}"),
                                               spectrum.Label, ex.Message);
                    }
                }
                else if (spectrum.SpectrumType != SpectrumType.QuickStream)
                {
                    try
                    {
                        file_type.Write(spectrum, spectrum.FileInfo.FullName, spectrum.CultureInfo);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                        InfoMessageDialog.Show(this.MainWindow,
                                               Catalog.GetString("Cannot save spectrum '{0}' to file '{1}': {2}"),
                                               spectrum.Label, spectrum.Id.Remove(0, 12), ex.Message);
                    }
                }
            }
        }
        
        internal void OnPrintButtonClicked(object o, EventArgs e)
        {
            var p = new Integration.Printer(this.MainWindow, this.ScrolledPlot.Plot);
        }
        
        internal void OnSendCvscButtonClicked(object o, EventArgs e)
        {
            UserInterface.Instance.ProcessEvents();
            try
            {
                string temp_dir  = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                Directory.CreateDirectory(temp_dir);
                string temp_file = Path.Combine(temp_dir, Catalog.GetString("untitled_container") + ".csvc");
                
                CSV.WriteContainer(this.plot_info_list.Current.CurrentSpectra.Values.ToList(),
                                   temp_file,
                                   this.OutputCultureInfo(this.StandardOutputCulture));
                
                MailClient.OpenWithAttachment(temp_file);
                
                GLib.Timeout.Add(60000, () =>
                {
#pragma warning disable RECS0022
                    try
                    {
                        Directory.Delete(temp_dir, true);
                    }
                    catch { }
#pragma warning restore RECS0022
                    return false;
                });
            }
            catch (Exception ex)
            {
                InfoMessageDialog.Show(this.MainWindow, 
                                       Catalog.GetString("Cannot send CSVC file: {0}"),
                                       ex.Message);
            }
        }
        
        internal void OnSendPngButtonClicked(object o, EventArgs e)
        {
            UserInterface.Instance.ProcessEvents();
            try
            {
                string temp_dir  = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                Directory.CreateDirectory(temp_dir);
                string temp_file = Path.Combine(temp_dir, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".png");
                
                System.Drawing.Bitmap bmp = this.ScrolledPlot.Plot.GetBitmap(300f, 300f);
                bmp.Save(temp_file, System.Drawing.Imaging.ImageFormat.Png);
                
                MailClient.OpenWithAttachment(temp_file);
                
                GLib.Timeout.Add(60000, () =>
                {
#pragma warning disable RECS0022
                    try
                    {
                        Directory.Delete(temp_dir, true);
                    }
                    catch { }
#pragma warning restore RECS0022
                    return false;
                });
            }
            catch (Exception ex)
            {
                InfoMessageDialog.Show(this.MainWindow,
                                       Catalog.GetString("Cannot send PNG file: {0}"),
                                       ex.Message);
            }
        }
        
        internal void OnCopyButtonClicked(object o, System.EventArgs e)
        {
            if (MainWindow.Focus is TextView)
            {
                TextView text = MainWindow.Focus as TextView;
                text.Buffer.CopyClipboard(Clipboard.Get(Gdk.Selection.Clipboard));
                return;
            }
            else if (MainWindow.Focus is Entry)
            {
                Entry entry = MainWindow.Focus as Entry;
                entry.CopyClipboard();
                return;
            }
            this.CopyToClipboard(this.ScrolledPlot.Plot, true);
        }
        
        internal void OnCopyPlotDataItemClicked(object o, System.EventArgs e)
        {
            this.CopyToClipboard(this.ScrolledPlot.Plot, false);
        }
        
        internal void OnAnnotationSettingsItemClicked(object o, EventArgs e)
        {
            var d = new AnnotationSettingsDialog(this.MainWindow);
        }
        
        internal void OnInsertTextItemClicked(object o, EventArgs e)
        {
            this.CancelAnnotations();
            
            var dialog = new Gtk.Dialog(Catalog.GetString("Text annotation"),
                                        this.MainWindow,
                                        DialogFlags.Modal,
                                        Stock.Ok,
                                        ResponseType.Ok,
                                        Stock.Cancel,
                                        ResponseType.Cancel);
            
            dialog.Default = dialog.ActionArea.Children[1];
            dialog.HasSeparator = true;
            dialog.Resizable    = false;
            
            Alignment align0 = new Alignment(0f, 0f, 0f, 0f);
            Alignment align1 = new Alignment(0f, 0.5f, 0f, 0f);
            Alignment align2 = new Alignment(0f, 0.5f, 0f, 0f);
            Alignment align3 = new Alignment(1f, 0.5f, 0f, 0f);
            Alignment align4 = new Alignment(1f, 0.5f, 0f, 0f);
            
            Label label1 = new Label(Catalog.GetString("Insert Text:"));
            Label label2 = new Label(Catalog.GetString("Font settings:"));
            TextView text  = new TextView();
            FontButton  fbutton = new FontButton();
            ColorButton cbutton = new ColorButton(new Gdk.Color(0, 0, 0));
            
            if (string.IsNullOrEmpty(Settings.Current.AnnotationTextDefaultFont) == false)
                fbutton.SetFontName(Settings.Current.AnnotationTextDefaultFont);
            
            cbutton.Color = UserInterface.GdkColor(Settings.Current.AnnotationTextDefaultColor);
            
            text.WidthRequest  = 200;
            text.HeightRequest = 80;
            text.AcceptsTab = false;

            align1.Add(label1);
            align2.Add(label2);
            align3.Add(fbutton);
            align4.Add(cbutton);

            Table table = new Table(3, 2, false)
            {
                ColumnSpacing = 10,
                RowSpacing = 5
            };
            table.Attach(align1, 0, 1, 0, 1);
            table.Attach(text,   1, 3, 0, 1);
            table.Attach(align2, 0, 1, 1, 2);
            table.Attach(align3, 1, 2, 1, 2);
            table.Attach(align4, 2, 3, 1, 2, AttachOptions.Fill, AttachOptions.Fill, 0, 0);

            align0.Add(table);
            align0.BorderWidth = 5;
            dialog.VBox.PackStart(align0);
            dialog.ShowAll();
            
            ResponseType choice = (ResponseType)dialog.Run();
            string s = text.Buffer.Text;
            System.Drawing.Font  font  = UserInterface.GetFontFromString(fbutton.FontName);
            System.Drawing.Color color = UserInterface.DrawingColor(cbutton.Color);
            
            dialog.Destroy();
            
            if (choice != ResponseType.Ok || string.IsNullOrEmpty(s))
                return;

            this.ScrolledPlot.GetPointer(out int x, out int y);
            Text ta = new Text(this.ScrolledPlot.Plot, s, font, color)
            {
                X = this.ScrolledPlot.Plot.GetWorldXCoord(x),
                Y = this.ScrolledPlot.Plot.GetWorldYCoord(y)
            };
            this.ScrolledPlot.Plot.Annotations.Add(ta);
            this.ScrolledPlot.Plot.AsyncRefresh(true);
        }
        
        internal void OnInsertLineItemClicked(object o, EventArgs e)
        {
            this.CancelAnnotations();

            Line a = new Line(this.ScrolledPlot.Plot,
                              Settings.Current.AnnotationLineWidth,
                              Settings.Current.AnnotationLineColor,
                              Settings.Current.AnnotationLineStyle);
            this.ScrolledPlot.Plot.Annotations.Add(a);
        }
        
        internal void OnInsertArrowItemClicked(object o, EventArgs e)
        {
            this.CancelAnnotations();

            var a = new NPlotEngine.Overlay.Annotation.Arrow(this.ScrolledPlot.Plot,
                                                             Settings.Current.AnnotationArrowLineWidth,
                                                             Settings.Current.AnnotationArrowColor,
                                                             Settings.Current.AnnotationArrowLineStyle,
                                                             Settings.Current.AnnotationArrowCapStyle);
            this.ScrolledPlot.Plot.Annotations.Add(a);
        }
        
        internal void OnInsertDoublearrowItemClicked(object o, EventArgs e)
        {
            this.CancelAnnotations();

            var a = new DoubleArrow(this.ScrolledPlot.Plot,
                                    Settings.Current.AnnotationArrowLineWidth,
                                    Settings.Current.AnnotationArrowColor,
                                    Settings.Current.AnnotationArrowLineStyle,
                                    Settings.Current.AnnotationArrowCapStyle);
            this.ScrolledPlot.Plot.Annotations.Add(a);
        }

        internal void OnInsertRectangleItemClicked(object o, EventArgs e)
        {
            this.CancelAnnotations();

            var a = new Rectangle(this.ScrolledPlot.Plot,
                                  Settings.Current.AnnotationLineWidth,
                                  Settings.Current.AnnotationLineColor,
                                  Settings.Current.AnnotationLineStyle);
            this.ScrolledPlot.Plot.Annotations.Add(a);
        }
        
        internal void OnInsertEllipseItemClicked(object o, EventArgs e)
        {
            this.CancelAnnotations();

            var a = new Ellipse(this.ScrolledPlot.Plot,
                                Settings.Current.AnnotationLineWidth,
                                Settings.Current.AnnotationLineColor,
                                Settings.Current.AnnotationLineStyle);
            this.ScrolledPlot.Plot.Annotations.Add(a);
        }
        
        internal void OnClearAnnotationsItemClicked(object o, EventArgs e)
        {
            this.ScrolledPlot.Plot.Annotations.Clear();
            this.ScrolledPlot.Plot.AsyncRefresh(true);
            this.UIPlotControlsSensitive(true);
        }
        
        private void ClipboardGetFunc(Clipboard clipboard, SelectionData data, uint info)
        {
            Console.WriteLine("External application getting clipboard data");

            switch (info)
            {
                
            case 0:
                if (this.clipboard_pixbuf_data != null)
                    data.SetPixbuf(this.clipboard_pixbuf_data);
                break;

            case 1:
                data.Text = this.clipboard_csv_data;
                break;

            case 2:
                data.Set(Gdk.Atom.Intern("text/plain;charset=utf-8", false), 8, Encoding.UTF8.GetBytes(this.clipboard_csv_data));
                break;

            }
        }
        
        private void ClipboardClearFunc(Clipboard clipboard)
        {
            Console.WriteLine("Clearing clipboard data");
        }
        
        internal void OnExportButtonClicked(object o, System.EventArgs e)
        {
            var chooser = new Dialog.FileChooserDialog(Catalog.GetString("Specify file name to export a figure"), 
                                                       this.MainWindow,
                                                       FileChooserAction.Save,
                                                       Stock.Save,
                                                       ResponseType.Ok,
                                                       Stock.Cancel,
                                                       ResponseType.Cancel);
            FileFilter filter = new FileFilter
            {
                Name = Catalog.GetString("PNG image")
            };
            filter.AddPattern("*.png");
            filter.Data.Add("type", "png");
            chooser.AddFilter(filter);
            filter = new FileFilter
            {
                Name = Catalog.GetString("EMF metafile")
            };
            filter.AddPattern("*.emf");
            filter.Data.Add("type", "emf");
            chooser.AddFilter(filter);
            filter = new FileFilter
            {
                Name = Catalog.GetString("BMP image")
            };
            filter.AddPattern("*.bmp");
            filter.Data.Add("type", "bmp");
            chooser.AddFilter(filter);
            filter = new FileFilter
            {
                Name = Catalog.GetString("GIF image")
            };
            filter.AddPattern("*.gif");
            filter.Data.Add("type", "gif");
            chooser.AddFilter(filter);
            filter = new FileFilter
            {
                Name = Catalog.GetString("JPG image")
            };
            filter.AddPattern("*.jpg");
            filter.Data.Add("type", "jpg");
            chooser.AddFilter(filter);
            filter = new FileFilter
            {
                Name = Catalog.GetString("Comma separated values")
            };
            filter.AddPattern("*.csv");
            filter.Data.Add("type", "csv");
            chooser.AddFilter(filter);
            chooser.DoOverwriteConfirmation = true;

            chooser.CurrentName = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
            
            if (string.IsNullOrEmpty(Settings.Current.ExportFilePath) == false)
                chooser.SetCurrentFolder(Settings.Current.ExportFilePath);
            else
                chooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));

            try
            {
                ResponseType choice = (ResponseType)chooser.Run();
                
                if (choice == ResponseType.Ok)
                {
                    chooser.Hide();
                    UserInterface.Instance.ProcessEvents();
                    
                    System.Drawing.Bitmap bmp = this.ScrolledPlot.Plot.GetBitmap(EXPORT_IMAGE_RESOLUTION,
                                                                                 EXPORT_IMAGE_RESOLUTION);
                    
                    switch ((string)chooser.Filter.Data["type"])
                    {
                        
                    case "png":
                        if (Path.GetExtension(chooser.Filename).ToLower() == ".png")
                            bmp.Save(chooser.Filename, System.Drawing.Imaging.ImageFormat.Png);
                        else
                            bmp.Save(Path.ChangeExtension(chooser.Filename, ".png"), System.Drawing.Imaging.ImageFormat.Png);
                        break;
                    
                    case "emf":
                        if (Path.GetExtension(chooser.Filename).ToLower() == ".emf")
                            bmp.Save(chooser.Filename, System.Drawing.Imaging.ImageFormat.Emf);
                        else
                            bmp.Save(Path.ChangeExtension(chooser.Filename, ".emf"), System.Drawing.Imaging.ImageFormat.Emf);
                        break;
                        
                    case "bmp":
                        if (Path.GetExtension(chooser.Filename).ToLower() == ".bmp")
                            bmp.Save(chooser.Filename, System.Drawing.Imaging.ImageFormat.Bmp);
                        else
                            bmp.Save(Path.ChangeExtension(chooser.Filename, ".bmp"), System.Drawing.Imaging.ImageFormat.Bmp);
                        break;
                        
                    case "gif":
                        if (Path.GetExtension(chooser.Filename).ToLower() == ".gif")
                            bmp.Save(chooser.Filename, System.Drawing.Imaging.ImageFormat.Gif);
                        else
                            bmp.Save(Path.ChangeExtension(chooser.Filename, ".gif"), System.Drawing.Imaging.ImageFormat.Gif);
                        break;
                        
                    case "jpg":
                        if (Path.GetExtension(chooser.Filename).ToLower() == ".jpg")
                            bmp.Save(chooser.Filename, System.Drawing.Imaging.ImageFormat.Jpeg);
                        else
                            bmp.Save(Path.ChangeExtension(chooser.Filename, ".jpg"), System.Drawing.Imaging.ImageFormat.Jpeg);
                        break;
                        
                    case "csv":
                        string fname = chooser.Filename;
                        if (Path.GetExtension(fname).ToLower() != ".csv")
                            fname = Path.ChangeExtension(fname, ".csv");
                        var data_sets = this.ScrolledPlot.Plot.DataSets.Values.ToList();
                        var headers = new List<string>
                        {
                            string.Empty
                        };
                        foreach (DataSet ds in this.ScrolledPlot.Plot.DataSets.Values)
                        {
                            headers.Add(ds.Label);
                        }
                        switch (Settings.Current.AxisType)
                        {
                            
                        case AxisType.Wavelength:
                            headers[0] = Catalog.GetString("Wavelength / nm");
                            CSV.Write(headers, data_sets, 1.0, fname, this.OutputCultureInfo(chooser.OutputCulture));
                            break;
                            
                        case AxisType.Wavecount:
                            headers[0] = Catalog.GetString("Wavenumber / cm^-1");
                            CSV.Write(headers, data_sets, 5.0, fname, this.OutputCultureInfo(chooser.OutputCulture));
                            break;
                        }
                        break;
                        
                    default:
                        throw new NotSupportedException(Catalog.GetString("Cannot export figure: Unknown export format."));
                    }
                    
                    Settings.Current.ExportFilePath = chooser.CurrentFolder;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception while exporting figure: {ex}");
                InfoMessageDialog.Show(this.MainWindow, 
                                       Catalog.GetString("Error while exporting figure: {0}"),
                                       ex.Message);
            }
            finally
            {
                chooser.Destroy();
            }
        }
        
        internal void OnNewTabItemClicked(object o, EventArgs e)
        {
            this.AddPlotTab();
        }

        internal void OnCloseTabItemClicked(object o, EventArgs e)
        {
            this.RemovePlotTab(this.plot_info_list.Current);
        }
        
        internal void OnSetUnchangedBackgroundItemClicked(object o, EventArgs e)
        {
            this.plot_info_list.BackgroundType = BackgroundType.Unchanged;
            this.plot_info_list.Background = this.plot_info_list.Current;
            this.plot_tv.UpdateFigureNames();
            this.UpdateFigureControls();
        }
        
        internal void OnSetGrayscaledBackgroundItemClicked(object o, EventArgs e)
        {
            this.plot_info_list.BackgroundType = BackgroundType.Grayscaled;
            this.plot_info_list.Background = this.plot_info_list.Current;
            this.plot_tv.UpdateFigureNames();
            this.UpdateFigureControls();
        }
        
        internal void OnSetDashedBackgroundItemClicked(object o, EventArgs e)
        {
            this.plot_info_list.BackgroundType = BackgroundType.Dashed;
            this.plot_info_list.Background = this.plot_info_list.Current;
            this.plot_tv.UpdateFigureNames();
            this.UpdateFigureControls();
        }

        internal void OnClearBackgroundItemClicked(object o, EventArgs e)
        {
            this.plot_info_list.Background = null;
            this.plot_info_list.UpdateBackground();
            this.plot_tv.UpdateFigureNames();
            this.lock_axes_item.Active = false;
            this.UpdateFigureControls();
        }
        
        internal void OnSyncAxesItemClicked(object o, EventArgs e)
        {
            this.plot_info_list.Current.ScrolledPlot.Plot.SyncAxes();
        }
        
        internal void OnLockAxesItemToggled(object o, EventArgs e)
        {
            CheckMenuItem c = o as CheckMenuItem;
            this.ScrolledPlot.Plot.LockAxes = c.Active;
            this.SetLockAxesImage(c.Active);
        }
        
        private void SetLockAxesImage(bool val)
        {
            if (val)
            {
                this.lock_axes_image.Stock = "hiperscan.locked";
                this.lock_axes_image.TooltipText = Catalog.GetString("Axes locked");
            }
            else
            {
                this.lock_axes_image.Stock = "hiperscan.unlocked";
                this.lock_axes_image.TooltipText = Catalog.GetString("Axes unlocked");
            }
        }

        internal void OnStartButtonClicked(object o, EventArgs e)
        {
            this.CheckForChangedDeviceProperties();
            if (this.active_devices.Count > 0)
                this.plot_info_list.Current.CurrentSpectra.AddHistoryEntry();

            foreach (IDevice dev in this.active_devices)
            {
                if (dev.CanAcquireSpectra == false)
                    continue;

                this.StartStream(dev);
            }
        }

        internal void OnQuickstreamButtonClicked(object o, EventArgs e)
        {
            this.CheckForChangedDeviceProperties();
            foreach (IDevice dev in this.active_devices)
            {
                if (dev.CanAcquireSpectra == false)
                    continue;

                this.StartQuickStream(dev);
            }
        }
        
        internal void OnStopButtonClicked(object o, EventArgs e)
        {
            foreach (IDevice dev in this.active_devices.Connected)
            {
                if (dev.CanAcquireSpectra == false)
                    continue;

                this.StopStream(dev);
            }
        }

        internal void OnSingleButtonClicked(object o, EventArgs e)
        {
            this.CheckForChangedDeviceProperties();
            foreach (IDevice dev in this.active_devices.Connected)
            {
                if (dev.CanAcquireSpectra == false)
                    continue;

                this.StartSingle(dev);
            }
        }
        
        internal void OnShareDeviceClicked(object o, EventArgs e)
        {
            this.CheckForChangedDeviceProperties();
            var sdm = new SharedDevicesManagerDialog(this.DeviceList);
        }
        
        internal void OnRefreshDeviceListClicked(object o, EventArgs e)
        {
            this.DeviceList.ResetBlacklist();

            if (this.DeviceList.AutoUpdate == false)
                this.DeviceList.Update();
        }
        
        internal void OnConnectRemoteDeviceClicked(object o, EventArgs e)
        {
            this.ConnectRemoteDevice();
        }
        
        internal void OnDisconnectRemoteDeviceClicked(object o, EventArgs e)
        {
            foreach (IDevice dev in this.active_devices)
            {
                if (dev.Info.IsRemote)
                    this.DisconnectRemoteDevice(dev);
            }
        }

        internal void OnUndoButtonClicked(object o, EventArgs e)
        {
            this.plot_info_list.Current.CurrentSpectra.Undo();
            this.UpdatePlot(this.plot_info_list.Current);
        }
        
        internal void OnRedoButtonClicked(object o, EventArgs e)
        {
            this.plot_info_list.Current.CurrentSpectra.Redo();
            this.UpdatePlot(this.plot_info_list.Current);
        }
        
        private void OnFirstEntryReached(CurrentSpectra current_spectra)
        {
            if (current_spectra.PlotInfo != this.plot_info_list.Current)
                return;

            this.undo_item.Sensitive   = false;
            this.undo_button.Sensitive = false;
        }

        private void OnLastEntryReached(CurrentSpectra current_spectra)
        {
            if (current_spectra.PlotInfo != this.plot_info_list.Current)
                return;

            this.redo_item.Sensitive   = false;
            this.redo_button.Sensitive = false;
        }
        
        private void OnUndoPossible(CurrentSpectra current_spectra)
        {
            if (current_spectra.PlotInfo != this.plot_info_list.Current)
                return;

            bool val = this.DeviceList.AllIdleOrDisconnected;
            this.undo_item.Sensitive   = val;
            this.undo_button.Sensitive = val;
        }

        private void OnRedoPossible(CurrentSpectra current_spectra)
        {
            if (current_spectra.PlotInfo != this.plot_info_list.Current)
                return;

            bool val = this.DeviceList.AllIdleOrDisconnected;
            this.redo_item.Sensitive   = val;
            this.redo_button.Sensitive = val;
        }

        internal void OnDefaultOutputCultureComboChanged(object o, EventArgs e)
        {
            ComboBox c = o as ComboBox;
            this.StandardOutputCulture = (OutputCulture)c.Active;
        }

        internal void OnPlotTypeComboChanged(object o, EventArgs e)
        {
            ComboBox c = o as ComboBox;

            if (c.Active == -1 || c.GetActiveIter(out TreeIter iter) == false)
            {
                this.plot_info_list.Current.PlotType = new UndefinedPlotTypeInfo();
                return;
            }

            // FIXME: zu kompliziert Menu->PlotType->Combo
            //            this.PlotType = (PlotTypeInfo)c.Model.GetValue(iter, 1);
            RadioMenuItem item = (RadioMenuItem)c.Model.GetValue(iter, 2);
            item.Active = true;

            if (__update_on_plot_type_combo_change)
                this.UpdatePlot(this.plot_info_list.Current);

            if (this.active_devices != null && this.PlotType.NeedAbsorbance)
            {
                foreach (IDevice dev in this.active_devices)
                {
                    if (!dev.IsConnected)
                        continue;

                    if (dev.CurrentConfig.HasReferenceSpectrum == false || 
                        dev.CurrentConfig.DarkIntensity.Intensity.Equals(0.0))
                    {
                        this.bg_message_is_pending = true;
                    }
                }
            }
            
            if (this.bg_message_is_pending)
            {
                InfoMessageDialog.Show(this.MainWindow,
                                       Catalog.GetString("Cannot display one or more spectrum sources. " +
                                                         "Please specify reference spectrum and " +
                                                         "dark intensity to solve this problem."));
                this.bg_message_is_pending = false;
            }
        }
        
        internal void OnStandardToolbarItemToggled(object o, EventArgs e)
        {
            CheckMenuItem i = o as CheckMenuItem;
            this.UpdatePluginInfos();
            Settings.Current.StandardToolbarVisible = i.Active;
        }

        internal void OnFigureToolbarItemToggled(object o, EventArgs e)
        {
            CheckMenuItem i = o as CheckMenuItem;
            this.UpdatePluginInfos();
            Settings.Current.FigureToolbarVisible = i.Active;
        }

        internal void OnExtensionsToolbarItemToggled(object o, EventArgs e)
        {
            CheckMenuItem i = o as CheckMenuItem;
            this.UpdatePluginInfos();
            Settings.Current.ExtensionsToolbarVisible = i.Active;
        }

        internal void OnDataSourcesButtonToggled(object o, EventArgs e)
        {
            ToggleToolButton b = o as ToggleToolButton;
            
            if (b.Active)
            {
                this.data_sources_frame.NoShowAll = false;
                this.data_sources_frame.ShowAll();
            }
            else
            {
                this.data_sources_frame.Visible = false;
                this.data_sources_frame.NoShowAll = true;
            }
                
            this.data_sources_item.Active       = b.Active;
            Settings.Current.DataSourcesVisible = b.Active;
        }
        
        internal void OnDataSourcesItemToggled(object o, EventArgs e)
        {
            CheckMenuItem c = o as CheckMenuItem;
            this.data_sources_button.Active = c.Active;
        }
        
        internal void OnPropertyEditorButtonToggled(object o, EventArgs e)
        {
            ToggleToolButton b = o as ToggleToolButton;
            
            if (b.Active)
            {
                this.property_handlebox.NoShowAll = false;
                this.property_handlebox.ShowAll();
            }
            else
            {
                this.property_handlebox.Visible = false;
                this.property_handlebox.NoShowAll = true;
            }

            this.properties_item.Active            = b.Active;
            Settings.Current.PropertyEditorVisible = b.Active;
        }
        
        internal void OnPlotBrowserItemToggled(object o, EventArgs e)
        {
            CheckMenuItem c = o as CheckMenuItem;
            this.plot_browser_button.Active = c.Active;
        }

        internal void OnPropertyEditorItemToggled(object o, EventArgs e)
        {
            CheckMenuItem c = o as CheckMenuItem;
            this.property_editor_button.Active = c.Active;
        }
        
        internal void OnPlotBrowserButtonToggled(object o, EventArgs e)
        {
            ToggleToolButton b = o as ToggleToolButton;
            
            if (b.Active)
            {
                this.plot_browser_frame.NoShowAll = false;
                this.plot_browser_frame.ShowAll();
            }
            else
            {
                this.plot_browser_frame.Visible = false;
                this.plot_browser_frame.NoShowAll = true;
            }
            
            this.ScrolledPlot.Plot.LegendVisible = b.Active == false ||
                                                   this.legend_checkbutton.Active;

            this.plot_browser_item.Active = b.Active;
            Settings.Current.PlotBrowserVisible = b.Active;
        }
        
        internal void OnLegendCheckbuttonToggled(object o, EventArgs e)
        {
            CheckButton cb = o as CheckButton;
            UserInterface.Settings.Current.AlwaysShowLegend = cb.Active;
            if (this.plot_info_list.Current != null)
                this.ScrolledPlot.Plot.LegendVisible = !this.plot_browser_button.Active || cb.Active;
        }
        
        internal void OnZoomInButtonClicked(object o, EventArgs e)
        {
            this.ScrolledPlot.ZoomIn();
        }

        internal void OnZoomOutButtonClicked(object o, EventArgs e)
        {
            this.ScrolledPlot.ZoomOut();
        }
        
        internal void OnEnglishItemToggled(object o, EventArgs e)
        {
            RadioMenuItem r = o as RadioMenuItem;
            if (r.Active)
                this.StandardOutputCulture = OutputCulture.English;
        }
        
        internal void OnGermanItemToggled(object o, EventArgs e)
        {
            RadioMenuItem r = o as RadioMenuItem;
            if (r.Active)
                this.StandardOutputCulture = OutputCulture.German;
        }
        
        internal void OnSystemItemToggled(object o, EventArgs e)
        {
            RadioMenuItem r = o as RadioMenuItem;
            if (r.Active)
                this.StandardOutputCulture = OutputCulture.System;
        }

        internal void OnWavelengthItemToggled(object o, EventArgs e)
        {
            RadioMenuItem r = o as RadioMenuItem;
            if (r.Active == false)
                return;

            Settings.Current.AxisType = AxisType.Wavelength;
            foreach (PlotInfo pi in this.plot_info_list.Values)
            {
                pi.ScrolledPlot.Plot.ReverseXAxes = false;
                this.UpdatePlot(pi);
            }
        }

        internal void OnWavecountItemToggled(object o, EventArgs e)
        {
            RadioMenuItem r = o as RadioMenuItem;
            if (r.Active == false)
                return;

            Settings.Current.AxisType = AxisType.Wavecount;
            foreach (PlotInfo pi in this.plot_info_list.Values)
            {
                pi.ScrolledPlot.Plot.ReverseXAxes = true;
                this.UpdatePlot(pi);
            }
        }

        internal void OnHelpButtonClicked(object o, EventArgs e)
        {
            string url = AppDomain.CurrentDomain.BaseDirectory.Replace("\\", "/");
            
            if (url.EndsWith("/", StringComparison.InvariantCulture) == false)
                url += "/";
            
            this.ShowUri("file://" + url + "manual.pdf");
        }
        
        internal void OnAboutButtonClicked(object o, EventArgs e)
        {
            AboutDialog dialog = new AboutDialog
            {
                TransientFor = this.MainWindow,
                Parent = this.MainWindow,
                ProgramName = MainClass.ApplicationName,
                Version = MainClass.Assembly.GetName().Version.ToString(),
                Comments = (MainClass.Assembly.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false)[0]
                            as AssemblyDescriptionAttribute).Description + MainClass.InfoExtension
            };

            string build_date = string.Empty;
            if (string.IsNullOrEmpty(MainClass.BuildDate) == false)
                build_date = $"\n{Catalog.GetString("Build date:")} {MainClass.BuildDate}";

            dialog.Copyright = (MainClass.Assembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false)[0]
                                as AssemblyCopyrightAttribute).Copyright + build_date;

            dialog.WrapLicense = true;
            if (this.IsRelease == true)
            {
                dialog.License = Licenses.GPL3;
            }
            else
            {
                switch(this.CurrentLicense)
                {
                case LicenseTyoe.GPL3:
                    dialog.License = Licenses.GPL3;
                    break;

                case LicenseTyoe.HiperScan:
                    dialog.License = Licenses.HiperScan;
                    break;

                default:
                    dialog.License = Catalog.GetString("Missing license");
                    break;
                }
            }


            dialog.Authors = MainClass.Authors;
            dialog.Website = "http://www.hiperscan.com";
            dialog.Logo    = MainClass.QuickStepIcons[4];
            
            Gdk.Pixbuf[] icons = new Gdk.Pixbuf[5];
            icons[0] = dialog.RenderIcon(Stock.About, IconSize.Menu,         string.Empty);
            icons[1] = dialog.RenderIcon(Stock.About, IconSize.SmallToolbar, string.Empty);
            icons[2] = dialog.RenderIcon(Stock.About, IconSize.LargeToolbar, string.Empty);
            icons[3] = dialog.RenderIcon(Stock.About, IconSize.Button,       string.Empty);
            icons[4] = dialog.RenderIcon(Stock.About, IconSize.Dialog,       string.Empty);
            dialog.IconList = icons;
            dialog.WindowPosition = WindowPosition.Center;
            
            dialog.Run();
            dialog.Destroy();
        }
        
        internal void OnPlotTvSaveClicked(object o, EventArgs e)
        {
            foreach (Spectrum spectrum in this.plot_tv.GetSelectedSpectra())
            {
                if (spectrum.IsPreview)
                    continue;
                
                this.SaveNewSpectrum(spectrum, this.GenerateFilename(spectrum));
            }
            this.UpdateProperties();
        }
        
        internal void OnPlotTvCopyClicked(object o, EventArgs e)
        {
            var plot = new NPlotEngine.NPlotEngine((NPlotEngine.NPlotEngine)this.ScrolledPlot.Plot);
            var selected_spectra = this.plot_tv.GetSelectedSpectra();
            
            if (selected_spectra.Count == 0)
                return;
            
            var selected_ids = new List<string>();
            foreach (Spectrum spectrum in selected_spectra)
            {
                Console.WriteLine("Copy selected to clipboard: " + spectrum.Id);
                selected_ids.Add(spectrum.Id);
            }
            
            var ids = plot.DataSets.Keys.ToList();
            foreach (string id in ids)
            {
                if (selected_ids.Contains(id))
                    continue;

                plot.DataSets.Remove(id);
            }
            plot.ProcessChanges();
            
            this.CopyToClipboard(plot, false);
        }
        
        internal void OnPlotTvAutoColorClicked(object o, EventArgs e)
        {
            this.plot_tv.AutoColorSelected();
        }
        
        internal void OnPlotTvDeleteClicked(object o, EventArgs e)
        {
            this.plot_tv.DeleteSelected();
        }
        
        internal void OnPlotTvAddClicked(object o, EventArgs e)
        {
            this.plot_tv.AddSelected();
        }
        
        internal void OnPlotTvRemoveClicked(object o, EventArgs e)
        {
            this.plot_tv.RemoveSelected();
        }
        
        internal void OnPlotTvMoveUpClicked(object o, EventArgs e)
        {
            this.plot_tv.MoveSelectedUp();
        }
        
        internal void OnPlotTvMoveDownClicked(object o, EventArgs e)
        {
            this.plot_tv.MoveSelectedDown();
        }
        
        internal void OnLockAxesImageButtonPressed(object o, ButtonPressEventArgs e)
        {
            if (this.lock_axes_image.Sensitive == false)
                return;
            
            if (e.Event.Button == 1 && e.Event.Type == Gdk.EventType.ButtonPress)
                this.lock_axes_item.Toggle();
        }
    }
}