//
//
//
//
// $Revision$
//
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;
namespace ICSharpCode.TextEditor
{
///
/// This class is used for a basic text area control
///
[ToolboxBitmap("ICSharpCode.TextEditor.Resources.TextEditorControl.bmp")]
[ToolboxItem(true)]
public class TextEditorControl : TextEditorControlBase
{
protected Panel textAreaPanel = new Panel();
TextAreaControl primaryTextArea;
Splitter textAreaSplitter = null;
TextAreaControl secondaryTextArea = null;
PrintDocument printDocument = null;
[Browsable(false)]
public PrintDocument PrintDocument {
get {
if (printDocument == null) {
printDocument = new PrintDocument();
printDocument.BeginPrint += new PrintEventHandler(this.BeginPrint);
printDocument.PrintPage += new PrintPageEventHandler(this.PrintPage);
}
return printDocument;
}
}
TextAreaControl activeTextAreaControl;
public override TextAreaControl ActiveTextAreaControl {
get {
return activeTextAreaControl;
}
}
protected void SetActiveTextAreaControl(TextAreaControl value)
{
if (activeTextAreaControl != value) {
activeTextAreaControl = value;
if (ActiveTextAreaControlChanged != null) {
ActiveTextAreaControlChanged(this, EventArgs.Empty);
}
}
}
public event EventHandler ActiveTextAreaControlChanged;
public TextEditorControl()
{
SetStyle(ControlStyles.ContainerControl, true);
textAreaPanel.Dock = DockStyle.Fill;
Document = (new DocumentFactory()).CreateDocument();
Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy();
primaryTextArea = new TextAreaControl(this);
activeTextAreaControl = primaryTextArea;
primaryTextArea.TextArea.GotFocus += OnTextAreaGotFocus;
primaryTextArea.Dock = DockStyle.Fill;
textAreaPanel.Controls.Add(primaryTextArea);
InitializeTextAreaControl(primaryTextArea);
Controls.Add(textAreaPanel);
ResizeRedraw = true;
Document.UpdateCommited += new EventHandler(CommitUpdateRequested);
OptionsChanged();
}
void OnTextAreaGotFocus(object sender, EventArgs e)
{
SetActiveTextAreaControl(primaryTextArea);
}
protected virtual void InitializeTextAreaControl(TextAreaControl newControl)
{
}
public override void OptionsChanged()
{
primaryTextArea.OptionsChanged();
if (secondaryTextArea != null) {
secondaryTextArea.OptionsChanged();
}
}
public void Split()
{
if (secondaryTextArea == null) {
secondaryTextArea = new TextAreaControl(this);
secondaryTextArea.Dock = DockStyle.Bottom;
secondaryTextArea.Height = Height / 2;
secondaryTextArea.TextArea.GotFocus += delegate {
SetActiveTextAreaControl(secondaryTextArea);
};
textAreaSplitter = new Splitter();
textAreaSplitter.BorderStyle = BorderStyle.FixedSingle ;
textAreaSplitter.Height = 8;
textAreaSplitter.Dock = DockStyle.Bottom;
textAreaPanel.Controls.Add(textAreaSplitter);
textAreaPanel.Controls.Add(secondaryTextArea);
InitializeTextAreaControl(secondaryTextArea);
secondaryTextArea.OptionsChanged();
} else {
SetActiveTextAreaControl(primaryTextArea);
textAreaPanel.Controls.Remove(secondaryTextArea);
textAreaPanel.Controls.Remove(textAreaSplitter);
secondaryTextArea.Dispose();
textAreaSplitter.Dispose();
secondaryTextArea = null;
textAreaSplitter = null;
}
}
[Browsable(false)]
public bool EnableUndo {
get {
return Document.UndoStack.CanUndo;
}
}
[Browsable(false)]
public bool EnableRedo {
get {
return Document.UndoStack.CanRedo;
}
}
public void Undo()
{
if (Document.ReadOnly) {
return;
}
if (Document.UndoStack.CanUndo) {
BeginUpdate();
Document.UndoStack.Undo();
Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
this.primaryTextArea.TextArea.UpdateMatchingBracket();
if (secondaryTextArea != null) {
this.secondaryTextArea.TextArea.UpdateMatchingBracket();
}
EndUpdate();
}
}
public void Redo()
{
if (Document.ReadOnly) {
return;
}
if (Document.UndoStack.CanRedo) {
BeginUpdate();
Document.UndoStack.Redo();
Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
this.primaryTextArea.TextArea.UpdateMatchingBracket();
if (secondaryTextArea != null) {
this.secondaryTextArea.TextArea.UpdateMatchingBracket();
}
EndUpdate();
}
}
public virtual void SetHighlighting(string name)
{
Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(name);
}
protected override void Dispose(bool disposing)
{
if (disposing) {
if (printDocument != null) {
printDocument.BeginPrint -= new PrintEventHandler(this.BeginPrint);
printDocument.PrintPage -= new PrintPageEventHandler(this.PrintPage);
printDocument = null;
}
Document.UndoStack.ClearAll();
Document.UpdateCommited -= new EventHandler(CommitUpdateRequested);
if (textAreaPanel != null) {
if (secondaryTextArea != null) {
secondaryTextArea.Dispose();
textAreaSplitter.Dispose();
secondaryTextArea = null;
textAreaSplitter = null;
}
if (primaryTextArea != null) {
primaryTextArea.Dispose();
}
textAreaPanel.Dispose();
textAreaPanel = null;
}
}
base.Dispose(disposing);
}
#region Update Methods
public override void EndUpdate()
{
base.EndUpdate();
Document.CommitUpdate();
if (!IsInUpdate) {
ActiveTextAreaControl.Caret.OnEndUpdate();
}
}
void CommitUpdateRequested(object sender, EventArgs e)
{
if (IsInUpdate) {
return;
}
foreach (TextAreaUpdate update in Document.UpdateQueue) {
switch (update.TextAreaUpdateType) {
case TextAreaUpdateType.PositionToEnd:
this.primaryTextArea.TextArea.UpdateToEnd(update.Position.Y);
if (this.secondaryTextArea != null) {
this.secondaryTextArea.TextArea.UpdateToEnd(update.Position.Y);
}
break;
case TextAreaUpdateType.PositionToLineEnd:
case TextAreaUpdateType.SingleLine:
this.primaryTextArea.TextArea.UpdateLine(update.Position.Y);
if (this.secondaryTextArea != null) {
this.secondaryTextArea.TextArea.UpdateLine(update.Position.Y);
}
break;
case TextAreaUpdateType.SinglePosition:
this.primaryTextArea.TextArea.UpdateLine(update.Position.Y, update.Position.X, update.Position.X);
if (this.secondaryTextArea != null) {
this.secondaryTextArea.TextArea.UpdateLine(update.Position.Y, update.Position.X, update.Position.X);
}
break;
case TextAreaUpdateType.LinesBetween:
this.primaryTextArea.TextArea.UpdateLines(update.Position.X, update.Position.Y);
if (this.secondaryTextArea != null) {
this.secondaryTextArea.TextArea.UpdateLines(update.Position.X, update.Position.Y);
}
break;
case TextAreaUpdateType.WholeTextArea:
this.primaryTextArea.TextArea.Invalidate();
if (this.secondaryTextArea != null) {
this.secondaryTextArea.TextArea.Invalidate();
}
break;
}
}
Document.UpdateQueue.Clear();
// this.primaryTextArea.TextArea.Update();
// if (this.secondaryTextArea != null) {
// this.secondaryTextArea.TextArea.Update();
// }
}
#endregion
#region Printing routines
int curLineNr = 0;
float curTabIndent = 0;
StringFormat printingStringFormat;
void BeginPrint(object sender, PrintEventArgs ev)
{
curLineNr = 0;
printingStringFormat = (StringFormat)System.Drawing.StringFormat.GenericTypographic.Clone();
// 100 should be enough for everyone ...err ?
float[] tabStops = new float[100];
for (int i = 0; i < tabStops.Length; ++i) {
tabStops[i] = TabIndent * primaryTextArea.TextArea.TextView.WideSpaceWidth;
}
printingStringFormat.SetTabStops(0, tabStops);
}
void Advance(ref float x, ref float y, float maxWidth, float size, float fontHeight)
{
if (x + size < maxWidth) {
x += size;
} else {
x = curTabIndent;
y += fontHeight;
}
}
// btw. I hate source code duplication ... but this time I don't care !!!!
float MeasurePrintingHeight(Graphics g, LineSegment line, float maxWidth)
{
float xPos = 0;
float yPos = 0;
float fontHeight = Font.GetHeight(g);
// bool gotNonWhitespace = false;
curTabIndent = 0;
FontContainer fontContainer = TextEditorProperties.FontContainer;
foreach (TextWord word in line.Words) {
switch (word.Type) {
case TextWordType.Space:
Advance(ref xPos, ref yPos, maxWidth, primaryTextArea.TextArea.TextView.SpaceWidth, fontHeight);
// if (!gotNonWhitespace) {
// curTabIndent = xPos;
// }
break;
case TextWordType.Tab:
Advance(ref xPos, ref yPos, maxWidth, TabIndent * primaryTextArea.TextArea.TextView.WideSpaceWidth, fontHeight);
// if (!gotNonWhitespace) {
// curTabIndent = xPos;
// }
break;
case TextWordType.Word:
// if (!gotNonWhitespace) {
// gotNonWhitespace = true;
// curTabIndent += TabIndent * primaryTextArea.TextArea.TextView.GetWidth(' ');
// }
SizeF drawingSize = g.MeasureString(word.Word, word.GetFont(fontContainer), new SizeF(maxWidth, fontHeight * 100), printingStringFormat);
Advance(ref xPos, ref yPos, maxWidth, drawingSize.Width, fontHeight);
break;
}
}
return yPos + fontHeight;
}
void DrawLine(Graphics g, LineSegment line, float yPos, RectangleF margin)
{
float xPos = 0;
float fontHeight = Font.GetHeight(g);
// bool gotNonWhitespace = false;
curTabIndent = 0 ;
FontContainer fontContainer = TextEditorProperties.FontContainer;
foreach (TextWord word in line.Words) {
switch (word.Type) {
case TextWordType.Space:
Advance(ref xPos, ref yPos, margin.Width, primaryTextArea.TextArea.TextView.SpaceWidth, fontHeight);
// if (!gotNonWhitespace) {
// curTabIndent = xPos;
// }
break;
case TextWordType.Tab:
Advance(ref xPos, ref yPos, margin.Width, TabIndent * primaryTextArea.TextArea.TextView.WideSpaceWidth, fontHeight);
// if (!gotNonWhitespace) {
// curTabIndent = xPos;
// }
break;
case TextWordType.Word:
// if (!gotNonWhitespace) {
// gotNonWhitespace = true;
// curTabIndent += TabIndent * primaryTextArea.TextArea.TextView.GetWidth(' ');
// }
g.DrawString(word.Word, word.GetFont(fontContainer), BrushRegistry.GetBrush(word.Color), xPos + margin.X, yPos);
SizeF drawingSize = g.MeasureString(word.Word, word.GetFont(fontContainer), new SizeF(margin.Width, fontHeight * 100), printingStringFormat);
Advance(ref xPos, ref yPos, margin.Width, drawingSize.Width, fontHeight);
break;
}
}
}
void PrintPage(object sender, PrintPageEventArgs ev)
{
Graphics g = ev.Graphics;
float yPos = ev.MarginBounds.Top;
while (curLineNr < Document.TotalNumberOfLines) {
LineSegment curLine = Document.GetLineSegment(curLineNr);
if (curLine.Words != null) {
float drawingHeight = MeasurePrintingHeight(g, curLine, ev.MarginBounds.Width);
if (drawingHeight + yPos > ev.MarginBounds.Bottom) {
break;
}
DrawLine(g, curLine, yPos, ev.MarginBounds);
yPos += drawingHeight;
}
++curLineNr;
}
// If more lines exist, print another page.
ev.HasMorePages = curLineNr < Document.TotalNumberOfLines;
}
#endregion
}
}