Exemplo n.º 1
0
 public void ForeachWidget <T> (WidgetAction <T> action) where T : class
 {
     for (int i = 0; i < notebook.NPages; i++)
     {
         GtkUtilities.ForeachWidget(notebook.GetNthPage(i) as Container, action);
     }
 }
Exemplo n.º 2
0
        protected override void OnStyleSet(Style old_style)
        {
            if (changing_style)
            {
                return;
            }

            changing_style = true;
            GtkUtilities.AdaptGtkRcStyle(this, typeof(TreeView));
            changing_style = false;

            base.OnStyleSet(old_style);
            RecomputeRowHeight = true;
            theme = Hyena.Gui.Theming.ThemeEngine.CreateTheme(this);

            // Save the drawable so we can reuse it
            Gdk.Drawable drawable = cell_context != null ? cell_context.Drawable : null;

            if (pango_layout != null)
            {
                pango_layout.Dispose();
                pango_layout = null;
            }

            cell_context          = new CellContext();
            cell_context.Theme    = theme;
            cell_context.Widget   = this;
            cell_context.Drawable = drawable;
        }
Exemplo n.º 3
0
        public void EnsureKeyPressInitiatesEditing()
        {
            // Click on clock node.
            explorerPresenter.SelectNode(".Simulations.Simulation.Field.Soil.Physical");
            GtkUtilities.WaitForGtkEvents();

            ProfileView profile = explorerPresenter.CurrentRightHandView as ProfileView;

            if (profile == null)
            {
                throw new Exception($"Soil.Physical view is not a ProfileView - it is a {explorerPresenter.CurrentRightHandView.GetType().Name}");
            }
            GridView grid = profile.ProfileGrid as GridView;

            // Click on top-right cell - this will be in the value column, and so will be editable.
            GtkUtilities.GetTreeViewCoordinates(grid.Grid, 0, 1, out int x, out int y);
            GtkUtilities.Click(grid.Grid, Gdk.EventType.ButtonPress, Gdk.ModifierType.None, GtkUtilities.ButtonPressType.LeftClick, x, y);

            // Grid should not be in edit mode at this point.
            Assert.IsFalse(grid.IsUserEditingCell);

            // Type the letter 'a' now that the cell is selected.
            GtkUtilities.SendKeyPress(grid.Grid, 'a');

            // Grid should now be in edit mode.
            Assert.IsTrue(grid.IsUserEditingCell);
        }
Exemplo n.º 4
0
        public void AddStockGenotype()
        {
            var simulations = new Simulations()
            {
                Children = new List <IModel>()
                {
                    new Models.GrazPlan.Stock()
                }
            };
            var fileName = Path.ChangeExtension(Path.GetTempFileName(), ".apsimx");

            simulations.Write(fileName);
            var explorerPresenter = UITestsMain.MasterPresenter.OpenApsimXFileInTab(fileName, onLeftTabControl: true);

            GtkUtilities.WaitForGtkEvents();

            var stock = explorerPresenter.ApsimXFile.FindInScope <Models.GrazPlan.Stock>();

            explorerPresenter.SelectNode(stock);
            explorerPresenter.ContextMenu.AddModel(explorerPresenter, EventArgs.Empty);
            GtkUtilities.WaitForGtkEvents();

            TreeView addModelsTree = (TreeView)ReflectionUtilities.GetValueOfFieldOrProperty("tree", explorerPresenter.CurrentPresenter);

            // Let's make sure we can add stock genotype resource - e.g. Angus.
            ActivateNode(addModelsTree, ".Models.GrazPlan.Genotypes.Cattle.Beef.Angus");
            Assert.AreEqual(1, stock.Children.Count);
            Assert.AreEqual(typeof(Genotype), stock.Children[0].GetType());
            var genotype = stock.Children[0] as Genotype;

            Assert.AreEqual("Angus", genotype.Name);
            Assert.AreEqual(500, genotype.BreedSRW);
        }
