// // // // // $Revision$ // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Threading; using System.Windows.Forms; using System.Xml; using System.Xml.XPath; using ICSharpCode.Core; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.TextEditor; using ICSharpCode.TextEditor.Document; namespace ICSharpCode.XmlEditor { public class XPathQueryControl : System.Windows.Forms.UserControl, IMementoCapable { const int ErrorImageIndex = 0; const string NamespacesProperty = "Namespaces"; const string PrefixColumnWidthProperty = "NamespacesDataGridView.PrefixColumn.Width"; const string MatchColumnWidthProperty = "XPathResultsListView.MatchColumn.Width"; const string LineColumnWidthProperty = "XPathResultsListView.LineColumn.Width"; const string XPathComboBoxTextProperty = "XPathQuery.LastQuery"; const string XPathComboBoxItemsProperty = "XPathQuery.History"; /// /// The filename that the last query was executed on. /// string fileName = String.Empty; /// /// The total number of xpath queries to remember. /// const int xpathQueryHistoryLimit = 20; bool ignoreXPathTextChanges; enum MoveCaret { ByJumping = 1, ByScrolling = 2 } public XPathQueryControl() { InitializeComponent(); InitStrings(); InitImageList(); xPathComboBox.KeyDown += XPathComboBoxKeyDown; InitAutoCompleteMode(); } /// /// Adds a namespace to the namespace list. /// public void AddNamespace(string prefix, string uri) { namespacesDataGridView.Rows.Add(new object[] {prefix, uri}); } /// /// Gets the list of namespaces in the namespace list. /// public ReadOnlyCollection GetNamespaces() { List namespaces = new List(); for (int i = 0; i < namespacesDataGridView.Rows.Count - 1; ++i) { DataGridViewRow row = namespacesDataGridView.Rows[i]; string prefix = GetPrefix(row); string uri = GetNamespace(row); if (prefix.Length == 0 && uri.Length == 0) { // Ignore. } else { namespaces.Add(new XmlNamespace(prefix, uri)); } } return new ReadOnlyCollection(namespaces); } public DataGridView NamespacesDataGridView { get { return namespacesDataGridView; } } public ListView XPathResultsListView { get { return xPathResultsListView; } } public ComboBox XPathComboBox { get { return xPathComboBox; } } /// /// Creates a properties object that contains the current state of the /// control. /// public Properties CreateMemento() { Properties properties = new Properties(); // Save namespaces. properties.Set(NamespacesProperty, GetNamespaceStringArray()); // Save namespace data grid column widths. properties.Set(PrefixColumnWidthProperty, prefixColumn.Width); // Save xpath results list view column widths. properties.Set(MatchColumnWidthProperty, matchColumnHeader.Width); properties.Set(LineColumnWidthProperty, lineColumnHeader.Width); // Save xpath query history. properties.Set(XPathComboBoxTextProperty, XPathComboBox.Text); properties.Set(XPathComboBoxItemsProperty, GetXPathHistory()); return properties; } /// /// Reloads the state of the control. /// public void SetMemento(Properties memento) { ignoreXPathTextChanges = true; try { // Set namespaces. string[] namespaces = memento.Get(NamespacesProperty, new string[0]); foreach (string ns in namespaces) { XmlNamespace xmlNamespace = XmlNamespace.FromString(ns); AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri); } // Set namespace data grid column widths. prefixColumn.Width = memento.Get(PrefixColumnWidthProperty, 50); // Set xpath results list view column widths. matchColumnHeader.Width = memento.Get(MatchColumnWidthProperty, 432); lineColumnHeader.Width = memento.Get(LineColumnWidthProperty, 60); // Set xpath query history. XPathComboBox.Text = memento.Get(XPathComboBoxTextProperty, String.Empty); string[] xpaths = memento.Get(XPathComboBoxItemsProperty, new string[0]); foreach (string xpath in xpaths) { xPathComboBox.Items.Add(xpath); } } finally { ignoreXPathTextChanges = false; } } /// /// Called when the active workbench window has changed. /// public void ActiveWindowChanged() { UpdateQueryButtonState(); } /// /// Removes all the XPath Node markers from all the open documents. /// public void RemoveXPathNodeTextMarkers() { foreach (IViewContent view in WorkbenchSingleton.Workbench.ViewContentCollection) { ITextEditorControlProvider textEditorProvider = view as ITextEditorControlProvider; if (textEditorProvider != null) { XPathNodeTextMarker.RemoveMarkers(textEditorProvider.TextEditorControl.Document.MarkerStrategy); textEditorProvider.TextEditorControl.Refresh(); } } } /// /// Disposes resources used by the control. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } #region Forms Designer generated code /// /// Designer variable used to keep track of non-visual components. /// System.ComponentModel.IContainer components = null; /// /// This method is required for Windows Forms designer support. /// Do not change the method contents inside the source code editor. The Forms designer might /// not be able to load this method if it was changed manually. /// void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.xPathLabel = new System.Windows.Forms.Label(); this.xPathComboBox = new System.Windows.Forms.ComboBox(); this.queryButton = new System.Windows.Forms.Button(); this.tabControl = new System.Windows.Forms.TabControl(); this.xPathResultsTabPage = new System.Windows.Forms.TabPage(); this.xPathResultsListView = new System.Windows.Forms.ListView(); this.matchColumnHeader = new System.Windows.Forms.ColumnHeader(); this.lineColumnHeader = new System.Windows.Forms.ColumnHeader(); this.imageList = new System.Windows.Forms.ImageList(this.components); this.namespacesTabPage = new System.Windows.Forms.TabPage(); this.namespacesDataGridView = new System.Windows.Forms.DataGridView(); this.prefixColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.namespaceColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.tabControl.SuspendLayout(); this.xPathResultsTabPage.SuspendLayout(); this.namespacesTabPage.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.namespacesDataGridView)).BeginInit(); this.SuspendLayout(); // // xPathLabel // this.xPathLabel.Location = new System.Drawing.Point(3, 3); this.xPathLabel.Name = "xPathLabel"; this.xPathLabel.Size = new System.Drawing.Size(46, 19); this.xPathLabel.TabIndex = 0; this.xPathLabel.Text = "XPath:"; this.xPathLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // xPathComboBox // this.xPathComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.xPathComboBox.FormattingEnabled = true; this.xPathComboBox.Location = new System.Drawing.Point(55, 3); this.xPathComboBox.Name = "xPathComboBox"; this.xPathComboBox.Size = new System.Drawing.Size(438, 21); this.xPathComboBox.TabIndex = 1; this.xPathComboBox.TextChanged += new System.EventHandler(this.XPathComboBoxTextChanged); this.xPathComboBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.XPathComboBoxKeyDown); // // queryButton // this.queryButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.queryButton.Enabled = false; this.queryButton.Location = new System.Drawing.Point(499, 3); this.queryButton.Name = "queryButton"; this.queryButton.Size = new System.Drawing.Size(70, 23); this.queryButton.TabIndex = 2; this.queryButton.Text = "Query"; this.queryButton.UseVisualStyleBackColor = true; this.queryButton.Click += new System.EventHandler(this.QueryButtonClick); // // tabControl // this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.tabControl.Controls.Add(this.xPathResultsTabPage); this.tabControl.Controls.Add(this.namespacesTabPage); this.tabControl.Location = new System.Drawing.Point(0, 30); this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; this.tabControl.Size = new System.Drawing.Size(572, 208); this.tabControl.TabIndex = 3; // // xPathResultsTabPage // this.xPathResultsTabPage.Controls.Add(this.xPathResultsListView); this.xPathResultsTabPage.Location = new System.Drawing.Point(4, 22); this.xPathResultsTabPage.Name = "xPathResultsTabPage"; this.xPathResultsTabPage.Padding = new System.Windows.Forms.Padding(3); this.xPathResultsTabPage.Size = new System.Drawing.Size(564, 182); this.xPathResultsTabPage.TabIndex = 0; this.xPathResultsTabPage.Text = "Results"; this.xPathResultsTabPage.UseVisualStyleBackColor = true; // // xPathResultsListView // this.xPathResultsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.matchColumnHeader, this.lineColumnHeader}); this.xPathResultsListView.Dock = System.Windows.Forms.DockStyle.Fill; this.xPathResultsListView.FullRowSelect = true; this.xPathResultsListView.HideSelection = false; this.xPathResultsListView.Location = new System.Drawing.Point(3, 3); this.xPathResultsListView.MultiSelect = false; this.xPathResultsListView.Name = "xPathResultsListView"; this.xPathResultsListView.Size = new System.Drawing.Size(558, 176); this.xPathResultsListView.SmallImageList = this.imageList; this.xPathResultsListView.TabIndex = 0; this.xPathResultsListView.UseCompatibleStateImageBehavior = false; this.xPathResultsListView.View = System.Windows.Forms.View.Details; this.xPathResultsListView.ItemActivate += new System.EventHandler(this.XPathResultsListViewItemActivate); this.xPathResultsListView.SelectedIndexChanged += new System.EventHandler(this.XPathResultsListViewSelectedIndexChanged); this.xPathResultsListView.Click += new System.EventHandler(this.XPathResultsListViewClick); // // matchColumnHeader // this.matchColumnHeader.Text = "Match"; this.matchColumnHeader.Width = 432; // // lineColumnHeader // this.lineColumnHeader.Text = "Line"; // // imageList // this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; this.imageList.ImageSize = new System.Drawing.Size(16, 16); this.imageList.TransparentColor = System.Drawing.Color.Transparent; // // namespacesTabPage // this.namespacesTabPage.Controls.Add(this.namespacesDataGridView); this.namespacesTabPage.Location = new System.Drawing.Point(4, 22); this.namespacesTabPage.Name = "namespacesTabPage"; this.namespacesTabPage.Padding = new System.Windows.Forms.Padding(3); this.namespacesTabPage.Size = new System.Drawing.Size(564, 182); this.namespacesTabPage.TabIndex = 1; this.namespacesTabPage.Text = "Namespaces"; this.namespacesTabPage.UseVisualStyleBackColor = true; // // namespacesDataGridView // this.namespacesDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.namespacesDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.prefixColumn, this.namespaceColumn}); this.namespacesDataGridView.Dock = System.Windows.Forms.DockStyle.Fill; this.namespacesDataGridView.Location = new System.Drawing.Point(3, 3); this.namespacesDataGridView.MultiSelect = false; this.namespacesDataGridView.Name = "namespacesDataGridView"; this.namespacesDataGridView.RowHeadersWidth = 25; this.namespacesDataGridView.ShowEditingIcon = false; this.namespacesDataGridView.Size = new System.Drawing.Size(558, 176); this.namespacesDataGridView.TabIndex = 0; // // prefixColumn // this.prefixColumn.HeaderText = "Prefix"; this.prefixColumn.Name = "prefixColumn"; this.prefixColumn.Width = 50; // // namespaceColumn // this.namespaceColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; this.namespaceColumn.HeaderText = "Namespace"; this.namespaceColumn.Name = "namespaceColumn"; // // XPathQueryControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tabControl); this.Controls.Add(this.queryButton); this.Controls.Add(this.xPathComboBox); this.Controls.Add(this.xPathLabel); this.Name = "XPathQueryControl"; this.Size = new System.Drawing.Size(572, 238); this.tabControl.ResumeLayout(false); this.xPathResultsTabPage.ResumeLayout(false); this.namespacesTabPage.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.namespacesDataGridView)).EndInit(); this.ResumeLayout(false); } private System.Windows.Forms.ImageList imageList; private System.Windows.Forms.DataGridViewTextBoxColumn namespaceColumn; private System.Windows.Forms.DataGridViewTextBoxColumn prefixColumn; private System.Windows.Forms.DataGridView namespacesDataGridView; private System.Windows.Forms.ColumnHeader lineColumnHeader; private System.Windows.Forms.ColumnHeader matchColumnHeader; private System.Windows.Forms.ListView xPathResultsListView; private System.Windows.Forms.TabPage namespacesTabPage; private System.Windows.Forms.TabPage xPathResultsTabPage; private System.Windows.Forms.TabControl tabControl; private System.Windows.Forms.Button queryButton; private System.Windows.Forms.ComboBox xPathComboBox; private System.Windows.Forms.Label xPathLabel; #endregion void XPathComboBoxTextChanged(object sender, EventArgs e) { if (!ignoreXPathTextChanges) { UpdateQueryButtonState(); } } void UpdateQueryButtonState() { queryButton.Enabled = IsXPathQueryEntered && XmlView.IsXmlViewActive; } bool IsXPathQueryEntered { get { return xPathComboBox.Text.Length > 0; } } void QueryButtonClick(object sender, EventArgs e) { RunXPathQuery(); } void RunXPathQuery() { XmlView view = XmlView.ActiveXmlView; if (view == null) { return; } try { MarkerStrategy markerStrategy = view.TextEditorControl.Document.MarkerStrategy; fileName = view.PrimaryFileName; // Clear previous XPath results. ClearResults(); XPathNodeTextMarker.RemoveMarkers(markerStrategy); // Run XPath query. XPathNodeMatch[] nodes = view.SelectNodes(xPathComboBox.Text, GetNamespaces()); if (nodes.Length > 0) { AddXPathResults(nodes); XPathNodeTextMarker.AddMarkers(markerStrategy, nodes); } else { AddNoXPathResult(); } AddXPathToHistory(); } catch (XPathException xpathEx) { AddErrorResult(xpathEx); } catch (XmlException xmlEx) { AddErrorResult(xmlEx); } finally { BringResultsTabToFront(); view.TextEditorControl.Refresh(); } } void ClearResults() { xPathResultsListView.Items.Clear(); } void BringResultsTabToFront() { tabControl.SelectedTab = tabControl.TabPages[0]; } void AddXPathResults(XPathNodeMatch[] nodes) { foreach (XPathNodeMatch node in nodes) { ListViewItem item = new ListViewItem(node.DisplayValue); if (node.HasLineInfo()) { int line = node.LineNumber + 1; item.SubItems.Add(line.ToString()); } item.Tag = node; xPathResultsListView.Items.Add(item); } } void AddNoXPathResult() { xPathResultsListView.Items.Add(StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.NoXPathResultsMessage}")); } void AddErrorResult(XmlException ex) { ListViewItem item = new ListViewItem(ex.Message, ErrorImageIndex); item.SubItems.Add(ex.LineNumber.ToString()); item.Tag = ex; xPathResultsListView.Items.Add(item); } void AddErrorResult(XPathException ex) { ListViewItem item = new ListViewItem(String.Concat(StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.XPathLabel}"), " ", ex.Message), ErrorImageIndex); item.Tag = ex; xPathResultsListView.Items.Add(item); } void InitImageList() { try { imageList.Images.Add(ResourceService.GetBitmap("Icons.16x16.Error")); } catch (ResourceNotFoundException) { } } void InitStrings() { lineColumnHeader.Text = StringParser.Parse("${res:CompilerResultView.LineText}"); matchColumnHeader.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.XPathMatchColumnHeaderTitle}"); prefixColumn.HeaderText = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.PrefixColumnHeaderTitle}"); namespaceColumn.HeaderText = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.NamespaceColumnHeaderTitle}"); queryButton.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.QueryButton}"); xPathLabel.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.XPathLabel}"); xPathResultsTabPage.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.ResultsTab}"); namespacesTabPage.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.NamespacesTab}"); } void InitAutoCompleteMode() { try { xPathComboBox.AutoCompleteMode = AutoCompleteMode.Suggest; xPathComboBox.AutoCompleteSource = AutoCompleteSource.ListItems; } catch (ThreadStateException) { } } void XPathResultsListViewItemActivate(object sender, EventArgs e) { JumpToResultLocation(); } /// /// Switches focus to the location of the XPath query result. /// void JumpToResultLocation() { MoveCaretToResultLocation(MoveCaret.ByJumping); } /// /// Scrolls the text editor so the location of the XPath query results is visible. /// void ScrollToResultLocation() { MoveCaretToResultLocation(MoveCaret.ByScrolling); } void MoveCaretToResultLocation(MoveCaret moveCaret) { if (xPathResultsListView.SelectedItems.Count > 0) { ListViewItem item = xPathResultsListView.SelectedItems[0]; XPathNodeMatch xPathNodeMatch = item.Tag as XPathNodeMatch; XPathException xpathException = item.Tag as XPathException; XmlException xmlException = item.Tag as XmlException; if (xPathNodeMatch != null) { MoveCaretToXPathNodeMatch(moveCaret, xPathNodeMatch); } else if (xmlException != null) { MoveCaretToXmlException(moveCaret, xmlException); } else if (xpathException != null && moveCaret == MoveCaret.ByJumping) { xPathComboBox.Focus(); } } } void MoveCaretToXPathNodeMatch(MoveCaret moveCaret, XPathNodeMatch node) { if (moveCaret == MoveCaret.ByJumping) { JumpTo(fileName, node.LineNumber, node.LinePosition); } else { ScrollTo(fileName, node.LineNumber, node.LinePosition, node.Value.Length); } } void MoveCaretToXmlException(MoveCaret moveCaret, XmlException ex) { int line = ex.LineNumber - 1; int column = ex.LinePosition - 1; if (moveCaret == MoveCaret.ByJumping) { JumpTo(fileName, line, column); } else { ScrollTo(fileName, line, column); } } void JumpTo(string fileName, int line, int column) { FileService.JumpToFilePosition(fileName, line, column); } /// /// Scrolls to the specified line and column and also selects the given /// length of text at this location. /// void ScrollTo(string fileName, int line, int column, int length) { XmlView view = XmlView.ActiveXmlView; if (view != null && IsFileNameMatch(view)) { TextAreaControl textAreaControl = view.TextEditorControl.ActiveTextAreaControl; if (length > 0 && line < textAreaControl.Document.TotalNumberOfLines) { SelectionManager selectionManager = textAreaControl.SelectionManager; selectionManager.ClearSelection(); TextLocation startPos = new TextLocation(column, line); TextLocation endPos = new TextLocation(column + length, line); selectionManager.SetSelection(startPos, endPos); } line = Math.Min(line, textAreaControl.Document.TotalNumberOfLines - 1); textAreaControl.ScrollTo(line, column); } } void ScrollTo(string fileName, int line, int column) { ScrollTo(fileName, line, column, 0); } /// /// Tests whether the specified view matches the filename the XPath /// results were found in. /// bool IsFileNameMatch(XmlView view) { return FileUtility.IsEqualFileName(fileName, view.PrimaryFileName); } /// /// Gets the namespaces and prefixes as a string array. /// string[] GetNamespaceStringArray() { List namespaces = new List(); foreach (XmlNamespace ns in GetNamespaces()) { namespaces.Add(ns.ToString()); } return namespaces.ToArray(); } /// /// Gets the previously used XPath queries from the combo box drop down list. /// /// string [] GetXPathHistory() { List xpaths = new List(); foreach (string xpath in xPathComboBox.Items) { xpaths.Add(xpath); } return xpaths.ToArray(); } /// /// Gets the namespace prefix in the specified row. /// string GetPrefix(DataGridViewRow row) { string prefix = (string)row.Cells[0].Value; if (prefix != null) { return prefix; } return String.Empty; } /// /// Gets the namespace stored in the row. /// string GetNamespace(DataGridViewRow row) { string ns = (string)row.Cells[1].Value; if (ns != null) { return ns; } return String.Empty; } /// /// Adds the text in the combo box to the combo box drop down list. /// void AddXPathToHistory() { string newXPath = xPathComboBox.Text; if (!xPathComboBox.Items.Contains(newXPath)) { xPathComboBox.Items.Insert(0, newXPath); if (xPathComboBox.Items.Count > xpathQueryHistoryLimit) { xPathComboBox.Items.RemoveAt(xpathQueryHistoryLimit); } } } void XPathComboBoxKeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Return) { RunXPathQuery(); } } void XPathResultsListViewSelectedIndexChanged(object sender, EventArgs e) { ScrollToResultLocation(); } void XPathResultsListViewClick(object sender, EventArgs e) { ScrollToResultLocation(); } } }