#region Disclaimer / License // Copyright (C) 2010, Jackie Ng // http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com // // 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. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Text; using System.Windows.Forms; using System.Xml; using System.Xml.Schema; using ICSharpCode.TextEditor.Document; using OSGeo.MapGuide.MaestroAPI; using OSGeo.MapGuide.MaestroAPI.Exceptions; using OSGeo.MapGuide.MaestroAPI.Resource; using OSGeo.MapGuide.ObjectModels; using ICSharpCode.TextEditor.Actions; using Maestro.Editors.Generic.XmlEditor; namespace Maestro.Editors.Generic { /// /// /// public delegate void XmlValidationCallback(out string[] errors, out string[] warnings); //TODO: Incorporate all the bells and whistles that ICSharpCode.TextEditor has to offer. //Right now this is an obvious shim-job /// /// A generic XML content editor /// public partial class XmlEditorCtrl : EditorBase, INotifyResourceChanged { class FindAction : AbstractEditAction { private XmlEditorCtrl _parent; public FindAction(XmlEditorCtrl parent) { _parent = parent; } public override void Execute(ICSharpCode.TextEditor.TextArea textArea) { _parent.DoFind(); } } class FindAndReplaceAction : AbstractEditAction { private XmlEditorCtrl _parent; public FindAndReplaceAction(XmlEditorCtrl parent) { _parent = parent; } public override void Execute(ICSharpCode.TextEditor.TextArea textArea) { _parent.DoFindReplace(); } } private bool _ready = false; /// /// Initializes a new instance of the class. /// public XmlEditorCtrl() { InitializeComponent(); txtXmlContent.RegisterAction(Keys.Control | Keys.F, new FindAction(this)); txtXmlContent.RegisterAction(Keys.Control | Keys.H, new FindAndReplaceAction(this)); var props = TextEditorProperties.CreateDefault(txtXmlContent.Font); props.LineViewerStyle = LineViewerStyle.FullRow; props.ShowInvalidLines = true; txtXmlContent.ApplySettings(props); txtXmlContent.TextChanged += new EventHandler(OnTextContentChanged); } private string _origText; private void OnTextContentChanged(object sender, EventArgs e) { if (!string.IsNullOrEmpty(_origText) && !txtXmlContent.Text.Equals(_origText)) { OnResourceChanged(); EvaluateCommands(); } if (string.IsNullOrEmpty(_origText)) _origText = txtXmlContent.Text; } /// /// Gets or sets the validator. /// /// The validator. public XmlValidationCallback Validator { get; set; } /// /// Gets or sets the color of the text. /// /// The color of the text. public Color TextColor { get { return txtXmlContent.ForeColor; } set { txtXmlContent.ForeColor = value; } } /// /// Gets or sets the color of the background. /// /// The color of the background. public Color BackgroundColor { get { return txtXmlContent.BackColor; } set { txtXmlContent.BackColor = value; } } /// /// Gets or sets the text font. /// /// The text font. public Font TextFont { get { return txtXmlContent.Font; } set { txtXmlContent.Font = value; } } /// /// Readies for editing. /// public void ReadyForEditing() { _ready = true; } /// /// Raises the event. /// /// An that contains the event data. protected override void OnLoad(EventArgs e) { EvaluateCommands(); } private void EvaluateCommands() { btnUndo.Enabled = txtXmlContent.EnableUndo; cutToolStripMenuItem.Enabled = btnCut.Enabled = txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableCut; copyToolStripMenuItem.Enabled = btnCopy.Enabled = txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableCopy; pasteToolStripMenuItem.Enabled = btnPaste.Enabled = Clipboard.ContainsText(); btnValidate.Enabled = (this.Validator != null); } /// /// Raised when XML content changes /// public event EventHandler TextChanged { add { txtXmlContent.TextChanged += value; } remove { txtXmlContent.TextChanged -= value; } } /// /// Gets or sets the content of the XML. /// /// The content of the XML. public string XmlContent { get { return txtXmlContent.Text; } set { _origText = null; txtXmlContent.Text = value; FormatText(); } } private void btnUndo_Click(object sender, EventArgs e) { txtXmlContent.Undo(); } private IDocument GetDocument() { return txtXmlContent.ActiveTextAreaControl.Document; } private void UpdateTextPosition() { var textEditor = txtXmlContent; int line = textEditor.ActiveTextAreaControl.Caret.Line; int col = textEditor.ActiveTextAreaControl.Caret.Column; lblCursorPos.Text = String.Format(Strings.XmlEditorCursorTemplate, line + 1, col + 1); } private void txtXmlContent_TextChanged(object sender, EventArgs e) { UpdateTextPosition(); EvaluateCommands(); txtXmlContent.UpdateFolding(); if (_ready) OnResourceChanged(); } private void btnValidate_Click(object sender, EventArgs e) { PerformValidation(false, false); } /// /// Performs validation of the XML content /// /// If true will not show a success dialog on successful validation /// if set to true displays only errors in validation, otherwise it shows both errors and warnings. /// /// true if validation was successful, false otherwise /// public bool PerformValidation(bool silentSuccess, bool errorsOnly) { if (this.Validator != null) { string[] errors = new string[0]; string[] warnings = new string[0]; try { this.Validator(out errors, out warnings); } catch (XmlException ex) { var err = new List(errors); err.Add(ex.Message); errors = err.ToArray(); } if (errors.Length > 0 || warnings.Length > 0) { if (errorsOnly) { if (errors.Length > 0) { new XmlValidationResult(errors, new string[0]).Show(); return false; } return true; } else { new XmlValidationResult(errors, warnings).Show(); } return false; } else { if (!silentSuccess) MessageBox.Show(Strings.XmlDocIsValid); return true; } } return true; } private void btnFormat_Click(object sender, EventArgs e) { FormatText(); } private void FormatText() { if (string.IsNullOrEmpty(txtXmlContent.Text)) return; System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.LoadXml(txtXmlContent.Text); System.Text.StringBuilder sb = new System.Text.StringBuilder(); System.Xml.XmlWriter xw = System.Xml.XmlTextWriter.Create(sb, new System.Xml.XmlWriterSettings() { Indent = true }); doc.WriteTo(xw); xw.Flush(); txtXmlContent.Text = sb.ToString(); } /// /// Binds the specified service. /// /// The service. public override void Bind(IEditorService service) { var res = service.GetEditedResource(); this.XmlContent = ResourceTypeRegistry.SerializeAsString(res); InitResourceData(service); } /// /// Inits the resource data. /// /// The service. public void InitResourceData(IEditorService service) { resDataCtrl.Init(service); } private void resDataCtrl_DataListChanged(object sender, EventArgs e) { if (_ready) OnResourceChanged(); } private void btnCut_Click(object sender, EventArgs e) { txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.Cut(this, EventArgs.Empty); } private void btnCopy_Click(object sender, EventArgs e) { txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.Copy(this, EventArgs.Empty); } private void btnPaste_Click(object sender, EventArgs e) { txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.Paste(this, EventArgs.Empty); } private XmlEditor.FindAndReplaceForm _findForm = new XmlEditor.FindAndReplaceForm(); private void btnFind_Click(object sender, EventArgs e) { DoFind(); } private void DoFind() { var editor = txtXmlContent; if (editor == null) return; _findForm.ShowFor(editor, false); } private void btnFindAndReplace_Click(object sender, EventArgs e) { DoFindReplace(); } private void DoFindReplace() { var editor = txtXmlContent; if (editor == null) return; _findForm.ShowFor(editor, true); } /// /// Find and replace all instances of the specified token with its replacement token /// /// /// public void FindAndReplace(string find, string replace) { _findForm.ShowFor(txtXmlContent, true, false); //This is just to initialize it just in case _findForm.FindAndReplace(find, replace); } private void cutToolStripMenuItem_Click(object sender, EventArgs e) { txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.Cut(this, EventArgs.Empty); } private void copyToolStripMenuItem_Click(object sender, EventArgs e) { txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.Copy(this, EventArgs.Empty); } private void pasteToolStripMenuItem_Click(object sender, EventArgs e) { txtXmlContent.ActiveTextAreaControl.TextArea.ClipboardHandler.Paste(this, EventArgs.Empty); } private void findToolStripMenuItem_Click(object sender, EventArgs e) { DoFind(); } private void findReplaceToolStripMenuItem_Click(object sender, EventArgs e) { DoFindReplace(); } } /// /// Holds information about the start of a fold in an xml string. /// internal class XmlFoldStart { int line = 0; int col = 0; string prefix = String.Empty; string name = String.Empty; string foldText = String.Empty; public XmlFoldStart(string prefix, string name, int line, int col) { this.line = line; this.col = col; this.prefix = prefix; this.name = name; } /// /// The line where the fold should start. Lines start from 0. /// public int Line { get { return line; } } /// /// The column where the fold should start. Columns start from 0. /// public int Column { get { return col; } } /// /// The name of the xml item with its prefix if it has one. /// public string Name { get { if (prefix.Length > 0) { return String.Concat(prefix, ":", name); //NOXLATE } else { return name; } } } /// /// The text to be displayed when the item is folded. /// public string FoldText { get { return foldText; } set { foldText = value; } } } }