Exemplo n.º 5
0
        protected override void ConnectEvents()
        {
            base.ConnectEvents();

            // Service events
            ServiceManager.SourceManager.ActiveSourceChanged += OnActiveSourceChanged;
            ServiceManager.SourceManager.SourceUpdated       += OnSourceUpdated;

            ActionService.TrackActions ["SearchForSameArtistAction"].Activated += OnProgrammaticSearch;
            ActionService.TrackActions ["SearchForSameAlbumAction"].Activated  += OnProgrammaticSearch;

            // UI events
            view_container.SearchEntry.Changed += OnSearchEntryChanged;
            views_pane.SizeRequested           += delegate {
                SourceViewWidth.Set(views_pane.Position);
            };

            source_view.RowActivated += delegate {
                Source source = ServiceManager.SourceManager.ActiveSource;
                if (source is ITrackModelSource)
                {
                    ServiceManager.PlaybackController.NextSource = (ITrackModelSource)source;
                    // Allow changing the play source without stopping the current song by
                    // holding ctrl when activating a source. After the song is done, playback will
                    // continue from the new source.
                    if (GtkUtilities.NoImportantModifiersAreSet(Gdk.ModifierType.ControlMask))
                    {
                        ServiceManager.PlaybackController.Next();
                    }
                }
            };

            header_toolbar.ExposeEvent += OnToolbarExposeEvent;
        }
        public void DoubleClickGraphFolder()
        {
            ExplorerPresenter explorerPresenter = UITestUtilities.OpenBasicFileInGui();

            GtkUtilities.WaitForGtkEvents();

            IModel paddock = Apsim.Find(explorerPresenter.ApsimXFile, typeof(Zone));

            explorerPresenter.SelectNode(paddock);
            explorerPresenter.ContextMenu.AddModel(explorerPresenter, EventArgs.Empty);
            GtkUtilities.WaitForGtkEvents();

            TreeView addModelsTree = (TreeView)ReflectionUtilities.GetValueOfFieldOrProperty("tree", explorerPresenter.CurrentPresenter);

            // Now, we double click on the fertiliser node. This should add a fertiliser model.
            // For some reason, sending a double click event doesn't trigger the ActivateRow signal.
            // Therefore, we need to manually activate the row.
            //GtkUtilities.ClickOnTreeView(treeView, path, 0, EventType.TwoButtonPress, ModifierType.None, GtkUtilities.ButtonPressType.LeftClick);
            ActivateNode(addModelsTree, ".Models.Fertiliser");
            Assert.AreEqual(2, paddock.Children.Count);
            Assert.AreEqual(typeof(Models.Fertiliser), paddock.Children[1].GetType());

            // While we're at it, let's make sure we can add resource models - e.g. wheat.
            ActivateNode(addModelsTree, ".Models.PMF.Wheat");
            Assert.AreEqual(3, paddock.Children.Count);
            Assert.AreEqual(typeof(Plant), paddock.Children[2].GetType());
            Plant wheat = paddock.Children[2] as Plant;

            Assert.AreEqual("Wheat", wheat.ResourceName);
        }
Exemplo n.º 7
0
        public void TestEditingNullProperty()
        {
            ExplorerPresenter explorerPresenter = UITestUtilities.OpenResourceFileInTab(Assembly.GetExecutingAssembly(),
                                                                                        "UnitTests.ApsimNG.Resources.SampleFiles.NullSample.apsimx");

            GtkUtilities.WaitForGtkEvents();

            Simulations sims   = explorerPresenter.ApsimXFile;
            Soil        soil   = sims.FindInScope <Soil>();
            Sample      sample = soil.FindChild <Sample>();

            explorerPresenter.SelectNode(sample);
            GtkUtilities.WaitForGtkEvents();

            Assert.IsNull(sample.NO3);

            ProfileView view = explorerPresenter.CurrentRightHandView as ProfileView;
            GridView    grid = view.ProfileGrid as GridView;

            // Click on the first cell in the second column (the NO3N column) and type 1.1 and then hit enter.
            GtkUtilities.ClickOnGridCell(grid, 0, 1, Gdk.EventType.ButtonPress, Gdk.ModifierType.None, GtkUtilities.ButtonPressType.LeftClick);
            GtkUtilities.SendKeyPress(grid.Grid, '1');
            GtkUtilities.SendKeyPress(grid.Grid, '.');
            GtkUtilities.SendKeyPress(grid.Grid, '1');
            GtkUtilities.TypeKey(grid.Grid, Gdk.Key.Return, Gdk.ModifierType.None);

            // The sample's NO3N property should now be an array containing 1.1.
            Assert.NotNull(sample.NO3);
            Assert.AreEqual(new double[1] {
                1.1
            }, sample.NO3);
        }
