2
// MonoDevelop XML Editor
4
// Copyright (C) 2006-2007 Matthew Ward
6
// Permission is hereby granted, free of charge, to any person obtaining
7
// a copy of this software and associated documentation files (the
8
// "Software"), to deal in the Software without restriction, including
9
// without limitation the rights to use, copy, modify, merge, publish,
10
// distribute, sublicense, and/or sell copies of the Software, and to
11
// permit persons to whom the Software is furnished to do so, subject to
12
// the following conditions:
14
// The above copyright notice and this permission notice shall be
15
// included in all copies or substantial portions of the Software.
17
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
using MonoDevelop.Components;
29
using MonoDevelop.Core;
30
using MonoDevelop.Core.Gui.Components;
31
using MonoDevelop.Core.Gui.Dialogs;
33
using System.Collections;
34
using System.Collections.Generic;
35
using System.Collections.ObjectModel;
36
using System.Collections.Specialized;
38
using System.Xml.XPath;
42
namespace MonoDevelop.XmlEditor
45
/// The XPathQueryWidget uses this interface to
46
/// access the currently active XmlEditorViewContent and
47
/// jump to a particular file.
49
public interface IXmlEditorViewContentProvider
51
XmlEditorViewContent View {get;}
53
void JumpTo(string fileName, int line, int column);
56
public class XPathQueryWidget : GladeWidgetExtract
59
/// The filename that the last query was executed on.
61
string fileName = String.Empty;
64
/// The total number of xpath queries to remember.
66
const int xpathQueryHistoryLimit = 20;
67
int xpathHistoryListCount;
70
/// The number of namespaces we have added to the
80
[Widget] TreeView resultsTreeView;
81
[Widget] TreeView namespacesTreeView;
82
[Widget] Button queryButton;
83
[Widget] ComboBoxEntry xpathComboBoxEntry;
84
[Widget] Notebook notebook;
86
// Results List: xpath result, line number, XPathNodeMatch or Exception.
87
const int xpathMatchColumnNumber = 0;
88
const int xpathMatchLineColumnNumber = 1;
89
const int xpathNodeMatchColumnNumber = 2;
90
ListStore resultsList = new ListStore(typeof(string), typeof(string), typeof(object));
92
// Namespace list: prefix, namespace.
93
const int prefixColumnNumber = 0;
94
const int namespaceColumnNumber = 1;
95
ListStore namespacesList = new ListStore(typeof(string), typeof(string));
97
// XPath query history: xpath query
98
ListStore xpathHistoryList = new ListStore(typeof(string));
100
IXmlEditorViewContentProvider viewProvider;
102
public XPathQueryWidget(IXmlEditorViewContentProvider viewProvider)
103
: base ("XmlEditor.glade", "XPathQueryPad")
105
this.viewProvider = viewProvider;
109
queryButton.Clicked += QueryButtonClicked;
111
EntryCompletion completion = new EntryCompletion();
112
completion.Model = xpathHistoryList;
113
completion.TextColumn = 0;
115
xpathComboBoxEntry.Model = xpathHistoryList;
116
xpathComboBoxEntry.TextColumn = 0;
117
xpathComboBoxEntry.Entry.Completion = completion;
118
xpathComboBoxEntry.Entry.Changed += XPathComboBoxEntryChanged;
119
xpathComboBoxEntry.Entry.Activated += XPathComboBoxEntryActivated;
123
/// Adds a namespace to the namespace list.
125
public void AddNamespace(string prefix, string uri)
127
TreeIter iter = namespacesList.Insert(namespaceCount);
128
namespacesList.SetValue(iter, prefixColumnNumber, prefix);
129
namespacesList.SetValue(iter, namespaceColumnNumber, uri);
134
/// Gets the list of namespaces in the namespace list.
136
public ReadOnlyCollection<XmlNamespace> GetNamespaces()
138
List<XmlNamespace> namespaces = new List<XmlNamespace>();
140
bool addNamespaces = namespacesList.GetIterFirst(out iter);
141
while (addNamespaces) {
142
string prefix = GetNamespacePrefix(iter);
143
string uri = GetNamespaceUri(iter);
144
if (prefix.Length == 0 && uri.Length == 0) {
147
namespaces.Add(new XmlNamespace(prefix, uri));
149
addNamespaces = namespacesList.IterNext(ref iter);
151
return new ReadOnlyCollection<XmlNamespace>(namespaces);
155
/// Gets the previously used XPath queries from the combo box drop down list.
157
public string [] GetXPathHistory()
159
List<string> xpaths = new List<string>();
161
bool add = xpathHistoryList.GetIterFirst(out iter);
163
string xpath = (string)xpathHistoryList.GetValue(iter, 0);
165
add = xpathHistoryList.IterNext(ref iter);
167
return xpaths.ToArray();
171
/// Adds the xpath into the combo box drop down list.
173
public void AddXPath(string xpath)
175
xpathHistoryList.AppendValues(xpath);
179
/// Gets or sets the active xpath query.
181
public string Query {
183
return xpathComboBoxEntry.Entry.Text;
186
xpathComboBoxEntry.Entry.Text = value;
190
public void UpdateQueryButtonState()
192
queryButton.Sensitive = IsXPathQueryEntered && IsXmlEditorViewContentActive;
195
void InitResultsList()
197
resultsTreeView.Model = resultsList;
198
resultsTreeView.Selection.Mode = SelectionMode.Single;
199
resultsTreeView.RowActivated += ResultsTreeViewRowActivated;
200
resultsTreeView.Selection.Changed += ResultsTreeViewSelectionChanged;
203
CellRendererText renderer = new CellRendererText();
204
resultsTreeView.AppendColumn("Match", renderer, "text", xpathMatchColumnNumber);
206
// Line number column.
207
renderer = new CellRendererText();
208
resultsTreeView.AppendColumn("Line", renderer, "text", xpathMatchLineColumnNumber);
211
void InitNamespaceList()
213
namespacesTreeView.Model = namespacesList;
214
namespacesTreeView.Selection.Mode = SelectionMode.Single;
217
CellRendererText renderer = new CellRendererText();
218
renderer.Edited += PrefixEdited;
219
renderer.Editable = true;
220
namespacesTreeView.AppendColumn("Prefix", renderer, "text", prefixColumnNumber);
223
renderer = new CellRendererText();
224
renderer.Edited += NamespaceEdited;
225
renderer.Editable = true;
226
namespacesTreeView.AppendColumn("Namespace", renderer, "text", namespaceColumnNumber);
228
// Add a few blank rows.
229
for (int i = 0; i < 20; ++i) {
230
namespacesList.Append();
234
void PrefixEdited(object source, EditedArgs e)
236
ListItemEdited(e, namespacesList, prefixColumnNumber);
239
void NamespaceEdited(object source, EditedArgs e)
241
ListItemEdited(e, namespacesList, namespaceColumnNumber);
245
/// Updates the list store item's text that has been
246
/// edited by the user.
248
void ListItemEdited(EditedArgs e, ListStore list, int column)
250
TreePath path = new TreePath(e.Path);
252
if (list.GetIter(out iter, path)) {
253
list.SetValue(iter, column, e.NewText);
257
void XPathComboBoxEntryChanged(object sender, EventArgs e)
259
UpdateQueryButtonState();
262
bool IsXPathQueryEntered {
264
return xpathComboBoxEntry.Entry.Text.Length > 0;
268
bool IsXmlEditorViewContentActive {
270
return viewProvider.View != null;
274
void QueryButtonClicked(object sender, EventArgs e)
281
XmlEditorViewContent view = viewProvider.View;
287
fileName = view.FileName;
288
// Clear previous XPath results.
291
XmlEditorView xmlEditor = view.XmlEditorView;
292
xmlEditor.RemoveXPathMarkers();
295
XPathNodeMatch[] nodes = xmlEditor.SelectNodes(Query, GetNamespaces());
296
if (nodes.Length > 0) {
297
AddXPathResults(nodes);
298
xmlEditor.AddXPathMarkers(nodes);
303
} catch (XPathException xpathEx) {
304
AddErrorResult(xpathEx);
305
} catch (XmlException xmlEx) {
306
AddErrorResult(xmlEx);
308
notebook.CurrentPage = 0;
317
void AddXPathResults(XPathNodeMatch[] nodes)
319
foreach (XPathNodeMatch node in nodes) {
320
string line = String.Empty;
321
if (node.HasLineInfo()) {
322
int lineNumber = node.LineNumber + 1;
323
line = lineNumber.ToString();
325
resultsList.AppendValues(node.DisplayValue, line, node);
329
void AddNoXPathResult()
331
resultsList.AppendValues("XPath query found 0 items.");
334
void AddErrorResult(XmlException ex)
336
resultsList.AppendValues(ex.Message, ex.LineNumber.ToString(), ex);
339
void AddErrorResult(XPathException ex)
341
resultsList.AppendValues(String.Concat("XPath: ", ex.Message), String.Empty, ex);
344
void ResultsTreeViewRowActivated(object sender, EventArgs e)
346
JumpToResultLocation();
349
/// Switches focus to the location of the XPath query result.
351
void JumpToResultLocation()
353
MoveCaretToResultLocation(MoveCaret.ByJumping);
357
/// Scrolls the text editor so the location of the XPath query results is visible.
359
void ScrollToResultLocation()
361
MoveCaretToResultLocation(MoveCaret.ByScrolling);
364
void MoveCaretToResultLocation(MoveCaret moveCaret)
367
if (resultsTreeView.Selection.GetSelected(out iter)) {
368
object tag = resultsList.GetValue(iter, xpathNodeMatchColumnNumber);
369
XPathNodeMatch xpathNodeMatch = tag as XPathNodeMatch;
370
XPathException xpathException = tag as XPathException;
371
XmlException xmlException = tag as XmlException;
372
if (xpathNodeMatch != null) {
373
MoveCaretToXPathNodeMatch(moveCaret, xpathNodeMatch);
374
} else if (xmlException != null) {
375
MoveCaretToXmlException(moveCaret, xmlException);
376
} else if (xpathException != null && moveCaret == MoveCaret.ByJumping) {
b'\t\t\t\t\txpathComboBoxEntry.Entry.HasFocus = true;'
381
void MoveCaretToXPathNodeMatch(MoveCaret moveCaret, XPathNodeMatch node)
383
if (moveCaret == MoveCaret.ByJumping) {
384
viewProvider.JumpTo(fileName, node.LineNumber, node.LinePosition);
386
ScrollTo(fileName, node.LineNumber, node.LinePosition, node.Value.Length);
390
void MoveCaretToXmlException(MoveCaret moveCaret, XmlException ex)
392
int line = ex.LineNumber - 1;
393
int column = ex.LinePosition - 1;
394
if (moveCaret == MoveCaret.ByJumping) {
395
viewProvider.JumpTo(fileName, line, column);
397
ScrollTo(fileName, line, column);
401
void ScrollTo(string fileName, int line, int column, int length)
403
XmlEditorViewContent view = viewProvider.View;
404
if (view != null && IsFileNameMatch(view)) {
405
view.SetCaretTo(line, column);
406
SourceBuffer buffer = (SourceBuffer)view.XmlEditorView.Buffer;
407
if (length > 0 && line < buffer.LineCount) {
408
TextIter lineIter = buffer.GetIterAtLine(line);
409
int startOffset = lineIter.Offset + column;
410
int endOffset = startOffset + length;
411
view.Select(startOffset, endOffset);
417
/// Tests whether the specified view matches the filename the XPath
b'\t\t/// results were found in.'
419
bool IsFileNameMatch(XmlEditorViewContent view)
421
return fileName == view.FileName;
425
/// Adds the text in the combo box to the combo box drop down list.
427
void AddXPathToHistory()
429
string newXPath = Query;
430
if (!XPathHistoryListContains(newXPath)) {
431
TreeIter iter = xpathHistoryList.Prepend();
432
xpathHistoryList.SetValue(iter, 0, newXPath);
433
xpathHistoryListCount++;
434
if (xpathHistoryListCount > xpathQueryHistoryLimit) {
435
iter = GetLastIter(xpathHistoryList);
436
xpathHistoryList.Remove(ref iter);
442
/// Brute force approach to get last list item.
444
TreeIter GetLastIter(ListStore list)
446
TreeIter iter = TreeIter.Zero;
447
TreeIter lastIter = iter;
448
bool success = list.GetIterFirst(out iter);
451
success = list.IterNext(ref iter);
457
/// Returns true if the xpath already exists.
459
bool XPathHistoryListContains(string xpath)
462
bool success = xpathHistoryList.GetIterFirst(out iter);
464
string existingXPath = (string)xpathHistoryList.GetValue(iter, 0);
465
if (xpath.Equals(existingXPath, StringComparison.InvariantCultureIgnoreCase)) {
468
success = xpathHistoryList.IterNext(ref iter);
472
void ResultsTreeViewSelectionChanged(object sender, EventArgs e)
474
ScrollToResultLocation();
478
/// Gets the namespace prefix from the specified
481
string GetNamespacePrefix(TreeIter iter)
483
string prefix = (string)namespacesList.GetValue(iter, prefixColumnNumber);
484
if (prefix != null) {
491
/// Gets the namespace uri from the specified
494
string GetNamespaceUri(TreeIter iter)
496
string uri = (string)namespacesList.GetValue(iter, namespaceColumnNumber);
503
void ScrollTo(string fileName, int line, int column)
505
ScrollTo(fileName, line, column, 0);
508
void XPathComboBoxEntryActivated(object source, EventArgs e)
b'\\ No newline at end of file'