| /******************************************************************************* | 
 |  * You may amend and distribute as you like, but don't remove this header! | 
 |  * | 
 |  * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. | 
 |  * See http://www.codeplex.com/EPPlus for details. | 
 |  * | 
 |  * Copyright (C) 2011  Jan K�llman | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |  * This library 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 Lesser General Public License for more details. | 
 |  * | 
 |  * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php | 
 |  * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html | 
 |  * | 
 |  * All code and executables are provided "as is" with no warranty either express or implied. | 
 |  * The author accepts no liability for any damage or loss of business that this product may cause. | 
 |  * | 
 |  * Code change notes: | 
 |  * | 
 |  * Author							Change						Date | 
 |  * ****************************************************************************** | 
 |  * Jan K�llman		    Initial Release		       2009-10-01 | 
 |  * Jan K�llman		    License changed GPL-->LGPL 2011-12-27 | 
 |  *******************************************************************************/ | 
 |  | 
 | using System; | 
 | using System.Collections.Immutable; | 
 | using System.Xml; | 
 |  | 
 | namespace AppsheetEpplus; | 
 |  | 
 | /// <summary> | 
 | /// Represents the different view states of the worksheet | 
 | /// </summary> | 
 | public class ExcelWorksheetView : XmlHelper { | 
 |   /// <summary> | 
 |   /// The worksheet panes after a freeze or split. | 
 |   /// </summary> | 
 |   public class ExcelWorksheetPanes : XmlHelper { | 
 |     private XmlElement _selectionNode; | 
 |  | 
 |     internal ExcelWorksheetPanes(XmlNamespaceManager ns, XmlNode topNode) | 
 |         : base(ns, topNode) { | 
 |       if (topNode.Name == "selection") { | 
 |         _selectionNode = topNode as XmlElement; | 
 |       } | 
 |     } | 
 |  | 
 |     private const string _activeCellPath = "@activeCell"; | 
 |  | 
 |     /// <summary> | 
 |     /// Set the active cell. Must be set within the SelectedRange. | 
 |     /// </summary> | 
 |     public string ActiveCell { | 
 |       get { | 
 |         string address = GetXmlNodeString(_activeCellPath); | 
 |         if (address == "") { | 
 |           return "A1"; | 
 |         } | 
 |         return address; | 
 |       } | 
 |       set { | 
 |         int toCol, | 
 |             toRow; | 
 |         if (_selectionNode == null) { | 
 |           CreateSelectionElement(); | 
 |         } | 
 |         ExcelCellBase.GetRowColFromAddress( | 
 |             value, | 
 |             out var fromRow, | 
 |             out var fromCol, | 
 |             out toRow, | 
 |             out toCol); | 
 |         SetXmlNodeString(_activeCellPath, value); | 
 |         if (((XmlElement)TopNode).GetAttribute("sqref") == "") { | 
 |           SelectedRange = ExcelCellBase.GetAddress(fromRow, fromCol); | 
 |         } | 
 |         //TODO:Add fix for out of range here | 
 |       } | 
 |     } | 
 |  | 
 |     private void CreateSelectionElement() { | 
 |       _selectionNode = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain); | 
 |       TopNode.AppendChild(_selectionNode); | 
 |       TopNode = _selectionNode; | 
 |     } | 
 |  | 
 |     private const string _selectionRangePath = "@sqref"; | 
 |  | 
 |     /// <summary> | 
 |     /// Selected Cells.Used in combination with ActiveCell | 
 |     /// </summary> | 
 |     public string SelectedRange { | 
 |       get { | 
 |         string address = GetXmlNodeString(_selectionRangePath); | 
 |         if (address == "") { | 
 |           return "A1"; | 
 |         } | 
 |         return address; | 
 |       } | 
 |       set { | 
 |         int toCol, | 
 |             toRow; | 
 |         if (_selectionNode == null) { | 
 |           CreateSelectionElement(); | 
 |         } | 
 |         ExcelCellBase.GetRowColFromAddress( | 
 |             value, | 
 |             out var fromRow, | 
 |             out var fromCol, | 
 |             out toRow, | 
 |             out toCol); | 
 |         SetXmlNodeString(_selectionRangePath, value); | 
 |         if (((XmlElement)TopNode).GetAttribute("activeCell") == "") { | 
 |           ActiveCell = ExcelCellBase.GetAddress(fromRow, fromCol); | 
 |         } | 
 |         //TODO:Add fix for out of range here | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private readonly ExcelWorksheet _worksheet; | 
 |  | 
 |   protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ | 
 |     "sheetViews", | 
 |     "sheetView", | 
 |     "pane", | 
 |     "selection", | 
 |   ]; | 
 |  | 
 |   /// <summary> | 
 |   /// Creates a new ExcelWorksheetView which provides access to all the view states of the worksheet. | 
 |   /// </summary> | 
 |   /// <param name="ns"></param> | 
 |   /// <param name="node"></param> | 
 |   /// <param name="xlWorksheet"></param> | 
 |   internal ExcelWorksheetView(XmlNamespaceManager ns, XmlNode node, ExcelWorksheet xlWorksheet) | 
 |       : base(ns, node) { | 
 |     _worksheet = xlWorksheet; | 
 |     Panes = LoadPanes(); | 
 |   } | 
 |  | 
 |   private ExcelWorksheetPanes[] LoadPanes() { | 
 |     XmlNodeList nodes = TopNode.SelectNodes("//d:selection", NameSpaceManager); | 
 |     if (nodes.Count == 0) { | 
 |       return [new ExcelWorksheetPanes(NameSpaceManager, TopNode)]; | 
 |     } | 
 |     ExcelWorksheetPanes[] panes = new ExcelWorksheetPanes[nodes.Count]; | 
 |     int i = 0; | 
 |     foreach (XmlElement elem in nodes) { | 
 |       panes[i++] = new(NameSpaceManager, elem); | 
 |     } | 
 |     return panes; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Returns a reference to the sheetView element | 
 |   /// </summary> | 
 |   protected internal XmlElement SheetViewElement => (XmlElement)TopNode; | 
 |  | 
 |   /// <summary> | 
 |   /// The active cell. | 
 |   /// </summary> | 
 |   public string ActiveCell { | 
 |     get => Panes[Panes.GetUpperBound(0)].ActiveCell; | 
 |     set => Panes[Panes.GetUpperBound(0)].ActiveCell = value; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Selected Cells in the worksheet.Used in combination with ActiveCell | 
 |   /// </summary> | 
 |   public string SelectedRange { | 
 |     get => Panes[Panes.GetUpperBound(0)].SelectedRange; | 
 |     set => Panes[Panes.GetUpperBound(0)].SelectedRange = value; | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Indicates if the worksheet is selected within the workbook | 
 |   /// </summary> | 
 |   public bool TabSelected { | 
 |     get => GetXmlNodeBool("@tabSelected"); | 
 |     set { | 
 |       if (value) { | 
 |         //    // ensure no other worksheet has its tabSelected attribute set to 1 | 
 |         foreach (ExcelWorksheet sheet in _worksheet.Workbook.Worksheets) { | 
 |           sheet.View.TabSelected = false; | 
 |         } | 
 |  | 
 |         SheetViewElement.SetAttribute("tabSelected", "1"); | 
 |         XmlElement bookView = | 
 |             _worksheet.Workbook.WorkbookXml.SelectSingleNode( | 
 |                 "//d:workbookView", | 
 |                 _worksheet.NameSpaceManager) as XmlElement; | 
 |         if (bookView != null) { | 
 |           bookView.SetAttribute("activeTab", (_worksheet.PositionID - 1).ToString()); | 
 |         } | 
 |       } else { | 
 |         SetXmlNodeString("@tabSelected", "0"); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Sets the view mode of the worksheet to pagelayout | 
 |   /// </summary> | 
 |   public bool PageLayoutView { | 
 |     get => GetXmlNodeString("@view") == "pageLayout"; | 
 |     set { | 
 |       if (value) { | 
 |         SetXmlNodeString("@view", "pageLayout"); | 
 |       } else { | 
 |         SheetViewElement.RemoveAttribute("view"); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Sets the view mode of the worksheet to pagebreak | 
 |   /// </summary> | 
 |   public bool PageBreakView { | 
 |     get => GetXmlNodeString("@view") == "pageBreakPreview"; | 
 |     set { | 
 |       if (value) { | 
 |         SetXmlNodeString("@view", "pageBreakPreview"); | 
 |       } else { | 
 |         SheetViewElement.RemoveAttribute("view"); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Show gridlines in the worksheet | 
 |   /// </summary> | 
 |   public bool ShowGridLines { | 
 |     get => GetXmlNodeBool("@showGridLines"); | 
 |     set => SetXmlNodeString("@showGridLines", value ? "1" : "0"); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Show the Column/Row headers (containg column letters and row numbers) | 
 |   /// </summary> | 
 |   public bool ShowHeaders { | 
 |     get => GetXmlNodeBool("@showRowColHeaders"); | 
 |     set => SetXmlNodeString("@showRowColHeaders", value ? "1" : "0"); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Window zoom magnification for current view representing percent values. | 
 |   /// </summary> | 
 |   public int ZoomScale { | 
 |     get => GetXmlNodeInt("@zoomScale"); | 
 |     set { | 
 |       if (value < 10 || value > 400) { | 
 |         throw new ArgumentOutOfRangeException("Zoome scale out of range (10-400)"); | 
 |       } | 
 |       SetXmlNodeString("@zoomScale", value.ToString()); | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Flag indicating whether the sheet is in 'right to left' display mode. When in this mode,Column A is on the far right, Column B ;is one column left of Column A, and so on. Also,information in cells is displayed in the Right to Left format. | 
 |   /// </summary> | 
 |   public bool RightToLeft { | 
 |     get => GetXmlNodeBool("@rightToLeft"); | 
 |     set => SetXmlNodeString("@rightToLeft", value ? "1" : "0"); | 
 |   } | 
 |  | 
 |   internal bool WindowProtection { | 
 |     get => GetXmlNodeBool("@windowProtection", false); | 
 |     set => SetXmlNodeBool("@windowProtection", value, false); | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Reference to the panes | 
 |   /// </summary> | 
 |   public ExcelWorksheetPanes[] Panes { get; internal set; } | 
 |  | 
 |   private readonly string _paneNodePath = "d:pane"; | 
 |   private readonly string _selectionNodePath = "d:selection"; | 
 |  | 
 |   /// <summary> | 
 |   /// Freeze the columns/rows to left and above the cell | 
 |   /// </summary> | 
 |   /// <param name="row"></param> | 
 |   /// <param name="column"></param> | 
 |   public void FreezePanes(int row, int column) { | 
 |     //TODO:fix this method to handle splits as well. | 
 |     if (row == 1 && column == 1) { | 
 |       UnFreezePanes(); | 
 |     } | 
 |     string sqRef = SelectedRange, | 
 |         activeCell = ActiveCell; | 
 |  | 
 |     XmlElement paneNode = TopNode.SelectSingleNode(_paneNodePath, NameSpaceManager) as XmlElement; | 
 |     if (paneNode == null) { | 
 |       CreateNode(_paneNodePath); | 
 |       paneNode = TopNode.SelectSingleNode(_paneNodePath, NameSpaceManager) as XmlElement; | 
 |     } | 
 |     paneNode.RemoveAll(); //Clear all attributes | 
 |     if (column > 1) { | 
 |       paneNode.SetAttribute("xSplit", (column - 1).ToString()); | 
 |     } | 
 |     if (row > 1) { | 
 |       paneNode.SetAttribute("ySplit", (row - 1).ToString()); | 
 |     } | 
 |     paneNode.SetAttribute("topLeftCell", ExcelCellBase.GetAddress(row, column)); | 
 |     paneNode.SetAttribute("state", "frozen"); | 
 |  | 
 |     RemoveSelection(); | 
 |  | 
 |     if (row > 1 && column == 1) { | 
 |       paneNode.SetAttribute("activePane", "bottomLeft"); | 
 |       XmlElement sel = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain); | 
 |       sel.SetAttribute("pane", "bottomLeft"); | 
 |       if (activeCell != "") { | 
 |         sel.SetAttribute("activeCell", activeCell); | 
 |       } | 
 |       if (sqRef != "") { | 
 |         sel.SetAttribute("sqref", sqRef); | 
 |       } | 
 |       sel.SetAttribute("sqref", sqRef); | 
 |       TopNode.InsertAfter(sel, paneNode); | 
 |     } else if (column > 1 && row == 1) { | 
 |       paneNode.SetAttribute("activePane", "topRight"); | 
 |       XmlElement sel = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain); | 
 |       sel.SetAttribute("pane", "topRight"); | 
 |       if (activeCell != "") { | 
 |         sel.SetAttribute("activeCell", activeCell); | 
 |       } | 
 |       if (sqRef != "") { | 
 |         sel.SetAttribute("sqref", sqRef); | 
 |       } | 
 |       TopNode.InsertAfter(sel, paneNode); | 
 |     } else { | 
 |       paneNode.SetAttribute("activePane", "bottomRight"); | 
 |       XmlElement sel1 = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain); | 
 |       sel1.SetAttribute("pane", "topRight"); | 
 |       string cell = ExcelCellBase.GetAddress(1, column); | 
 |       sel1.SetAttribute("activeCell", cell); | 
 |       sel1.SetAttribute("sqref", cell); | 
 |       paneNode.ParentNode.InsertAfter(sel1, paneNode); | 
 |  | 
 |       XmlElement sel2 = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain); | 
 |       cell = ExcelCellBase.GetAddress(row, 1); | 
 |       sel2.SetAttribute("pane", "bottomLeft"); | 
 |       sel2.SetAttribute("activeCell", cell); | 
 |       sel2.SetAttribute("sqref", cell); | 
 |       sel1.ParentNode.InsertAfter(sel2, sel1); | 
 |  | 
 |       XmlElement sel3 = TopNode.OwnerDocument.CreateElement("selection", ExcelPackage._schemaMain); | 
 |       sel3.SetAttribute("pane", "bottomRight"); | 
 |       if (activeCell != "") { | 
 |         sel3.SetAttribute("activeCell", activeCell); | 
 |       } | 
 |       if (sqRef != "") { | 
 |         sel3.SetAttribute("sqref", sqRef); | 
 |       } | 
 |       sel2.ParentNode.InsertAfter(sel3, sel2); | 
 |     } | 
 |     Panes = LoadPanes(); | 
 |   } | 
 |  | 
 |   private void RemoveSelection() { | 
 |     //Find selection nodes and remove them | 
 |     XmlNodeList selections = TopNode.SelectNodes(_selectionNodePath, NameSpaceManager); | 
 |     foreach (XmlNode sel in selections) { | 
 |       sel.ParentNode.RemoveChild(sel); | 
 |     } | 
 |   } | 
 |  | 
 |   /// <summary> | 
 |   /// Unlock all rows and columns to scroll freely | 
 |   /// /// </summary> | 
 |   public void UnFreezePanes() { | 
 |     string sqRef = SelectedRange, | 
 |         activeCell = ActiveCell; | 
 |  | 
 |     XmlElement paneNode = TopNode.SelectSingleNode(_paneNodePath, NameSpaceManager) as XmlElement; | 
 |     if (paneNode != null) { | 
 |       paneNode.ParentNode.RemoveChild(paneNode); | 
 |     } | 
 |     RemoveSelection(); | 
 |  | 
 |     Panes = LoadPanes(); | 
 |  | 
 |     SelectedRange = sqRef; | 
 |     ActiveCell = activeCell; | 
 |   } | 
 | } |