Exemplo n.º 8
0
        public void OpenTestFileInTab()
        {
            // Open a simple .apsimx file in the GUI.
            explorerPresenter = UITestUtilities.OpenResourceFileInTab(Assembly.GetExecutingAssembly(),
                                                                      "UnitTests.ApsimNG.Resources.SampleFiles.BasicSimulation.apsimx");
            // Create a table model.
            model      = new DualTableModel();
            model.Name = "Table";

            // Create a datatable and assign it to the model.
            DataTable table = new DataTable();

            table.Columns.Add("test_col", typeof(string));
            table.Rows.Add("test");
            model.Tables = new List <DataTable>()
            {
                table, table.Copy()
            };

            // Add the model to the .apsimx file.
            Structure.Add(model, explorerPresenter.ApsimXFile);
            explorerPresenter.Refresh();

            // Select the table model in the GUI.
            explorerPresenter.SelectNode(model);
            GtkUtilities.WaitForGtkEvents();
        }
Exemplo n.º 9
0
        protected override bool OnKeyPressEvent(Gdk.EventKey evnt)
        {
            bool focus_search = false;

            if (Focus is Gtk.Entry && (GtkUtilities.NoImportantModifiersAreSet() &&
                                       evnt.Key != Gdk.Key.Control_L && evnt.Key != Gdk.Key.Control_R))
            {
                if (accel_group_active)
                {
                    RemoveAccelGroup(ActionService.UIManager.AccelGroup);
                    accel_group_active = false;

                    // Reinstate the AccelGroup as soon as the focus leaves the entry
                    Focus.FocusOutEvent += OnEntryFocusOutEvent;
                }
            }
            else
            {
                if (!accel_group_active)
                {
                    AddAccelGroup(ActionService.UIManager.AccelGroup);
                    accel_group_active = true;
                }
            }

            switch (evnt.Key)
            {
            case Gdk.Key.f:
                if (Gdk.ModifierType.ControlMask == (evnt.State & Gdk.ModifierType.ControlMask))
                {
                    focus_search = true;
                }
                break;

            case Gdk.Key.S:
            case Gdk.Key.s:
            case Gdk.Key.F3:
            case Gdk.Key.slash:
                focus_search = true;
                break;

            case Gdk.Key.F11:
                ActionService.ViewActions["FullScreenAction"].Activate();
                break;

            case Gdk.Key.F4:
                main_menu.Visible = !main_menu.Visible;
                break;
            }

            if (focus_search && !header.SearchEntry.Entry.HasFocus /* && !source_view.EditingRow*/)
            {
                header.SearchEntry.Entry.HasFocus = true;
                return(true);
            }

            return(base.OnKeyPressEvent(evnt));
        }
Exemplo n.º 10
0
        /// <summary>
        /// Activate a row in the TreeView. Would be good to find a better way of doing this.
        /// </summary>
        /// <param name="tree">An instance of UserInterface.Views.TreeView.</param>
        /// <param name="path">Path to the row. e.g. ".Simulations.ContinuousWheat.Paddock".</param>
        private void ActivateNode(TreeView tree, string path)
        {
            tree.SelectedNode = path;

            Gtk.TreeView treeView = (Gtk.TreeView)ReflectionUtilities.GetValueOfFieldOrProperty("treeview1", tree);
            Gtk.TreePath treePath = GetTreePath(tree, path);
            treeView.ActivateRow(treePath, treeView.Columns[0]);
            GtkUtilities.WaitForGtkEvents();
        }
Exemplo n.º 11
0
 protected override bool OnKeyPressEvent(Gdk.EventKey press)
 {
     // Have o act the same as enter - activate the selection
     if (GtkUtilities.NoImportantModifiersAreSet() && press.Key == Gdk.Key.o && ActivateSelection())
     {
         return(true);
     }
     return(base.OnKeyPressEvent(press));
 }
