+2005-06-21 Peter Bartok <pbartok@novell.com>
+
+ * Form.cs: Set focus to active control when form is activated
+ * TextControl.cs:
+ - Added word-wrap functionality to RecalculateLine()
+ - Added some short function descriptions for VS.Net to aid in
+ writing dependent controls
+ - Added Caret property, returning the current coords of the caret
+ - Added ViewPortWidth and ViewPortHeight properties
+ - Added Wrap property
+ - Added CaretMoved event
+ - Removed some old debug code
+ - Split() can now create soft splits
+ - Added PreviousTag()/NextTag() to allow walking "tag-lists"
+ - Added method to format existing text
+ - Fixed size/alignment calculations to use viewport
+ - RecalculateDocument now can handle changing line-numbers while
+ calculating lines
+
+ * TextBox.cs:
+ - Added some wrap logic, we don't wrap if alignment is not left
+ - Added casts for scrollbar var, base class switched types to
+ also support RichTextBoxA
+ - Implemented handling of scrollbar visibility flags
+
+ * TextBoxBase.cs:
+ - Switched scrollbars type to RichTextBoxScrollBars to support
+ RichTextBox
+ - Added tracking of canvas width/height
+ - Switched scrollbars to be not selectable (to keep focus on text)
+ - Added central CalculateDocument() method to handle all redraw
+ requirements
+ - Added ReadOnly support
+ - Added WordWrap support
+ - Fixed handling of Enter key (we now treat it as a DialogKey)
+ - Fixed caret positioning when h or v scroll is not zero
+ - Fixed placing/generation of vertical scrollbar
+ - Added CalculateScrollBars() method to allow updating scrollbar
+ limits and visibility
+ - Fixed handling of horizontal scroll
+ - Added handling of vertical scroll
+ - Implemented auto-'jump' when caret moves to close to a left or
+ right border and there is text to be scrolled into view (currently
+ there's the potential for a stack overflow, until a bug in
+ scrollbar is fixed)
+
2005-06-21 Geoff Norton <gnorton@customerdna.com>
* XplatUIOSX.cs: Initial implementation of WM_ERASEBKGND
}
case Msg.WM_SETFOCUS: {
-#if not
if (this.ActiveControl != null) {
ActiveControl.Focus();
+ return; // FIXME - do we need to run base.WndProc, even though we just changed focus?
}
-#endif
base.WndProc(ref m);
return;
}
public TextBox() {
accepts_return = false;
password_char = '\u25cf';
- scrollbars = ScrollBars.None;
+ scrollbars = RichTextBoxScrollBars.None;
alignment = HorizontalAlignment.Left;
this.LostFocus +=new EventHandler(TextBox_LostFocus);
this.BackColor = ThemeEngine.Current.ColorWindow;
[Localizable(true)]
public ScrollBars ScrollBars {
get {
- return scrollbars;
+ return (ScrollBars)scrollbars;
}
set {
- if (value != scrollbars) {
- scrollbars = value;
+ if (value != (ScrollBars)scrollbars) {
+ scrollbars = (RichTextBoxScrollBars)value;
+ base.CalculateScrollBars();
}
}
}
set {
if (value != alignment) {
alignment = value;
+
+ // MS word-wraps if alignment isn't left
+ if (alignment != HorizontalAlignment.Left) {
+ document.Wrap = true;
+ } else {
+ document.Wrap = word_wrap;
+ }
+
for (int i = 1; i <= document.Lines; i++) {
document.GetLine(i).Alignment = value;
}
internal Document document;
internal LineTag caret_tag; // tag our cursor is in
internal int caret_pos; // position on the line our cursor is in (can be 0 = beginning of line)
- internal int viewport_x; // left visible pixel
- internal int viewport_y; // top visible pixel
internal HScrollBar hscroll;
internal VScrollBar vscroll;
- internal ScrollBars scrollbars;
+ internal RichTextBoxScrollBars scrollbars;
internal bool grabbed;
internal bool richtext;
internal int requested_height;
-
+ internal int canvas_width;
+ internal int canvas_height;
+ internal int track_width = 20;
#if Debug
internal static bool draw_lines = false;
#endif
word_wrap = true;
richtext = false;
document = new Document(this);
+ //document.CaretMoved += new EventHandler(CaretMoved);
+ document.Wrap = true;
requested_height = -1;
MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
- scrollbars = ScrollBars.None;
+ scrollbars = RichTextBoxScrollBars.None;
hscroll = new HScrollBar();
- hscroll.ValueChanged +=new EventHandler(hscroll_ValueChanged);
- hscroll.Enabled = true;
+ hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
+ hscroll.control_style &= ~ControlStyles.Selectable;
+ hscroll.Enabled = false;
hscroll.Visible = false;
vscroll = new VScrollBar();
+ vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
+ vscroll.control_style &= ~ControlStyles.Selectable;
+ vscroll.Enabled = false;
vscroll.Visible = false;
this.Controls.Add(hscroll);
//SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
+
+ canvas_width = this.Width;
+ canvas_height = this.Height;
+
+ CalculateScrollBars();
}
#endregion // Internal Constructor
for (i = 0; i < l; i++) {
document.Add(i+1, CaseAdjust(value[i]), alignment, Font, brush);
}
- document.RecalculateDocument(CreateGraphics());
+ CalculateDocument();
OnTextChanged(EventArgs.Empty);
}
}
}
set {
- document.ReplaceSelection(CaseAdjust(value));
- OnTextChanged(EventArgs.Empty);
+ if (!read_only) {
+ document.ReplaceSelection(CaseAdjust(value));
+ OnTextChanged(EventArgs.Empty);
+ }
}
}
for (i = 1; i < document.Lines; i++) {
sb.Append(document.GetLine(i).text.ToString() + Environment.NewLine);
}
-
return sb.ToString();
}
}
set {
- if (value == base.Text)
+ if (value == base.Text) {
return;
+ }
if (value != null) {
Line line;
} else {
document.Clear();
document.Add(1, CaseAdjust(value), alignment, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
- document.RecalculateDocument(CreateGraphics());
+ CalculateDocument();
line = document.GetLine(1);
document.SetSelectionStart(line, 0);
document.SetSelectionEnd(line, value.Length);
set {
if (value != word_wrap) {
word_wrap = value;
+ document.Wrap = value;
}
}
}
#region Public Instance Methods
public void AppendText(string text) {
-
if (multiline) {
string[] lines;
int linecount;
document.Add(document.CaretLine.LineNo+i, CaseAdjust(lines[i]), alignment, document.CaretTag.font, document.CaretTag.color);
}
- document.RecalculateDocument(CreateGraphics());
+ CalculateDocument();
document.MoveCaret(CaretDirection.CtrlEnd);
- Invalidate();
} else {
document.MoveCaret(CaretDirection.CtrlEnd);
document.InsertStringAtCaret(text, true);
protected override bool IsInputKey(Keys keyData) {
switch (keyData) {
+#if not
+ // We handle Enter in ProcessDialogKey
case Keys.Enter: {
if (multiline && (accepts_return || ((keyData & Keys.Control) != 0))) {
return true;
}
return false;
}
+#endif
case Keys.Tab: {
if (accepts_tab) {
} else {
document.MoveCaret(CaretDirection.CharBack);
}
+ CaretMoved(this, null);
return true;
}
} else {
document.MoveCaret(CaretDirection.CharForward);
}
+ CaretMoved(this, null);
return true;
}
case Keys.Up: {
document.SetSelectionToCaret(true);
document.MoveCaret(CaretDirection.LineUp);
+ CaretMoved(this, null);
return true;
}
case Keys.Down: {
document.SetSelectionToCaret(true);
document.MoveCaret(CaretDirection.LineDown);
+ CaretMoved(this, null);
return true;
}
} else {
document.MoveCaret(CaretDirection.Home);
}
+ CaretMoved(this, null);
return true;
}
} else {
document.MoveCaret(CaretDirection.End);
}
+ CaretMoved(this, null);
return true;
}
case Keys.Enter: {
- if (multiline && (accepts_return || ((Control.ModifierKeys & Keys.Control) != 0))) {
+ if (!read_only && multiline && (accepts_return || ((Control.ModifierKeys & Keys.Control) != 0))) {
+ Line line;
+
if (document.selection_visible) {
document.ReplaceSelection("");
}
document.SetSelectionToCaret(true);
- document.Split(document.CaretLine, document.CaretTag, document.CaretPosition);
+ line = document.CaretLine;
+
+ document.Split(document.CaretLine, document.CaretTag, document.CaretPosition, false);
OnTextChanged(EventArgs.Empty);
- document.UpdateView(document.CaretLine, 2, 0);
+ document.UpdateView(line, 2, 0);
document.MoveCaret(CaretDirection.CharForward);
+ CaretMoved(this, null);
return true;
}
break;
}
case Keys.Tab: {
- if (accepts_tab) {
+ if (!read_only && accepts_tab) {
document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
if (document.selection_visible) {
document.ReplaceSelection("");
document.SetSelectionToCaret(true);
OnTextChanged(EventArgs.Empty);
+ CaretMoved(this, null);
return true;
}
break;
case Keys.Back: {
+ if (read_only) {
+ break;
+ }
+
// delete only deletes on the line, doesn't do the combine
if (document.selection_visible) {
document.ReplaceSelection("");
document.MoveCaret(CaretDirection.CharBack);
OnTextChanged(EventArgs.Empty);
}
+ CaretMoved(this, null);
return true;
}
case Keys.Delete: {
+ if (read_only) {
+ break;
+ }
+
// delete only deletes on the line, doesn't do the combine
if (document.CaretPosition == document.CaretLine.text.Length) {
if (document.CaretLine.LineNo < document.Lines) {
document.DeleteChar(document.CaretTag, document.CaretPosition, true);
OnTextChanged(EventArgs.Empty);
}
+ CaretMoved(this, null);
return true;
}
}
}
}
+ document.ViewPortWidth = this.Width;
+ document.ViewPortHeight = this.Height;
+
+ CalculateDocument();
+
base.SetBoundsCore (x, y, width, height, specified);
}
return;
}
- if (m.WParam.ToInt32() >= 32) { // FIXME, tabs should probably go through
+ if (!read_only && (m.WParam.ToInt32() >= 32)) { // FIXME, tabs should probably go through
if (document.selection_visible) {
document.ReplaceSelection("");
}
case CharacterCasing.Normal: {
document.InsertCharAtCaret((char)m.WParam, true);
OnTextChanged(EventArgs.Empty);
+ CaretMoved(this, null);
return;
}
case CharacterCasing.Lower: {
document.InsertCharAtCaret(Char.ToLower((char)m.WParam), true);
OnTextChanged(EventArgs.Empty);
+ CaretMoved(this, null);
return;
}
case CharacterCasing.Upper: {
document.InsertCharAtCaret(Char.ToUpper((char)m.WParam), true);
OnTextChanged(EventArgs.Empty);
+ CaretMoved(this, null);
return;
}
}
private void PaintControl(PaintEventArgs pevent) {
// Fill background
pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
- //pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
+ pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
// Draw the viewable document
document.Draw(pevent.Graphics, pevent.ClipRectangle);
Rectangle rect = ClientRectangle;
rect.Width--;
rect.Height--;
- pevent.Graphics.DrawRectangle(ThemeEngine.Current.ResPool.GetPen(ThemeEngine.Current.ColorButtonShadow), rect);
-
- // Set the scrollbar
- switch (scrollbars) {
- case ScrollBars.Both: {
- break;
- }
- case ScrollBars.Vertical: {
- break;
- }
- case ScrollBars.Horizontal: {
- hscroll.Minimum = 0;
- hscroll.Maximum = document.Width - this.Width;
- break;
- }
- }
+ //pevent.Graphics.DrawRectangle(ThemeEngine.Current.ResPool.GetPen(ThemeEngine.Current.ColorButtonShadow), rect);
#if Debug
int start;
p = new Pen(Color.Red, 1);
// First, figure out from what line to what line we need to draw
- start = document.GetLineByPixel(pevent.ClipRectangle.Top - viewport_y, false).line_no;
- end = document.GetLineByPixel(pevent.ClipRectangle.Bottom - viewport_y, false).line_no;
+ start = document.GetLineByPixel(pevent.ClipRectangle.Top - document.ViewPortY, false).line_no;
+ end = document.GetLineByPixel(pevent.ClipRectangle.Bottom - document.ViewPortY, false).line_no;
//Console.WriteLine("Starting drawing on line '{0}'", document.GetLine(start));
//Console.WriteLine("Ending drawing on line '{0}'", document.GetLine(end));
return;
}
- tag = document.FindTag(e.X, e.Y, out pos, false);
+ tag = document.FindTag(e.X + document.ViewPortX, e.Y + document.ViewPortY, out pos, false);
Console.WriteLine("Click found tag {0}, character {1}", tag, pos);
line = tag.line;
this.Capture = false;
this.grabbed = false;
if (e.Button == MouseButtons.Left) {
- document.PositionCaret(e.X + viewport_x, e.Y + viewport_y);
+ document.PositionCaret(e.X, e.Y);
document.SetSelectionToCaret(false);
document.DisplayCaret();
return;
private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
-
- // First, check which scrollbars we need
-
+ canvas_width = this.Width;
+ canvas_height = this.Height;
+ // We always move them, they just might not be displayed
hscroll.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hscroll.Height, Width, hscroll.Height);
+ vscroll.Bounds = new Rectangle (ClientRectangle.Right - vscroll.Width, ClientRectangle.Top, vscroll.Width, Height);
}
+ private void CalculateDocument() {
+ document.RecalculateDocument(CreateGraphics());
+ CalculateScrollBars();
+ Invalidate(); // FIXME - do we need this?
+ }
+
+ protected void CalculateScrollBars() {
+ // No scrollbars for a single line
+ if (document.Width >= this.Width) {
+ hscroll.Enabled = true;
+ hscroll.Minimum = 0;
+ hscroll.Maximum = document.Width - this.Width;
+ } else {
+ hscroll.Enabled = false;
+ }
+
+ if (document.Height >= this.Height) {
+ vscroll.Enabled = true;
+ vscroll.Minimum = 0;
+ vscroll.Maximum = document.Height - this.Height;
+ } else {
+ vscroll.Enabled = false;
+ }
+
+
+ if (!multiline) {
+ return;
+ }
+
+ if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
+ if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
+ hscroll.Visible = true;
+ }
+ }
+
+ if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
+ if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
+ vscroll.Visible = true;
+ }
+ }
+
+ if (hscroll.Visible) {
+ vscroll.Maximum += hscroll.Height * 2;
+ canvas_height = this.Height - hscroll.Height * 2;
+ }
+
+ if (vscroll.Visible) {
+ hscroll.Maximum += vscroll.Width * 2;
+ canvas_width = this.Width - vscroll.Width * 2;
+ }
+ }
+
private void hscroll_ValueChanged(object sender, EventArgs e) {
XplatUI.ScrollWindow(this.Handle, document.ViewPortX-this.hscroll.Value, 0, false);
document.ViewPortX = this.hscroll.Value;
document.UpdateCaret();
- Console.WriteLine("Dude scrolled");
+ Console.WriteLine("Dude scrolled horizontal");
}
+ private void vscroll_ValueChanged(object sender, EventArgs e) {\r
+ XplatUI.ScrollWindow(this.Handle, 0, document.ViewPortY-this.vscroll.Value, false);
+ document.ViewPortX = this.vscroll.Value;
+ document.UpdateCaret();
+ Console.WriteLine("Dude scrolled vertical");
+ }\r
+
private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
// FIXME - handle auto-scrolling if mouse is to the right/left of the window
if (grabbed) {
- document.PositionCaret(e.X + viewport_x, e.Y + viewport_y);
+ document.PositionCaret(e.X, e.Y);
document.SetSelectionToCaret(false);
document.DisplayCaret();
}
// Make sure the caret height is matching the new font height
document.AlignCaret();
}
- }
+ }\r
+\r
+ /// <summary>Ensure the caret is always visible</summary>\r
+ internal void CaretMoved(object sender, EventArgs e) {\r
+ Point pos;\r
+ \r
+ pos = document.Caret;\r
+ Console.WriteLine("Caret now at {0} (Thumb: {1}x{2}, Canvas: {3}x{4}, Document {5}x{6})", pos, hscroll.Value, vscroll.Value, canvas_width, canvas_height, document.Width, document.Height);\r
+\r
+ // Handle horizontal scrolling\r
+ if (pos.X < (document.ViewPortX + track_width)) {\r
+ if ((hscroll.Value - track_width) >= hscroll.Minimum) {\r
+ hscroll.Value -= track_width;\r
+ } else {\r
+ hscroll.Value = hscroll.Minimum;\r
+ }\r
+ }\r
+\r
+ if ((pos.X > (this.Width + document.ViewPortX - track_width)) && (hscroll.Value != hscroll.Maximum)) {\r
+ if ((hscroll.Value + track_width) <= hscroll.Maximum) {\r
+ hscroll.Value += track_width;\r
+ } else {\r
+ hscroll.Value = hscroll.Maximum;\r
+ }\r
+ }\r
+\r
+ if (!multiline) {\r
+ return;\r
+ }\r
+#if not\r
+ // Handle vertical scrolling\r
+ if (pos.Y < (document.ViewPortY + track_width)) {\r
+ if ((hscroll.Value - track_width) >= hscroll.Minimum) {\r
+ hscroll.Value -= track_width;\r
+ } else {\r
+ hscroll.Value = hscroll.Minimum;\r
+ }\r
+\r
+ if (pos.X > this.Width + document.ViewPortX) {\r
+ hscroll.Value = hscroll.Minimum;\r
+ }\r
+ }\r
+\r
+ if (pos.X > (this.Width + document.ViewPortX - track_width)) {\r
+ if ((hscroll.Value + track_width) <= hscroll.Maximum) {\r
+ hscroll.Value += track_width;\r
+ } else {\r
+ hscroll.Value = hscroll.Maximum;\r
+ }\r
+ }\r
+\r
+ if (pos.X < document.ViewPortX) {\r
+ hscroll.Value = hscroll.Minimum;\r
+ }\r
+#endif\r
+ }\r
}
}
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Copyright (c) 2004 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2004-2005 Novell, Inc. (http://www.novell.com)
//
// Authors:
// Peter Bartok pbartok@novell.com
internal int ascent; // Ascent of the line (ascent of the tallest tag)
internal HorizontalAlignment alignment; // Alignment of the line
internal int align_shift; // Pixel shift caused by the alignment
+ internal bool soft_break; // Tag is 'broken soft' and continuation from previous line
+
// Stuff that's important for the tree
internal Line parent; // Our parent line
parent = null;
text = null;
recalc = true;
+ soft_break = false;
alignment = HorizontalAlignment.Left;
if (string_format == null) {
}
}
- // Find the tag on a line based on the character position
+ /// <summary> Find the tag on a line based on the character position</summary>
internal LineTag FindTag(int pos) {
LineTag tag;
}
- //
- // Go through all tags on a line and recalculate all size-related values
- // returns true if lineheight changed
- //
- internal bool RecalculateLine(Graphics g) {
+ /// <summary>
+ /// Go through all tags on a line and recalculate all size-related values;
+ /// returns true if lineheight changed
+ /// </summary>
+ internal bool RecalculateLine(Graphics g, Document doc) {
LineTag tag;
int pos;
int len;
SizeF size;
float w;
int prev_height;
+ bool retval;
+ bool wrapped;
+ Line line;
+ int wrap_pos;
+ float wrap_width;
pos = 0;
len = this.text.Length;
tag.width = 0;
widths[0] = 0;
this.recalc = false;
+ retval = false;
+ wrapped = false;
+
+ wrap_pos = 0;
+ wrap_width = 0;
while (pos < len) {
size = g.MeasureString(this.text.ToString(pos, 1), tag.font, 10000, string_format);
w = size.Width;
- tag.width += w;
+ if (Char.IsWhiteSpace(text[pos])) {
+ wrap_pos = pos + 1;
+ wrap_width = tag.width + w;
+ }
- pos++;
+ if (doc.wrap) {
+ if ((widths[pos] + w) + 27 > doc.viewport_width) {
+ pos = wrap_pos;
+ tag.width = wrap_width;
+ doc.Split(this, tag, pos, true);
+ len = this.text.Length;
+ retval = true;
+ wrapped = true;
+ }
+ }
- widths[pos] = widths[pos-1] + w;
+ // Contract all soft lines that follow back into our line
+ if (!wrapped) {
+ tag.width += w;
+
+ pos++;
+
+ widths[pos] = widths[pos-1] + w;
+
+ if (pos == len) {
+ line = doc.GetLine(this.line_no + 1);
+ if ((line != null) && (line.soft_break)) {
+ // Pull the previous line back into this one
+ doc.Combine(this.line_no, this.line_no + 1);
+ len = this.text.Length;
+ retval = true;
+ }
+ }
+ }
if (pos == (tag.start-1 + tag.length)) {
// We just found the end of our current tag
if (tag != null) {
tag.width = 0;
tag.shift = 0;
+ wrap_pos = pos;
+ wrap_width = tag.width;
}
}
}
}
if (prev_height != this.height) {
- return true;
+ retval = true;
}
- return false;
+ return retval;
}
#endregion // Internal Methods
internal int viewport_x;
internal int viewport_y; // The visible area of the document
+ internal int viewport_width;
+ internal int viewport_height;
internal int document_x; // Width of the document
internal int document_y; // Height of the document
}
}
+ internal Point Caret {
+ get {
+ return new Point((int)caret.tag.line.widths[caret.pos] + caret.line.align_shift, caret.line.Y + caret.tag.shift);
+ }
+ }
+
internal LineTag CaretTag {
get {
return caret.tag;
}
}
+ internal int ViewPortWidth {
+ get {
+ return viewport_width;
+ }
+
+ set {
+ viewport_width = value;
+ }
+ }
+
+ internal int ViewPortHeight {
+ get {
+ return viewport_height;
+ }
+
+ set {
+ viewport_height = value;
+ }
+ }
+
+
internal int Width {
get {
return this.document_x;
}
}
+ internal bool Wrap {
+ get {
+ return wrap;
+ }
+
+ set {
+ wrap = value;
+ // FIXME - force recalc/redisplay
+ }
+ }
+
#endregion // Internal Properties
#region Private Methods
// Lineheight changed, invalidate the rest of the document
if ((line.Y - viewport_y) >=0 ) {
// We formatted something that's in view, only draw parts of the screen
- owner.Invalidate(new Rectangle(0, line.Y - viewport_y, owner.Width, owner.Height - line.Y - viewport_y));
+ owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
} else {
// The tag was above the visible area, draw everything
owner.Invalidate();
}
} else {
- owner.Invalidate(new Rectangle((int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, (int)owner.Width, line.height));
+ owner.Invalidate(new Rectangle((int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, viewport_width, line.height));
}
}
// Lineheight changed, invalidate the rest of the document
if ((line.Y - viewport_y) >=0 ) {
// We formatted something that's in view, only draw parts of the screen
- owner.Invalidate(new Rectangle(0, line.Y - viewport_y, owner.Width, owner.Height - line.Y - viewport_y));
+ owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
} else {
// The tag was above the visible area, draw everything
owner.Invalidate();
selection_end.line = this.document;
selection_end.pos = 0;
- viewport_x = -2;
- viewport_y = -2;
+ viewport_x = 0;
+ viewport_y = 0;
document_x = 0;
document_y = 0;
XplatUI.DestroyCaret(owner.Handle);
XplatUI.CreateCaret(owner.Handle, 2, caret.height);
XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
+
+ if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
}
internal void PositionCaret(int x, int y) {
XplatUI.DestroyCaret(owner.Handle);
XplatUI.CreateCaret(owner.Handle, 2, caret.height);
XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
+
+ if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
}
internal void CaretHasFocus() {
XplatUI.CreateCaret(owner.Handle, 2, caret.height);
XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
XplatUI.CaretVisible(owner.Handle, true);
+
+ if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
}
internal void UpdateCaret() {
}
XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
XplatUI.CaretVisible(owner.Handle, true);
+
+ if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
}
internal void DisplayCaret() {
line = GetLine(LineNo);
tag = LineTag.FindTag(line, pos);
- Split(line, tag, pos);
+ Split(line, tag, pos, false);
}
internal void Split(Line line, int pos) {
LineTag tag;
tag = LineTag.FindTag(line, pos);
- Split(line, tag, pos);
+ Split(line, tag, pos, false);
}
- internal void Split(Line line, LineTag tag, int pos) {
+ internal void Split(Line line, LineTag tag, int pos, bool soft) {
LineTag new_tag;
Line new_line;
+ bool move_caret;
+
+ move_caret = false;
+
+ // Adjust selection and cursors
+ if (soft && (caret.line == line) && (caret.pos >= pos)) {
+ move_caret = true;
+ }
// cover the easy case first
if (pos == line.text.Length) {
Add(line.line_no + 1, "", line.alignment, tag.font, tag.color);
+ if (soft) {
+ if (move_caret) {
+ caret.line = GetLine(line.line_no + 1);
+ caret.line.soft_break = true;
+ caret.tag = selection_start.line.tags;
+ caret.pos = 0;
+ } else {
+ GetLine(line.line_no + 1).soft_break = true;
+ }
+ }
return;
}
}
}
+
+ if (soft) {
+ if (move_caret) {
+ caret.line = new_line;
+ caret.pos = caret.pos - pos;
+ caret.tag = caret.line.FindTag(caret.pos);
+ }
+ new_line.soft_break = true;
+ }
+
line.text.Remove(pos, line.text.Length - pos);
}
line1.text = line3.text;
line1.widths = line3.widths;
line1.Y = line3.Y;
+ line1.soft_break = line3.soft_break;
tag = line1.tags;
while (tag != null) {
// Three invalidates:
// First line from start
- owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, owner.Width, l1.height));
+ owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
// lines inbetween
if ((l1.line_no + 1) < l2.line_no) {
int y;
y = GetLine(l1.line_no + 1).Y;
- owner.Invalidate(new Rectangle(0 - viewport_x, y - viewport_y, owner.Width, GetLine(l2.line_no - 1).Y - y - viewport_y));
+ owner.Invalidate(new Rectangle(0 - viewport_x, y - viewport_y, viewport_width, GetLine(l2.line_no - 1).Y - y - viewport_y));
}
// Last line to end
- owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, owner.Width, l1.height));
+ owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
}
}
- // Give it a Line number and it returns the Line object at with that line number
+ /// <summary>Give it a Line number and it returns the Line object at with that line number</summary>
internal Line GetLine(int LineNo) {
Line line = document;
return null;
}
- // Give it a Y pixel coordinate and it returns the Line covering that Y coordinate
- ///
+ /// <summary>Retrieve the previous tag; walks line boundaries</summary>\r
+ internal LineTag PreviousTag(LineTag tag) {\r
+ Line l; \r
+\r
+ if (tag.previous != null) {\r
+ return tag.previous;\r
+ }\r
+\r
+ // Next line \r
+ if (tag.line.line_no == 1) {\r
+ return null;\r
+ }\r
+\r
+ l = GetLine(tag.line.line_no - 1);\r
+ if (l != null) {\r
+ LineTag t;\r
+\r
+ t = l.tags;\r
+ while (t.next != null) {\r
+ t = t.next;\r
+ }\r
+ return t;\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /// <summary>Retrieve the next tag; walks line boundaries</summary>\r
+ internal LineTag NextTag(LineTag tag) {\r
+ Line l;\r
+\r
+ if (tag.next != null) {\r
+ return tag.next;\r
+ }\r
+\r
+ // Next line\r
+ l = GetLine(tag.line.line_no + 1);\r
+ if (l != null) {\r
+ return l.tags;\r
+ }\r
+\r
+ return null;\r
+ }\r
+
+ /// <summary>Give it a Y pixel coordinate and it returns the Line covering that Y coordinate</summary>
internal Line GetLineByPixel(int y, bool exact) {
Line line = document;
Line last = null;
}
}
+ internal void FormatText(Line start_line, int start_pos, Line end_line, int end_pos, Font font, Brush color) {\r
+ Line l;\r
+\r
+ // First, format the first line\r
+ LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos, font, color);\r
+\r
+ // Format last line\r
+ if (end_line != start_line) {\r
+ LineTag.FormatText(end_line, 0, end_pos, font, color);\r
+ }\r
+\r
+ // Now all the lines inbetween\r
+ for (int i = start_line.line_no + 1; i < end_line.line_no - 1; i++) {\r
+ l = GetLine(i);\r
+ LineTag.FormatText(l, 0, l.text.Length, font, color);\r
+ }\r
+ }\r
+
internal void RecalculateAlignments() {
Line line;
int line_no;
if (line.alignment != HorizontalAlignment.Left) {
if (line.alignment == HorizontalAlignment.Center) {
- line.align_shift = (document_x - (int)line.widths[line.text.Length]) / 2;
+ line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
} else {
- line.align_shift = document_x - (int)line.widths[line.text.Length];
+ line.align_shift = viewport_width - (int)line.widths[line.text.Length];
}
}
return;
}
- // Calculate formatting for the whole document
+ /// <summary>Calculate formatting for the whole document</summary>
internal bool RecalculateDocument(Graphics g) {
return RecalculateDocument(g, 1, this.lines, false);
}
- // Calculate formatting starting at a certain line
+ /// <summary>Calculate formatting starting at a certain line</summary>
internal bool RecalculateDocument(Graphics g, int start) {
return RecalculateDocument(g, start, this.lines, false);
}
- // Calculate formatting within two given line numbers
+ /// <summary>Calculate formatting within two given line numbers</summary>
internal bool RecalculateDocument(Graphics g, int start, int end) {
return RecalculateDocument(g, start, end, false);
}
- // With optimize on, returns true if line heights changed
+ /// <summary>With optimize on, returns true if line heights changed</summary>
internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
Line line;
int line_no;
bool alignment_recalc;
changed = false;
- alignment_recalc = false;
+ alignment_recalc = true;
while (line_no <= end) {
line = GetLine(line_no++);
line.Y = Y;
if (line.recalc) {
- if (line.RecalculateLine(g)) {
+ if (line.RecalculateLine(g, this)) {
changed = true;
// If the height changed, all subsequent lines change
end = this.lines;
// Calculate alignment
if (line.alignment != HorizontalAlignment.Left) {
if (line.alignment == HorizontalAlignment.Center) {
- line.align_shift = (document_x - (int)line.widths[line.text.Length]) / 2;
+ line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
} else {
- line.align_shift = document_x - (int)line.widths[line.text.Length];
+ line.align_shift = viewport_width - (int)line.widths[line.text.Length];
}
}
}
Y += line.height;
+
+ if (line_no > lines) {
+ break;
+ }
}
if (alignment_recalc) {
RecalculateAlignments();
}
+ line = GetLine(lines);
+ document_y = line.Y + line.height;
+
return changed;
} else {
- while (line_no <= end) {
+ int shift;
+
+ shift = 0;
+
+ while (line_no <= (end + shift)) {
line = GetLine(line_no++);
line.Y = Y;
- line.RecalculateLine(g);
+
+ shift = this.lines;
+ line.RecalculateLine(g, this);
+ if (this.lines > shift) {
+ shift = this.lines - shift;
+ } else {
+ shift = 0;
+ }
+
if (line.widths[line.text.Length] > this.document_x) {
this.document_x = (int)line.widths[line.text.Length];
}
// Calculate alignment
if (line.alignment != HorizontalAlignment.Left) {
if (line.alignment == HorizontalAlignment.Center) {
- line.align_shift = (document_x - (int)line.widths[line.text.Length]) / 2;
+ line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
} else {
- line.align_shift = document_x - (int)line.widths[line.text.Length];
+ line.align_shift = viewport_width - (int)line.widths[line.text.Length];
}
}
Y += line.height;
+
+ if (line_no > lines) {
+ break;
+ }
}
RecalculateAlignments();
+
+ line = GetLine(lines);
+ document_y = line.Y + line.height;
+
return true;
}
}
}
#endregion // Internal Methods
+ #region Events
+ internal event EventHandler CaretMoved;
+ #endregion // Events
+
#region Administrative
public IEnumerator GetEnumerator() {
// FIXME
public override string ToString() {
return "document " + this.document_id;
}
-
#endregion // Administrative
}
internal int ascent; // Ascent of the font for this tag
internal int shift; // Shift down for this tag, to stay on baseline
- internal int soft_break; // Tag is 'broken soft' and continues in the next line
-
// Administrative
internal Line line; // The line we're on
internal LineTag next; // Next tag on the same line
#endregion // Constructors
#region Internal Methods
- //
- // Applies 'font' to characters starting at 'start' for 'length' chars
- // Removes any previous tags overlapping the same area
- // returns true if lineheight has changed
- //
+ /// <summary>Applies 'font' to characters starting at 'start' for 'length' chars;
+ /// Removes any previous tags overlapping the same area;
+ /// returns true if lineheight has changed</summary>
internal static bool FormatText(Line line, int start, int length, Font font, Brush color) {
LineTag tag;
LineTag start_tag;
}
- //
- // Finds the tag that describes the character at position 'pos' on 'line'
- //
+ /// <summary>Finds the tag that describes the character at position 'pos' on 'line'</summary>
internal static LineTag FindTag(Line line, int pos) {
LineTag tag = line.tags;
return null;
}
- //
- // Combines 'this' tag with 'other' tag.
- //
+ /// <summary>Combines 'this' tag with 'other' tag</summary>
internal bool Combine(LineTag other) {
if (!this.Equals(other)) {
return false;
}
- //
- // Remove 'this' tag ; to be called when formatting is to be removed
- //
+ /// <summary>Remove 'this' tag ; to be called when formatting is to be removed</summary>
internal bool Remove() {
if ((this.start == 1) && (this.next == null)) {
// We cannot remove the only tag
}
- //
- // Checks if 'this' tag describes the same formatting options as 'obj'
- //
+ /// <summary>Checks if 'this' tag describes the same formatting options as 'obj'</summary>
public override bool Equals(object obj) {
LineTag other;