2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // * Neither the name of the application "Radegast", nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 using System.Collections.Generic;
34 using System.Text.RegularExpressions;
36 using System.Windows.Forms;
38 using OpenMetaverse.Assets;
43 public partial class ScriptEditor : UserControl
45 private RadegastInstance instance;
46 private GridClient client { get { return instance.Client; } }
47 private InventoryLSL script;
48 private string scriptName;
49 private string fileName;
50 private Primitive prim;
52 public ScriptEditor(RadegastInstance instance)
53 : this(instance, null, null)
55 scriptName = "New Script";
58 public ScriptEditor(RadegastInstance instance, InventoryLSL script)
59 : this(instance, script, null)
63 public ScriptEditor(RadegastInstance instance, InventoryLSL script, Primitive prim)
65 InitializeComponent();
66 Disposed += new EventHandler(SscriptEditor_Disposed);
68 this.instance = instance;
71 rtb.SyntaxHighlightEnabled = instance.GlobalSettings["script_syntax_highlight"].AsBoolean();
72 lblScripStatus.Text = string.Empty;
73 lblScripStatus.TextChanged += (object sender, EventArgs e) =>
74 instance.TabConsole.DisplayNotificationInChat(lblScripStatus.Text, ChatBufferTextStyle.Invisible);
75 Dock = DockStyle.Fill;
80 cbRunning.Visible = false;
86 scriptName = script.Name;
89 client.Assets.RequestInventoryAsset(script.AssetUUID, script.UUID, prim.ID, prim.OwnerID, script.AssetType, true, Assets_OnAssetReceived);
90 client.Inventory.RequestGetScriptRunning(prim.ID, script.UUID);
91 client.Inventory.ScriptRunningReply += OnScriptRunningReplyReceived;
95 client.Assets.RequestInventoryAsset(script, true, Assets_OnAssetReceived);
97 rtb.Text = lblScripStatus.Text = "Loading script...";
101 rtb.Text = " "; //bugs in control grrrr
102 rtb.SelectionStart = 0;
106 void SscriptEditor_Disposed(object sender, EventArgs e)
108 client.Inventory.ScriptRunningReply -= OnScriptRunningReplyReceived;
111 void OnScriptRunningReplyReceived(object sender, ScriptRunningReplyEventArgs e)
115 if (!instance.MonoRuntime || IsHandleCreated)
116 BeginInvoke(new MethodInvoker(() => OnScriptRunningReplyReceived(sender, e)));
120 cbRunning.Checked = e.IsRunning;
121 cbMono.Checked = e.IsMono;
124 void Assets_OnAssetReceived(AssetDownload transfer, Asset asset)
128 if (!instance.MonoRuntime || IsHandleCreated)
129 BeginInvoke(new MethodInvoker(() => Assets_OnAssetReceived(transfer, asset)));
133 if (!transfer.Success || asset.AssetType != AssetType.LSLText)
135 lblScripStatus.Text = rtb.Text = "Failed to download.";
139 lblScripStatus.Text = rtb.Text = "OK";
142 rtb.Text = ((AssetScriptText)asset).Source;
143 lineNubersForRtb.Invalidate();
147 private void SetTitle()
151 FindForm().Text = scriptName + " - " + Properties.Resources.ProgramName + " script editor";
155 #region Detach/Attach
156 private Control originalParent;
157 private Form detachedForm;
158 private bool detached = false;
169 if (detached == value) return; // no change
184 public void ShowDetached()
189 private void Detach()
193 if (detachedForm != null)
195 detachedForm.Dispose();
198 detachedForm = new Form();
199 originalParent = Parent;
200 Parent = detachedForm;
201 detachedForm.ClientSize = new Size(873, 580);
203 detachedForm.ActiveControl = this;
205 detachedForm.FormClosing += new FormClosingEventHandler(detachedForm_FormClosing);
207 if (originalParent != null)
209 originalParent.ControlAdded += new ControlEventHandler(originalParent_ControlAdded);
213 tbtnAttach.Visible = false;
216 tbtnAttach.Text = "Retach";
218 if (originalParent == null)
220 tSeparator1.Visible = true;
221 tbtnExit.Visible = true;
225 tSeparator1.Visible = false;
226 tbtnExit.Visible = false;
230 void originalParent_ControlAdded(object sender, ControlEventArgs e)
232 if (detachedForm != null)
234 detachedForm.FormClosing -= new FormClosingEventHandler(detachedForm_FormClosing);
236 originalParent.ControlAdded -= new ControlEventHandler(originalParent_ControlAdded);
237 tbtnAttach.Visible = false;
240 void detachedForm_FormClosing(object sender, FormClosingEventArgs e)
245 private void Retach()
249 if (originalParent != null)
251 originalParent.ControlAdded -= new ControlEventHandler(originalParent_ControlAdded);
252 Parent = originalParent;
255 if (detachedForm != null)
257 detachedForm.Dispose();
261 tbtnAttach.Text = "Detach";
263 if (originalParent == null)
269 private void tbtnAttach_Click(object sender, EventArgs e)
284 private void tbtbSaveToDisk_Click_1(object sender, EventArgs e)
286 if (string.IsNullOrEmpty(fileName))
288 tbtbSaveToDisk_Click(sender, e);
291 File.WriteAllText(fileName, rtb.Text);
294 private void tbtbSaveToDisk_Click(object sender, EventArgs e)
296 SaveFileDialog dlg = new SaveFileDialog();
297 dlg.Title = "Save script";
298 dlg.Filter = "LSL script file (*.lsl)|*.lsl|Plain text file (*.txt)|*.txt";
299 dlg.FileName = RadegastMisc.SafeFileName(scriptName);
300 DialogResult res = dlg.ShowDialog();
302 if (res == DialogResult.OK)
304 fileName = dlg.FileName;
305 scriptName = Path.GetFileName(fileName);
307 File.WriteAllText(fileName, rtb.Text);
312 private void tbtbLoadFromDisk_Click(object sender, EventArgs e)
314 OpenFileDialog dlg = new OpenFileDialog();
315 dlg.Title = "Open script";
316 dlg.Filter = "LSL script files (*.lsl)|*.lsl|Plain text files (*.txt)|*.txt|All files (*.*)|*.*";
317 dlg.Multiselect = false;
318 DialogResult res = dlg.ShowDialog();
320 if (res == DialogResult.OK)
322 fileName = dlg.FileName;
323 scriptName = Path.GetFileName(fileName);
325 rtb.Text = File.ReadAllText(fileName);
326 lineNubersForRtb.Invalidate();
331 private void tbtnExit_Click(object sender, EventArgs e)
336 private void rtb_SelectionChanged(object sender, EventArgs e)
338 RRichTextBox.CursorLocation c = rtb.CursorPosition;
339 lblLine.Text = string.Format("Ln {0}", c.Line + 1);
340 lblCol.Text = string.Format("Col {0}", c.Column + 1);
343 private bool spaceOrTab(char c)
345 return c == ' ' || c == '\t';
348 private int lastPos(string s, char c)
352 if (s == string.Empty)
355 if (s[s.Length - 1] == c)
357 return s.LastIndexOf(c);
362 //private RRichTextBox.CursorLocation prevCursor;
364 private void rtb_KeyDown(object sender, KeyEventArgs e)
366 if (e.KeyCode == Keys.S && e.Control && !e.Shift)
368 tbtbSave_Click(this, EventArgs.Empty);
369 e.Handled = e.SuppressKeyPress = true;
373 if (e.KeyCode == Keys.L && e.Control && !e.Shift)
375 ReadCursorPosition();
376 e.Handled = e.SuppressKeyPress = true;
380 if (e.Modifiers == Keys.Control && e.KeyCode == Keys.F)
382 findToolStripMenuItem_Click(null, null);
384 if (e.KeyCode == Keys.Tab)
386 rtb.SelectedText = " ";
390 else if (e.KeyCode == Keys.Enter)
393 e.SuppressKeyPress = true;
395 // RRichTextBox.CursorLocation cl = prevCursor;
396 RRichTextBox.CursorLocation cl = rtb.CursorPosition;
397 string prevLine = rtb.Lines[cl.Line];
398 string addIndent = "\n";
402 spaces < prevLine.Length && spaceOrTab(prevLine[spaces]);
403 addIndent += prevLine[spaces++]) ;
405 System.Console.WriteLine("In identer");
406 if ((pos = lastPos(prevLine.Substring(0, cl.Column), '{')) != -1)
411 rtb.SelectedText = addIndent;
413 for (eat = 0; (eat + rtb.SelectionStart) < rtb.Text.Length && rtb.Text[rtb.SelectionStart + eat] == ' '; eat++) ;
414 rtb.SelectionLength = eat;
415 rtb.SelectedText = "";
422 private void rtb_KeyPress(object sender, KeyPressEventArgs e)
424 if (rtb.Lines.Length > 0)
426 if (e.KeyChar == '}')
428 string li = rtb.Lines[rtb.GetLineFromCharIndex(rtb.SelectionStart)];
433 int toDelete = li.Length;
440 rtb.SelectionStart -= toDelete;
441 rtb.SelectionLength = toDelete;
442 rtb.SelectedText = "";
450 #region Edit menu handlers
451 private void undoToolStripMenuItem_Click(object sender, EventArgs e)
456 private void redoToolStripMenuItem_Click(object sender, EventArgs e)
461 private void cutToolStripMenuItem_Click(object sender, EventArgs e)
466 private void copyToolStripMenuItem_Click(object sender, EventArgs e)
471 private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
476 private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
478 rtb.SelectedText = string.Empty;
481 private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
483 rtb.SelectionStart = 0;
484 rtb.SelectionLength = rtb.Text.Length;
487 private void findToolStripMenuItem_Click(object sender, EventArgs e)
489 tsFindReplace.Show();
490 tfindFindText.Focus();
494 private void tfindClose_Click(object sender, EventArgs e)
496 tsFindReplace.Hide();
499 private class FindHistoryItem
501 public FindHistoryItem(string term, int ss, int sl)
510 public int SelLength;
512 public override string ToString()
518 Dictionary<string, FindHistoryItem> FindHistory = new Dictionary<string, FindHistoryItem>();
519 FindHistoryItem startPos;
522 private void tfindFindText_TextChanged(object sender, EventArgs e)
524 string st = tfindFindText.Text;
526 if (startPos == null)
528 startPos = new FindHistoryItem(string.Empty, rtb.SelectionStart, rtb.SelectionLength);
529 FindHistory[startPos.Term] = startPos;
532 StringComparison type;
534 if (tfindMatchCase.Checked)
536 type = StringComparison.Ordinal;
540 type = StringComparison.OrdinalIgnoreCase;
543 if (FindHistory.ContainsKey(st))
545 tfindFindText.BackColor = Color.FromKnownColor(KnownColor.Window);
546 FindHistoryItem h = FindHistory[st];
548 rtb.Select(h.SelStart, h.SelLength);
551 searchFrom = h.SelStart;
553 if (st == string.Empty)
556 tfindFindText.BackColor = Color.FromKnownColor(KnownColor.Window);
562 if (st == string.Empty)
565 tfindFindText.BackColor = Color.FromKnownColor(KnownColor.Window);
569 int pos = rtb.Text.IndexOf(st, searchFrom, type);
573 tfindFindText.BackColor = Color.FromKnownColor(KnownColor.Window);
574 FindHistory[st] = new FindHistoryItem(st, pos, st.Length);
576 rtb.Select(pos, st.Length);
584 tfindFindText.BackColor = Color.FromArgb(200, 0, 0);
588 private void tfindFindText_KeyDown(object sender, KeyEventArgs e)
590 if (e.KeyCode == Keys.Enter)
592 e.SuppressKeyPress = true;
596 private void tfindFindText_KeyUp(object sender, KeyEventArgs e)
601 tfindDoFind_Click(null, null);
603 e.SuppressKeyPress = true;
608 private void tfindDoFind_Click(object sender, EventArgs e)
611 searchFrom += rtb.SelectionLength;
612 tfindFindText_TextChanged(sender, e);
615 private void tfindFindText_Leave(object sender, EventArgs e)
621 private void tfindFindText_Enter(object sender, EventArgs e)
623 searchFrom = rtb.SelectionStart;
627 private void tfindFindNextReplace_Click(object sender, EventArgs e)
629 tfindDoFind_Click(null, null);
632 private void tfindReplace_Click(object sender, EventArgs e)
634 if (tfindFindText.Text.Length > 0 && rtb.SelectionLength > 0)
636 rtb.SelectedText = tfindReplaceText.Text;
637 tfindDoFind_Click(null, null);
641 private static string ReplaceEx(string original, string pattern, string replacement)
643 int count, position0, position1;
644 count = position0 = position1 = 0;
645 string upperString = original.ToUpper();
646 string upperPattern = pattern.ToUpper();
647 int inc = (original.Length / pattern.Length) * (replacement.Length - pattern.Length);
648 char[] chars = new char[original.Length + Math.Max(0, inc)];
649 while ((position1 = upperString.IndexOf(upperPattern, position0)) != -1)
651 for (int i = position0; i < position1; ++i)
652 chars[count++] = original[i];
653 for (int i = 0; i < replacement.Length; ++i)
654 chars[count++] = replacement[i];
655 position0 = position1 + pattern.Length;
657 if (position0 == 0) return original;
658 for (int i = position0; i < original.Length; ++i)
659 chars[count++] = original[i];
660 return new string(chars, 0, count);
663 private void tfindReplaceAll_Click(object sender, EventArgs e)
665 if (tfindFindText.Text.Length > 0)
667 if (tfindMatchCase.Checked)
669 rtb.Text.Replace(tfindFindText.Text, tfindReplaceText.Text);
673 rtb.Text = ReplaceEx(rtb.Text, tfindFindText.Text, tfindReplaceText.Text);
678 private void syntaxHiglightingToolStripMenuItem_Click(object sender, EventArgs e)
680 if (rtb.SyntaxHighlightEnabled == true)
682 rtb.SyntaxHighlightEnabled = false;
686 rtb.SyntaxHighlightEnabled = true;
688 syntaxHiglightingToolStripMenuItem.Checked = rtb.SyntaxHighlightEnabled;
691 private void ReadCursorPosition()
693 instance.TabConsole.DisplayNotificationInChat(
694 string.Format("Cursor at line {0}, column {1}", rtb.CursorPosition.Line + 1, rtb.CursorPosition.Column + 1),
695 ChatBufferTextStyle.Invisible);
698 private void tbtbSave_Click(object sender, EventArgs e)
700 InventoryManager.ScriptUpdatedCallback handler = (bool uploadSuccess, string uploadStatus, bool compileSuccess, List<string> compileMessages, UUID itemID, UUID assetID) =>
702 if (!IsHandleCreated && instance.MonoRuntime) return;
704 BeginInvoke(new MethodInvoker(() =>
706 if (uploadSuccess && compileSuccess)
708 lblScripStatus.Text = "Saved OK";
714 lblScripStatus.Text = "Compilation failed";
715 if (compileMessages != null)
718 txtStatus.Text = string.Empty;
719 for (int i = 0; i < compileMessages.Count; i++)
721 Match m = Regex.Match(compileMessages[i], @"\((?<line>\d+),\s*(?<column>\d+)\s*\)\s*:\s*(?<kind>\w+)\s*:\s*(?<msg>.*)", RegexOptions.IgnoreCase);
725 int line = 1 + int.Parse(m.Groups["line"].Value, Utils.EnUsCulture);
726 int column = 1 + int.Parse(m.Groups["column"].Value, Utils.EnUsCulture);
727 string kind = m.Groups["kind"].Value;
728 string msg = m.Groups["msg"].Value;
729 instance.TabConsole.DisplayNotificationInChat(
730 string.Format("{0} on line {1}, column {2}: {3}", kind, line, column, msg),
731 ChatBufferTextStyle.Invisible);
732 txtStatus.Text += string.Format("{0} (Ln {1}, Col {2}): {3}", kind, line, column, msg);
736 rtb.CursorPosition = new RRichTextBox.CursorLocation(line - 1, column - 1);
737 ReadCursorPosition();
743 txtStatus.Text += compileMessages[i] + Environment.NewLine;
744 instance.TabConsole.DisplayNotificationInChat(compileMessages[i]);
751 lblScripStatus.Text = rtb.Text = "Failed to download.";
760 lblScripStatus.Text = "Saving...";
762 txtStatus.Text = string.Empty;
764 AssetScriptText n = new AssetScriptText();
770 client.Inventory.RequestUpdateScriptTask(n.AssetData, script.UUID, prim.ID, cbMono.Checked, cbRunning.Checked, handler);
774 client.Inventory.RequestUpdateScriptAgentInventory(n.AssetData, script.UUID, cbMono.Checked, handler);