Exemplo n.º 12
0
        protected override void OnStyleUpdated()
        {
            base.OnStyleUpdated();

            CheckButton check = new CheckButton();

            interior_focus = GtkUtilities.StyleGetProperty <bool> (check, "interior-focus", false);
            focus_width    = GtkUtilities.StyleGetProperty <int> (check, "focus-line-width", -1);
            focus_padding  = GtkUtilities.StyleGetProperty <int> (check, "focus-padding", -1);
            padding        = interior_focus ? focus_width + focus_padding : 0;
        }
Exemplo n.º 13
0
        protected override void ConnectEvents()
        {
            base.ConnectEvents();

            // Service events
            ServiceManager.SourceManager.ActiveSourceChanged += OnActiveSourceChanged;
            ServiceManager.SourceManager.SourceUpdated       += OnSourceUpdated;

            ActionService.TrackActions ["SearchForSameArtistAction"].Activated += OnProgrammaticSearch;
            ActionService.TrackActions ["SearchForSameAlbumAction"].Activated  += OnProgrammaticSearch;

            (ActionService.ViewActions ["ShowCoverArtAction"] as Gtk.ToggleAction).Active = ShowCoverArt.Get();
            ActionService.ViewActions ["ShowCoverArtAction"].Activated += (o, a) => {
                ShowCoverArt.Set((o as Gtk.ToggleAction).Active);
                UpdateCoverArtDisplay();
            };

            // UI events
            view_container.SearchEntry.Changed += OnSearchEntryChanged;
            views_pane.SizeRequested           += delegate {
                SourceViewWidth.Set(views_pane.Position);
            };

            source_view.RowActivated += delegate {
                Source source  = ServiceManager.SourceManager.ActiveSource;
                var    handler = source.Properties.Get <System.Action> ("ActivationAction");
                if (handler != null)
                {
                    handler();
                }
                else if (source is ITrackModelSource)
                {
                    ServiceManager.PlaybackController.NextSource = (ITrackModelSource)source;
                    // Allow changing the play source without stopping the current song by
                    // holding ctrl when activating a source. After the song is done, playback will
                    // continue from the new source.
                    if (GtkUtilities.NoImportantModifiersAreSet(Gdk.ModifierType.ControlMask))
                    {
                        ServiceManager.PlaybackController.Next();
                    }
                }
            };

            if (!PlatformDetection.IsMeeGo)
            {
                header_toolbar.ExposeEvent += OnToolbarExposeEvent;
            }
        }
Exemplo n.º 14
0
        private void Edit(GridView grid, int row, int col, string contents)
        {
            // Get coordinates of top-left cell.
            GtkUtilities.GetTreeViewCoordinates(grid.Grid, 0, 0, out int x, out int y);

            // Click on the top-left cell. After this, typing anything should initiate an edit operation.
            GtkUtilities.Click(grid.Grid, Gdk.EventType.ButtonPress, Gdk.ModifierType.None, GtkUtilities.ButtonPressType.LeftClick, x, y);

            // Type the letter 'a' now that the cell is selected.
            foreach (char character in contents)
            {
                GtkUtilities.SendKeyPress(grid.Grid, character);
            }

            // Send return keypress, to apply changes.
            GtkUtilities.TypeKey(grid.Grid, Gdk.Key.Return, Gdk.ModifierType.None);
        }
Exemplo n.º 15
0
        public Gtk.FileChooserDialog CreateChooser(string title, FileChooserAction action)
        {
            var chooser = new Gtk.FileChooserDialog(title, this.Window, action)
            {
                DefaultResponse = ResponseType.Ok
            };

            chooser.AddButton(Stock.Cancel, ResponseType.Cancel);
            chooser.AddFilter(GtkUtilities.GetFileFilter(Catalog.GetString("PDF Documents"), new string [] { "pdf" }));
            chooser.AddFilter(GtkUtilities.GetFileFilter(Catalog.GetString("All Files"), new string [] { "*" }));

            var dirs = new string [] { "DOWNLOAD", "DOCUMENTS" }.Select(s => GetXdgDir(s))
            .Where(d => d != null)
            .ToArray();

            Hyena.Gui.GtkUtilities.SetChooserShortcuts(chooser, dirs);

            return(chooser);
        }
