2008-10-26 23:10:48 +00:00
|
|
|
|
|
|
|
#region ================== Copyright (c) 2007 Pascal vd Heiden
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
|
|
|
|
* This program is released under GNU General Public License
|
|
|
|
*
|
|
|
|
* This program 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Namespaces
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.ComponentModel;
|
|
|
|
using System.Drawing;
|
|
|
|
using System.Text;
|
|
|
|
using System.Windows.Forms;
|
|
|
|
using Microsoft.Win32;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using CodeImp.DoomBuilder.Data;
|
|
|
|
using CodeImp.DoomBuilder.Map;
|
2008-10-29 10:56:14 +00:00
|
|
|
using CodeImp.DoomBuilder.Geometry;
|
2008-10-26 23:10:48 +00:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.Controls
|
|
|
|
{
|
2008-10-29 10:56:14 +00:00
|
|
|
public unsafe partial class ScriptEditControl : RichTextBox
|
2008-10-26 23:10:48 +00:00
|
|
|
{
|
|
|
|
#region ================== Constants
|
|
|
|
|
|
|
|
// Update delay in milliseconds
|
|
|
|
private const int UPDATE_DELAY = 100;
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Variables
|
|
|
|
|
|
|
|
// Range in which updating is required
|
|
|
|
// Only the timer may reset this range, the key press methods
|
|
|
|
private int updatestartline;
|
|
|
|
private int updateendline;
|
|
|
|
|
|
|
|
// Line information
|
|
|
|
private ScriptLineInfo[] lineinfo;
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
// Update timer
|
|
|
|
private Timer updatetimer;
|
|
|
|
|
|
|
|
#endregion
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
#region ================== Properties
|
|
|
|
|
|
|
|
// These prevent changing these properties
|
|
|
|
public new bool AcceptsTab { get { return base.AcceptsTab; } }
|
|
|
|
public new bool AllowDrop { get { return base.AllowDrop; } }
|
|
|
|
public new bool AutoWordSelection { get { return base.AutoWordSelection; } }
|
|
|
|
public new bool EnableAutoDragDrop { get { return base.EnableAutoDragDrop; } }
|
|
|
|
public new bool Multiline { get { return base.Multiline; } }
|
|
|
|
public new bool ShortcutsEnabled { get { return base.ShortcutsEnabled; } }
|
|
|
|
public new bool WordWrap { get { return base.WordWrap; } }
|
|
|
|
public new float ZoomFactor { get { return base.ZoomFactor; } }
|
|
|
|
|
|
|
|
#endregion
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
#region ================== Contructor
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
// Constructor
|
|
|
|
public ScriptEditControl()
|
|
|
|
{
|
|
|
|
// Properties that we need to have set this way.
|
|
|
|
// No, you cannot choose these yourself.
|
|
|
|
base.AcceptsTab = true;
|
|
|
|
base.AllowDrop = false;
|
|
|
|
base.AutoWordSelection = false;
|
|
|
|
base.EnableAutoDragDrop = false;
|
|
|
|
base.Multiline = true;
|
2008-10-29 10:56:14 +00:00
|
|
|
base.ShortcutsEnabled = true;
|
2008-10-26 23:10:48 +00:00
|
|
|
base.WordWrap = false;
|
|
|
|
base.ZoomFactor = 1.0f;
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
// Initialize
|
|
|
|
if(!this.DesignMode)
|
|
|
|
{
|
|
|
|
lineinfo = new ScriptLineInfo[1];
|
|
|
|
lineinfo[0] = new ScriptLineInfo(ScriptMarking.None);
|
|
|
|
updatetimer = new Timer();
|
|
|
|
updatetimer.Interval = UPDATE_DELAY;
|
|
|
|
updatetimer.Tick += new EventHandler(OnUpdateTimerTick);
|
|
|
|
}
|
|
|
|
}
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
// Disposer
|
|
|
|
protected override void Dispose(bool disposing)
|
|
|
|
{
|
|
|
|
// Disposing?
|
|
|
|
if(!base.IsDisposed)
|
|
|
|
{
|
|
|
|
// Dispose managed resources
|
|
|
|
if(disposing)
|
|
|
|
{
|
|
|
|
if(!this.DesignMode)
|
|
|
|
{
|
|
|
|
updatetimer.Stop();
|
|
|
|
updatetimer.Dispose();
|
|
|
|
lineinfo = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
base.Dispose(disposing);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Events
|
|
|
|
|
|
|
|
// Key pressed
|
|
|
|
protected override void OnKeyDown(KeyEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnKeyDown(e);
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-29 06:54:30 +00:00
|
|
|
// Get selection/position
|
|
|
|
int startline = base.GetLineFromCharIndex(base.SelectionStart);
|
|
|
|
int endline = base.GetLineFromCharIndex(base.SelectionStart + base.SelectionLength);
|
2008-10-26 23:10:48 +00:00
|
|
|
|
|
|
|
// Check in which range this keypress must update the highlighting.
|
|
|
|
// The range may only be increased, not decreased!
|
2008-10-29 10:56:14 +00:00
|
|
|
|
|
|
|
// Newline must update surrounding lines also
|
|
|
|
if(e.KeyCode == Keys.Enter)
|
2008-10-29 06:54:30 +00:00
|
|
|
{
|
|
|
|
if(startline > 0) startline--;
|
|
|
|
if(endline < base.Lines.Length) endline++;
|
|
|
|
}
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-29 06:54:30 +00:00
|
|
|
// Update range
|
2008-10-26 23:10:48 +00:00
|
|
|
if(startline < updatestartline) updatestartline = startline;
|
|
|
|
if(endline < updatestartline) updatestartline = endline;
|
|
|
|
if(startline > updateendline) updateendline = startline;
|
|
|
|
if(endline > updateendline) updateendline = endline;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Key presses (repeating)
|
|
|
|
protected override void OnKeyPress(KeyPressEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnKeyPress(e);
|
|
|
|
|
|
|
|
// (Re)start timer!
|
|
|
|
updatetimer.Stop();
|
|
|
|
updatetimer.Start();
|
|
|
|
}
|
|
|
|
|
2008-10-29 10:56:14 +00:00
|
|
|
// Key released
|
|
|
|
protected override void OnKeyUp(KeyEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnKeyUp(e);
|
|
|
|
|
|
|
|
// Get selection/position
|
|
|
|
int startline = base.GetLineFromCharIndex(base.SelectionStart);
|
|
|
|
int endline = base.GetLineFromCharIndex(base.SelectionStart + base.SelectionLength);
|
|
|
|
|
|
|
|
// Check in which range this keypress must update the highlighting.
|
|
|
|
// The range may only be increased, not decreased!
|
|
|
|
|
|
|
|
// Paste must do a complete update
|
|
|
|
if((e.KeyCode == Keys.V) && (e.Modifiers == Keys.Control))
|
|
|
|
{
|
|
|
|
// TODO: Somehow we have to figure out which lines were pasted in
|
|
|
|
// We could use the selection range from keydown event and compare
|
|
|
|
// it with current position
|
|
|
|
startline = 0;
|
|
|
|
endline = base.Lines.Length;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update range
|
|
|
|
if(startline < updatestartline) updatestartline = startline;
|
|
|
|
if(endline < updatestartline) updatestartline = endline;
|
|
|
|
if(startline > updateendline) updateendline = startline;
|
|
|
|
if(endline > updateendline) updateendline = endline;
|
|
|
|
}
|
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
// Time to update!
|
|
|
|
private void OnUpdateTimerTick(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
updatetimer.Stop();
|
|
|
|
UpdateRange();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Methods
|
|
|
|
|
|
|
|
// This updates the range
|
|
|
|
private void UpdateRange()
|
|
|
|
{
|
|
|
|
bool continueupdate = false;
|
|
|
|
int updateline = updatestartline;
|
2008-10-27 08:19:15 +00:00
|
|
|
int selstart = base.SelectionStart;
|
|
|
|
int sellength = base.SelectionLength;
|
|
|
|
General.LockWindowUpdate(base.Handle);
|
|
|
|
|
2008-10-29 10:56:14 +00:00
|
|
|
// Keep scroll position
|
|
|
|
Vector2D point = new Vector2D();
|
|
|
|
IntPtr pointptr = new IntPtr(&point);
|
|
|
|
General.SendMessage(base.Handle, General.EM_GETSCROLLPOS, 0, pointptr.ToInt32());
|
|
|
|
|
2008-10-27 08:19:15 +00:00
|
|
|
if(updateendline > base.Lines.Length) updateendline = base.Lines.Length;
|
2008-10-26 23:10:48 +00:00
|
|
|
|
|
|
|
// First make sure the lineinfo array is big enough
|
|
|
|
if(base.Lines.Length >= lineinfo.Length)
|
|
|
|
{
|
|
|
|
// Resize array
|
|
|
|
ScriptLineInfo[] oldlineinfo = lineinfo;
|
|
|
|
lineinfo = new ScriptLineInfo[base.Lines.Length + 1];
|
|
|
|
oldlineinfo.CopyTo(lineinfo, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start updating the range
|
|
|
|
// Or go beyond the range when the result has influence on the next line
|
2008-10-29 06:54:30 +00:00
|
|
|
while(((updateline <= updateendline) || continueupdate) && (updateline < base.Lines.Length))
|
2008-10-26 23:10:48 +00:00
|
|
|
{
|
|
|
|
continueupdate = UpdateLine(updateline++);
|
|
|
|
}
|
2008-10-29 10:56:14 +00:00
|
|
|
|
2008-10-26 23:10:48 +00:00
|
|
|
// Reset the range to current position/selection
|
2008-10-27 08:19:15 +00:00
|
|
|
int startline = base.GetLineFromCharIndex(selstart);
|
|
|
|
int endline = base.GetLineFromCharIndex(selstart + sellength);
|
2008-10-26 23:10:48 +00:00
|
|
|
updatestartline = Math.Min(startline, endline);
|
|
|
|
updateendline = Math.Max(startline, endline);
|
2008-10-29 06:54:30 +00:00
|
|
|
|
2008-10-27 08:19:15 +00:00
|
|
|
// Restore selection
|
|
|
|
base.SelectionStart = selstart;
|
|
|
|
base.SelectionLength = sellength;
|
2008-10-29 10:56:14 +00:00
|
|
|
|
|
|
|
// Restore scroll position
|
|
|
|
General.SendMessage(base.Handle, General.EM_SETSCROLLPOS, 0, pointptr.ToInt32());
|
|
|
|
|
|
|
|
// Done
|
2008-10-27 08:19:15 +00:00
|
|
|
General.LockWindowUpdate(IntPtr.Zero);
|
2008-10-26 23:10:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// This parses a single line to update the syntax highlighting
|
2008-10-27 08:19:15 +00:00
|
|
|
// NOTE: Before calling this make sure the lineinfo array is resized correctly!
|
|
|
|
// NOTE: This function changes the selection and does not prevent any redrawing!
|
2008-10-26 23:10:48 +00:00
|
|
|
// Returns true if the change continues on the next line (multi-line comment changes)
|
|
|
|
private bool UpdateLine(int lineindex)
|
|
|
|
{
|
|
|
|
// Get the start information
|
2008-10-27 08:19:15 +00:00
|
|
|
ScriptLineInfo info = lineinfo[lineindex];
|
2008-10-26 23:10:48 +00:00
|
|
|
ScriptLineInfo endinfo = lineinfo[lineindex + 1];
|
2008-10-27 08:19:15 +00:00
|
|
|
string text = base.Lines[lineindex];
|
|
|
|
int lineoffset = base.GetFirstCharIndexFromLine(lineindex);
|
|
|
|
int curpos = 0;
|
|
|
|
int prevpos = 0;
|
2008-10-26 23:10:48 +00:00
|
|
|
|
|
|
|
// TODO: Scan the line
|
|
|
|
// TODO: Use regexes for this?
|
2008-10-27 08:19:15 +00:00
|
|
|
|
|
|
|
// TEST: Block comment only
|
|
|
|
int startcurpos;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
startcurpos = curpos;
|
|
|
|
|
|
|
|
// If we're in a block comment, we first have to find the block ending
|
2008-10-29 06:54:30 +00:00
|
|
|
if(info.mark == ScriptMarking.BlockComment)
|
2008-10-27 08:19:15 +00:00
|
|
|
{
|
2008-10-29 06:54:30 +00:00
|
|
|
int endpos = text.IndexOf("*/", curpos);
|
2008-10-27 08:19:15 +00:00
|
|
|
if(endpos > -1)
|
|
|
|
{
|
|
|
|
// Block comment ends here
|
|
|
|
curpos = endpos + 2;
|
|
|
|
base.SelectionStart = lineoffset + prevpos;
|
2008-10-29 10:56:14 +00:00
|
|
|
base.SelectionLength = curpos - prevpos;
|
2008-10-27 08:19:15 +00:00
|
|
|
base.SelectionColor = General.Colors.Comments.ToColor();
|
2008-10-29 06:54:30 +00:00
|
|
|
info.mark = ScriptMarking.None;
|
2008-10-27 08:19:15 +00:00
|
|
|
prevpos = curpos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Out of the block comment?
|
2008-10-29 06:54:30 +00:00
|
|
|
if(info.mark != ScriptMarking.BlockComment)
|
2008-10-27 08:19:15 +00:00
|
|
|
{
|
2008-10-29 06:54:30 +00:00
|
|
|
int endpos = text.IndexOf("/*", curpos);
|
2008-10-27 08:19:15 +00:00
|
|
|
if(endpos > -1)
|
|
|
|
{
|
|
|
|
// Block comment starts here
|
|
|
|
curpos = endpos;
|
|
|
|
base.SelectionStart = lineoffset + prevpos;
|
2008-10-29 10:56:14 +00:00
|
|
|
base.SelectionLength = curpos - prevpos;
|
2008-10-27 08:19:15 +00:00
|
|
|
base.SelectionColor = General.Colors.PlainText.ToColor();
|
2008-10-29 06:54:30 +00:00
|
|
|
info.mark = ScriptMarking.BlockComment;
|
2008-10-27 08:19:15 +00:00
|
|
|
prevpos = curpos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while(startcurpos < curpos);
|
|
|
|
|
|
|
|
// More to mark?
|
|
|
|
if(prevpos < text.Length)
|
|
|
|
{
|
2008-10-29 06:54:30 +00:00
|
|
|
if(info.mark == ScriptMarking.BlockComment)
|
2008-10-27 08:19:15 +00:00
|
|
|
{
|
|
|
|
base.SelectionStart = lineoffset + prevpos;
|
2008-10-29 10:56:14 +00:00
|
|
|
base.SelectionLength = text.Length - prevpos;
|
2008-10-27 08:19:15 +00:00
|
|
|
base.SelectionColor = General.Colors.Comments.ToColor();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
base.SelectionStart = lineoffset + prevpos;
|
2008-10-29 10:56:14 +00:00
|
|
|
base.SelectionLength = text.Length - prevpos;
|
2008-10-27 08:19:15 +00:00
|
|
|
base.SelectionColor = General.Colors.PlainText.ToColor();
|
|
|
|
}
|
|
|
|
}
|
2008-10-26 23:10:48 +00:00
|
|
|
|
2008-10-29 06:54:30 +00:00
|
|
|
// Check if the block content marking changes for next line
|
|
|
|
if((info.mark == ScriptMarking.BlockComment) && (endinfo.mark != ScriptMarking.BlockComment))
|
|
|
|
{
|
|
|
|
// Change marking that the next line begins with
|
|
|
|
lineinfo[lineindex + 1].mark = ScriptMarking.BlockComment;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if((info.mark != ScriptMarking.BlockComment) && (endinfo.mark == ScriptMarking.BlockComment))
|
|
|
|
{
|
|
|
|
// Change marking that the next line begins with
|
|
|
|
lineinfo[lineindex + 1].mark = ScriptMarking.None;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// No change for the next line
|
|
|
|
return false;
|
|
|
|
}
|
2008-10-26 23:10:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|