Exemplo n.º 16
0
        public NavigationControl()
        {
            back_button.Clicked += (o, e) => {
                if (web_view != null && web_view.CanGoBack)
                {
                    web_view.GoBack();
                }
            };

            forward_button.Clicked += (o, e) => {
                if (web_view != null && web_view.CanGoForward)
                {
                    web_view.GoForward();
                }
            };

            reload_button.Clicked += (o, e) => {
                if (web_view != null)
                {
                    web_view.Reload(!GtkUtilities.NoImportantModifiersAreSet());
                }
            };

            home_button.Clicked += (o, e) => {
                var handler = GoHomeEvent;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            };

            shortcut_menu_button = new MenuButton(home_button, shortcut_menu, true);

            UpdateNavigation();

            PackStart(back_button, false, false, 0);
            PackStart(forward_button, false, false, 0);
            PackStart(reload_button, false, false, 5);
            PackStart(shortcut_menu_button, false, false, 0);

            ShowAll();
            ClearLinks();
        }
Exemplo n.º 17
0
        protected override void OnStyleSet(Style old_style)
        {
            if (changing_style)
            {
                return;
            }

            changing_style = true;
            GtkUtilities.AdaptGtkRcStyle(this, typeof(TreeView));
            changing_style = false;

            base.OnStyleSet(old_style);

            // FIXME: legacy list foo
            if (ViewLayout == null)
            {
                OnInvalidateMeasure();
            }

            Theme = Hyena.Gui.Theming.ThemeEngine.CreateTheme(this);

            // Save the drawable so we can reuse it
            var drawable = cell_context?.Drawable;

            if (pango_layout != null)
            {
                cell_context.FontDescription.Dispose();
                pango_layout.Dispose();
                pango_layout                 = null;
                cell_context.Layout          = null;
                cell_context.FontDescription = null;
            }

            cell_context = new CellContext {
                Theme    = Theme,
                Widget   = this,
                Drawable = drawable
            };
            SetDirection();
        }
Exemplo n.º 18
0
        public void EnsureArrowKeysMoveCursorInsideCell()
        {
            // Click on clock node.
            explorerPresenter.SelectNode(".Simulations.Simulation.Field.Soil.Physical");
            GtkUtilities.WaitForGtkEvents();

            ProfileView profile = explorerPresenter.CurrentRightHandView as ProfileView;

            if (profile == null)
            {
                throw new Exception($"Soil.Physical view is not a ProfileView - it is a {explorerPresenter.CurrentRightHandView.GetType().Name}");
            }
            GridView grid = profile.ProfileGrid as GridView;

            GtkUtilities.ClickOnGridCell(grid, 0, 1, Gdk.EventType.TwoButtonPress, Gdk.ModifierType.None, GtkUtilities.ButtonPressType.LeftClick, wait: true);

            // Double clicking on the cell will highlight all text in the cell. If we press the left arrow, the cursor should move to the start of the cell.
            GtkUtilities.TypeKey(grid.Grid, Gdk.Key.Left, Gdk.ModifierType.None, wait: true);

            Entry editControl = ReflectionUtilities.GetValueOfFieldOrProperty("editControl", grid) as Entry;

            Assert.AreEqual(0, editControl.CursorPosition);

            // The cursor is at the start of the cell. Pressing the left key now should have no effect.
            GtkUtilities.TypeKey(grid.Grid, Gdk.Key.Left, Gdk.ModifierType.None, wait: true);
            Assert.AreEqual(0, editControl.CursorPosition);

            // Now keep hitting the right key until we get to the end of the end of the cell.
            for (int i = 1; i < editControl.Text.Length; i++)
            {
                GtkUtilities.TypeKey(grid.Grid, Gdk.Key.Right, Gdk.ModifierType.None, wait: true);
                Assert.AreEqual(i, editControl.CursorPosition);
            }

            // The cursor should now be at the far right of the cell. Pressing the right arrow now should have no effect.
            GtkUtilities.TypeKey(grid.Grid, Gdk.Key.Right, Gdk.ModifierType.None, wait: true);
            Assert.AreEqual(editControl.Text.Length, editControl.CursorPosition);

            // What behaviour should up/down arrows have if the cell is in edit mode?
        }
Exemplo n.º 19
0
        public void EnsureDoubleClickInitiatesEditing()
        {
            // Click on clock node.
            explorerPresenter.SelectNode(".Simulations.Simulation.Clock");
            GtkUtilities.WaitForGtkEvents();

            GridView grid = explorerPresenter.CurrentRightHandView as GridView;

            if (grid == null)
            {
                throw new Exception("Clock view is not a GridView");
            }

            // Grid should not be in edit mode at this point.
            Assert.IsFalse(grid.IsUserEditingCell);

            // Double-click on the top-right cell using the coordinates.
            GtkUtilities.ClickOnGridCell(grid, 0, 1, Gdk.EventType.TwoButtonPress, Gdk.ModifierType.None, GtkUtilities.ButtonPressType.LeftClick, wait: true);

            // Grid should now be in edit mode.
            Assert.IsTrue(grid.IsUserEditingCell);
        }
Exemplo n.º 20
0
        public void EnsureDoubleClickInitiatesEditing()
        {
            // Click on clock node.
            explorerPresenter.SelectNode(".Simulations.Simulation.Field.Soil.Physical");
            GtkUtilities.WaitForGtkEvents();

            ProfileView profile = explorerPresenter.CurrentRightHandView as ProfileView;

            if (profile == null)
            {
                throw new Exception($"Soil.Physical view is not a ProfileView - it is a {explorerPresenter.CurrentRightHandView.GetType().Name}");
            }
            GridView grid = profile.ProfileGrid as GridView;

            // Grid should not be in edit mode at this point.
            Assert.IsFalse(grid.IsUserEditingCell);

            // Double-click on the top-right cell using the coordinates.
            GtkUtilities.ClickOnGridCell(grid, 0, 1, Gdk.EventType.TwoButtonPress, Gdk.ModifierType.None, GtkUtilities.ButtonPressType.LeftClick, wait: true);

            // Grid should now be in edit mode.
            Assert.IsTrue(grid.IsUserEditingCell);
        }
Exemplo n.º 21
0
        public void AddResourceModelToReplacements()
        {
            ExplorerPresenter explorerPresenter = UITestUtilities.OpenBasicFileInGui();

            GtkUtilities.WaitForGtkEvents();

            // Add a replacements node.
            Replacements replacements = new Replacements();

            Structure.Add(replacements, explorerPresenter.ApsimXFile);
            explorerPresenter.Refresh();

            // Select the replacements node, then activate the 'add model' context menu item.
            explorerPresenter.SelectNode(replacements);
            explorerPresenter.ContextMenu.AddModel(explorerPresenter, EventArgs.Empty);
            GtkUtilities.WaitForGtkEvents();

            TreeView addModelsTree = (TreeView)ReflectionUtilities.GetValueOfFieldOrProperty("tree", explorerPresenter.CurrentPresenter);

            // Now, we double click on the fertiliser node. This should add a fertiliser model.
            // For some reason, sending a double click event doesn't trigger the ActivateRow signal.
            // Therefore, we need to manually activate the row.
            //GtkUtilities.ClickOnTreeView(treeView, path, 0, EventType.TwoButtonPress, ModifierType.None, GtkUtilities.ButtonPressType.LeftClick);
            ActivateNode(addModelsTree, ".Models.PMF.Wheat");
            Assert.AreEqual(1, replacements.Children.Count, "Replacements should now have 1 child after adding wheat, but it doesn't");
            Assert.AreEqual(typeof(Models.PMF.Plant), replacements.Children[0].GetType());

            // Wheat should have some children (read in from the resource file).
            IModel wheat = replacements.Children[0];

            Assert.NotZero(wheat.Children.Count);
            // The children should all be visible.
            foreach (IModel child in wheat.Children)
            {
                Assert.False(child.IsHidden);
            }
        }
Exemplo n.º 22
0
        public void CreateGraphs()
        {
            Simulations sims = CreateTemplate();

            sims.FileName = Path.ChangeExtension(Path.GetTempFileName(), ".apsimx");

            DataStore storage = sims.FindInScope <DataStore>();

            storage.FileName = Path.ChangeExtension(sims.FileName, ".db");

            // Run the file to populate the datastore.
            Runner           runner = new Runner(sims);
            List <Exception> errors = runner.Run();

            if (errors != null && errors.Count > 0)
            {
                throw errors[0];
            }

            // Open the .apsimx file in the GUI.
            sims.Write(sims.FileName);
            ExplorerPresenter explorer = UITestsMain.MasterPresenter.OpenApsimXFileInTab(sims.FileName, true);

            GtkUtilities.WaitForGtkEvents();
            sims = explorer.ApsimXFile;

            // Create a graphs folder under the zone.
            IModel paddock = sims.FindInScope <Zone>();
            Folder graphs  = new Folder();

            graphs.Name = "Graphs";

            var command = new AddModelCommand(paddock, graphs);

            explorer.CommandHistory.Add(command, true);

            // Add an empty graph to the folder.
            Models.Graph graph = new Models.Graph();
            graph.Name = "Graph";
            command    = new AddModelCommand(graphs, graph);
            explorer.CommandHistory.Add(command, true);

            // Add an empty series to the graph.
            Models.Series series = new Models.Series();
            series.Name = "Series";
            command     = new AddModelCommand(graph, series);
            explorer.CommandHistory.Add(command, true);
            explorer.Refresh();

            // click on the series node.
            explorer.SelectNode(series.FullPath);
            GtkUtilities.WaitForGtkEvents();

            // Get a reference to the OxyPlot PlotView via reflection.
            SeriesView seriesView = explorer.CurrentRightHandView as SeriesView;
            GraphView  view       = seriesView?.GraphView as GraphView;

            Assert.NotNull(view);

            PlotView plot = ReflectionUtilities.GetValueOfFieldOrProperty("plot1", view) as PlotView;

            Assert.NotNull(plot);

            // Series has no table name or x/y series names yet, so there should
            // be nothing shown on the graph.
            Assert.AreEqual(0, plot.Model.Series.Count);

            // Now draw some data on the graph.
            seriesView.DataSource.SelectedValue = "Report";
            seriesView.X.SelectedValue          = "n";
            seriesView.Y.SelectedValue          = "n2";
            seriesView.SeriesType.SelectedValue = "Scatter";

            GtkUtilities.WaitForGtkEvents();

            // There should now be one series showing.
            Assert.AreEqual(1, plot.Model.Series.Count);

            // It should be a line series.
            Assert.True(plot.Model.Series[0] is LineSeries, "Graph series type is set to scatter, but the series object is not a LineSeries.");

            // Series colour should not be white, and should not be the same as the background colour.
            LineSeries line = plot.Model.Series[0] as LineSeries;

            OxyPlot.OxyColor empty = OxyPlot.OxyColor.FromArgb(0, 0, 0, 0);
            OxyPlot.OxyColor white = OxyPlot.OxyColor.FromArgb(0, 255, 255, 255);
            Assert.AreNotEqual(empty, line.Color, "Graph line series default colour is white on white.");
            Assert.AreNotEqual(white, line.Color, "Graph line series default colour is white on white.");

            // Legend should be visible but empty by default.
            Assert.True(plot.Model.IsLegendVisible);
            // todo - ensure legend is empty

            // Next, we want to change the legend position and ensure that the legend actually moves.

            // Click on the 'show in legend' checkbox.
            seriesView.ShowInLegend.Checked = true;
            GtkUtilities.WaitForGtkEvents();

            // Double click on the middle of the legend.
            Cairo.Rectangle legendRect = plot.Model.LegendArea.ToRect(true);
            double          x          = (legendRect.X + (legendRect.X + legendRect.Width)) / 2;
            double          y          = (legendRect.Y + (legendRect.Y + legendRect.Height)) / 2;

            GtkUtilities.DoubleClick(plot, x, y, wait: true);

            // Default legend position should be top-left.
            Assert.AreEqual(plot.Model.LegendPosition, OxyPlot.LegendPosition.TopLeft);

            // Now we want to change the legend position. First, get a reference to the legend view
            // via the legend presenter, via the graph presenter, via the series presenter, via the explorer presenter.
            Assert.True(explorer.CurrentPresenter is SeriesPresenter);
            SeriesPresenter seriesPresenter = explorer.CurrentPresenter as SeriesPresenter;
            LegendPresenter legendPresenter = seriesPresenter.GraphPresenter.CurrentPresenter as LegendPresenter;

            // todo: should we add something like a GetView() method to the IPresenter interface?
            // It might be a bit of work to set up but would save us having to use reflection
            LegendView legendView = ReflectionUtilities.GetValueOfFieldOrProperty("view", legendPresenter) as LegendView;

            // The legend options are inside a Gtk expander.
            Assert.IsTrue(legendView.MainWidget.Parent is Expander);
            Expander expander = legendView.MainWidget.Parent as Expander;

            // The expander should be expanded and the options visible.
            Assert.IsTrue(expander.Expanded);
            Assert.IsTrue(legendView.MainWidget.Visible);

            // The legend view contains a combo box with the legend position options (top-right, bottom-left, etc).
            // This should really be refactored to use a public IDropDownView, which is much more convenient to use.
            // First, get a reference to the combo box via reflection.
            ComboBox combo = ReflectionUtilities.GetValueOfFieldOrProperty("combobox1", legendView) as ComboBox;

            // fixme - we should support all valid OxyPlot legend position types.
            foreach (Models.Graph.LegendPositionType legendPosition in Enum.GetValues(typeof(Models.Graph.LegendPositionType)))
            {
                string name = legendPosition.ToString();
                GtkUtilities.SelectComboBoxItem(combo, name, wait: true);

                OxyPlot.LegendPosition oxyPlotEquivalent = (OxyPlot.LegendPosition)Enum.Parse(typeof(OxyPlot.LegendPosition), name);
                Assert.AreEqual(plot.Model.LegendPosition, oxyPlotEquivalent);
            }

            // If we change the graph to a box plot then the several unused properties should be disabled.
            // These are x variable dropdown, x cumulative, x on top, marker size/type checkboxes.

            // First, make sure that these options are sensitive to input and can be changed.
            Assert.IsTrue(seriesView.X.IsSensitive);
            Assert.IsTrue(seriesView.XCumulative.IsSensitive);
            Assert.IsTrue(seriesView.XOnTop.IsSensitive);
            Assert.IsTrue(seriesView.MarkerSize.IsSensitive);
            Assert.IsTrue(seriesView.MarkerType.IsSensitive);

            // Now change series type to box plot.
            GtkUtilities.SelectComboBoxItem(seriesView.SeriesType, "Box", wait: true);
            Assert.AreEqual(SeriesType.Box, series.Type);

            // Ensure the box plot is not white in light theme.
            plot = ReflectionUtilities.GetValueOfFieldOrProperty("plot1", view) as PlotView;
            Assert.NotNull(plot);
            BoxPlotSeries boxPlot = plot.Model.Series.OfType <BoxPlotSeries>().FirstOrDefault();

            Assert.NotNull(boxPlot);

            Assert.AreNotEqual(empty, boxPlot.Fill);
            Assert.AreNotEqual(white, boxPlot.Fill);
            Assert.AreNotEqual(empty, boxPlot.Stroke);
            Assert.AreNotEqual(white, boxPlot.Stroke);

            // The controls should no longer be sensitive.
            Assert.IsFalse(seriesView.XCumulative.IsSensitive);
            Assert.IsFalse(seriesView.XOnTop.IsSensitive);
            Assert.IsFalse(seriesView.MarkerSize.IsSensitive);
            Assert.IsFalse(seriesView.MarkerType.IsSensitive);

            // Change the series type back to scatter.
            GtkUtilities.SelectComboBoxItem(seriesView.SeriesType, "Scatter", wait: true);

            // The controls should be sensitive once more.
            Assert.IsTrue(seriesView.X.IsSensitive);
            Assert.IsTrue(seriesView.XCumulative.IsSensitive);
            Assert.IsTrue(seriesView.XOnTop.IsSensitive);
            Assert.IsTrue(seriesView.MarkerSize.IsSensitive);
            Assert.IsTrue(seriesView.MarkerType.IsSensitive);
        }