aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Widgets/RichTextBox.cs
diff options
context:
space:
mode:
authorMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
committerMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
commitd40fed5ce2bc806a91245adb18039634eac13ed0 (patch)
treef1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Widgets/RichTextBox.cs
parentf1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff)
downloadshiftos-c-_theultimatehacker-d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz
shiftos-c-_theultimatehacker-d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2
shiftos-c-_theultimatehacker-d40fed5ce2bc806a91245adb18039634eac13ed0.zip
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Widgets/RichTextBox.cs')
-rw-r--r--source/ShiftUI/Widgets/RichTextBox.cs2089
1 files changed, 2089 insertions, 0 deletions
diff --git a/source/ShiftUI/Widgets/RichTextBox.cs b/source/ShiftUI/Widgets/RichTextBox.cs
new file mode 100644
index 0000000..c1d2325
--- /dev/null
+++ b/source/ShiftUI/Widgets/RichTextBox.cs
@@ -0,0 +1,2089 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// 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) 2005-2006 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Peter Bartok <[email protected]>
+//
+//
+
+// #define DEBUG
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using RTF=ShiftUI.RTF;
+
+namespace ShiftUI {
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [Docking (DockingBehavior.Ask)]
+ [ComVisible (true)]
+ [Designer ("ShiftUI.Design.RichTextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class RichTextBox : TextBoxBase {
+ #region Local Variables
+ internal bool auto_word_select;
+ internal int bullet_indent;
+ internal bool detect_urls;
+ private bool reuse_line; // Sometimes we are loading text with already available lines
+ internal int margin_right;
+ internal float zoom;
+ private StringBuilder rtf_line;
+
+ private RtfSectionStyle rtf_style; // Replaces individual style
+ // properties so we can revert
+ private Stack rtf_section_stack;
+
+ private RTF.TextMap rtf_text_map;
+ private int rtf_skip_count;
+ private int rtf_cursor_x;
+ private int rtf_cursor_y;
+ private int rtf_chars;
+
+ private bool enable_auto_drag_drop;
+ private RichTextBoxLanguageOptions language_option;
+ private bool rich_text_shortcuts_enabled;
+ private Color selection_back_color;
+ #endregion // Local Variables
+
+ #region Public Constructors
+ public RichTextBox() {
+ accepts_return = true;
+ auto_size = false;
+ auto_word_select = false;
+ bullet_indent = 0;
+ base.MaxLength = Int32.MaxValue;
+ margin_right = 0;
+ zoom = 1;
+ base.Multiline = true;
+ document.CRLFSize = 1;
+ shortcuts_enabled = true;
+ base.EnableLinks = true;
+ richtext = true;
+
+ rtf_style = new RtfSectionStyle ();
+ rtf_section_stack = null;
+
+ scrollbars = RichTextBoxScrollBars.Both;
+ alignment = HorizontalAlignment.Left;
+ LostFocus += new EventHandler(RichTextBox_LostFocus);
+ GotFocus += new EventHandler(RichTextBox_GotFocus);
+ BackColor = ThemeEngine.Current.ColorWindow;
+ backcolor_set = false;
+ language_option = RichTextBoxLanguageOptions.AutoFontSizeAdjust;
+ rich_text_shortcuts_enabled = true;
+ selection_back_color = DefaultBackColor;
+ ForeColor = ThemeEngine.Current.ColorWindowText;
+
+ base.HScrolled += new EventHandler(RichTextBox_HScrolled);
+ base.VScrolled += new EventHandler(RichTextBox_VScrolled);
+
+ SetStyle (Widgetstyles.StandardDoubleClick, false);
+ }
+ #endregion // Public Constructors
+
+ #region Private & Internal Methods
+
+ internal override void HandleLinkClicked (LinkRectangle link)
+ {
+ OnLinkClicked (new LinkClickedEventArgs (link.LinkTag.LinkText));
+ }
+
+ internal override Color ChangeBackColor (Color backColor)
+ {
+ if (backColor == Color.Empty) {
+ backcolor_set = false;
+ if (!ReadOnly) {
+ backColor = SystemColors.Window;
+ }
+ }
+ return backColor;
+ }
+
+ internal override void RaiseSelectionChanged()
+ {
+ OnSelectionChanged (EventArgs.Empty);
+ }
+
+ private void RichTextBox_LostFocus(object sender, EventArgs e) {
+ Invalidate();
+ }
+
+ private void RichTextBox_GotFocus(object sender, EventArgs e) {
+ Invalidate();
+ }
+ #endregion // Private & Internal Methods
+
+ #region Public Instance Properties
+ [Browsable (false)]
+ public override bool AllowDrop {
+ get {
+ return base.AllowDrop;
+ }
+
+ set {
+ base.AllowDrop = value;
+ }
+ }
+
+ [DefaultValue(false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override bool AutoSize {
+ get {
+ return auto_size;
+ }
+
+ set {
+ base.AutoSize = value;
+ }
+ }
+
+ [MonoTODO ("Value not respected, always true")]
+ [DefaultValue(false)]
+ public bool AutoWordSelection {
+ get { return auto_word_select; }
+ set { auto_word_select = value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override System.Drawing.Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set { base.BackgroundImage = value; }
+ }
+
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [DefaultValue(0)]
+ [Localizable(true)]
+ public int BulletIndent {
+ get {
+ return bullet_indent;
+ }
+
+ set {
+ bullet_indent = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public bool CanRedo {
+ get {
+ return document.undo.CanRedo;
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool DetectUrls {
+ get { return base.EnableLinks; }
+ set { base.EnableLinks = value; }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [DefaultValue (false)]
+ public bool EnableAutoDragDrop {
+ get { return enable_auto_drag_drop; }
+ set { enable_auto_drag_drop = value; }
+ }
+
+ public override Font Font {
+ get {
+ return base.Font;
+ }
+
+ set {
+ if (font != value) {
+ Line start;
+ Line end;
+
+ if (auto_size) {
+ if (PreferredHeight != Height) {
+ Height = PreferredHeight;
+ }
+ }
+
+ base.Font = value;
+
+ // Font changes always set the whole doc to that font
+ start = document.GetLine(1);
+ end = document.GetLine(document.Lines);
+ document.FormatText(start, 1, end, end.text.Length + 1, base.Font, Color.Empty, Color.Empty, FormatSpecified.Font);
+ }
+ }
+ }
+
+ public override Color ForeColor {
+ get {
+ return base.ForeColor;
+ }
+
+ set {
+ base.ForeColor = value;
+ }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public RichTextBoxLanguageOptions LanguageOption {
+ get { return language_option; }
+ set { language_option = value; }
+ }
+
+ [DefaultValue(Int32.MaxValue)]
+ public override int MaxLength {
+ get { return base.MaxLength; }
+ set { base.MaxLength = value; }
+ }
+
+ [DefaultValue(true)]
+ public override bool Multiline {
+ get {
+ return base.Multiline;
+ }
+
+ set {
+ base.Multiline = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string RedoActionName {
+ get {
+ return document.undo.RedoActionName;
+ }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DefaultValue (true)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public bool RichTextShortcutsEnabled {
+ get { return rich_text_shortcuts_enabled; }
+ set { rich_text_shortcuts_enabled = value; }
+ }
+
+ [DefaultValue(0)]
+ [Localizable(true)]
+ [MonoTODO ("Stub, does nothing")]
+ [MonoInternalNote ("Teach TextControl.RecalculateLine to consider the right margin as well")]
+ public int RightMargin {
+ get {
+ return margin_right;
+ }
+
+ set {
+ margin_right = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [RefreshProperties (RefreshProperties.All)]
+ public string Rtf {
+ get {
+ Line start_line;
+ Line end_line;
+
+ start_line = document.GetLine(1);
+ end_line = document.GetLine(document.Lines);
+ return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
+ }
+
+ set {
+ MemoryStream data;
+
+ document.Empty();
+ data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
+
+ InsertRTFFromStream(data, 0, 1);
+
+ data.Close();
+
+ Invalidate();
+ }
+ }
+
+ [DefaultValue(RichTextBoxScrollBars.Both)]
+ [Localizable(true)]
+ public RichTextBoxScrollBars ScrollBars {
+ get {
+ return scrollbars;
+ }
+
+ set {
+ if (!Enum.IsDefined (typeof (RichTextBoxScrollBars), value))
+ throw new InvalidEnumArgumentException ("value", (int) value,
+ typeof (RichTextBoxScrollBars));
+
+ if (value != scrollbars) {
+ scrollbars = value;
+ CalculateDocument ();
+ }
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue("")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string SelectedRtf {
+ get {
+ return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
+ }
+
+ set {
+ MemoryStream data;
+ int x;
+ int y;
+ int sel_start;
+ int chars;
+ Line line;
+ LineTag tag;
+
+ if (document.selection_visible) {
+ document.ReplaceSelection("", false);
+ }
+
+ sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
+
+ data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
+ int cursor_x = document.selection_start.pos;
+ int cursor_y = document.selection_start.line.line_no;
+
+ // The RFT parser by default, when finds our x cursor in 0, it thinks if needs to
+ // add a new line; but in *this* scenario the line is already created, so force it to reuse it.
+ // Hackish, but works without touching the heart of the buggy parser.
+ if (cursor_x == 0)
+ reuse_line = true;
+
+ InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
+ data.Close();
+
+ int nl_length = document.LineEndingLength (XplatUI.RunningOnUnix ? LineEnding.Rich : LineEnding.Hard);
+ document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * nl_length,
+ out line, out tag, out sel_start);
+ if (sel_start >= line.text.Length)
+ sel_start = line.text.Length -1;
+
+ document.SetSelection(line, sel_start);
+ document.PositionCaret(line, sel_start);
+ document.DisplayCaret();
+ ScrollToCaret();
+ OnTextChanged(EventArgs.Empty);
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue("")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public override string SelectedText {
+ get {
+ return base.SelectedText;
+ }
+
+ set {
+ // TextBox/TextBoxBase don't set Modified in this same property
+ Modified = true;
+ base.SelectedText = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(HorizontalAlignment.Left)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public HorizontalAlignment SelectionAlignment {
+ get {
+ HorizontalAlignment align;
+ Line start;
+ Line end;
+ Line line;
+
+ start = document.ParagraphStart(document.selection_start.line);
+ align = start.alignment;
+
+ end = document.ParagraphEnd(document.selection_end.line);
+
+ line = start;
+
+ while (true) {
+ if (line.alignment != align) {
+ return HorizontalAlignment.Left;
+ }
+
+ if (line == end) {
+ break;
+ }
+ line = document.GetLine(line.line_no + 1);
+ }
+
+ return align;
+ }
+
+ set {
+ Line start;
+ Line end;
+ Line line;
+
+ start = document.ParagraphStart(document.selection_start.line);
+
+ end = document.ParagraphEnd(document.selection_end.line);
+
+ line = start;
+
+ while (true) {
+ line.alignment = value;
+
+ if (line == end) {
+ break;
+ }
+ line = document.GetLine(line.line_no + 1);
+ }
+ this.CalculateDocument();
+ }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Color SelectionBackColor {
+ get { return selection_back_color; }
+ set { selection_back_color = value; }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public bool SelectionBullet {
+ get {
+ return false;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionCharOffset {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Color SelectionColor {
+ get {
+ Color color;
+ LineTag start;
+ LineTag end;
+ LineTag tag;
+
+ if (selection_length > 0) {
+ start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
+ end = document.selection_start.line.FindTag (document.selection_end.pos);
+ } else {
+ start = document.selection_start.line.FindTag (document.selection_start.pos);
+ end = start;
+ }
+
+ color = start.Color;
+
+ tag = start;
+ while (tag != null) {
+
+ if (!color.Equals (tag.Color))
+ return Color.Empty;
+
+ if (tag == end)
+ break;
+
+ tag = document.NextTag (tag);
+ }
+
+ return color;
+ }
+
+ set {
+ if (value == Color.Empty)
+ value = DefaultForeColor;
+
+ int sel_start;
+ int sel_end;
+
+ sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
+ sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
+
+ document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
+ document.selection_end.line, document.selection_end.pos + 1, null,
+ value, Color.Empty, FormatSpecified.Color);
+
+ document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
+ document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
+
+ document.UpdateView(document.selection_start.line, 0);
+
+ //Re-Align the caret in case its changed size or position
+ //probably not necessary here
+ document.AlignCaret(false);
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Font SelectionFont {
+ get {
+ Font font;
+ LineTag start;
+ LineTag end;
+ LineTag tag;
+
+ if (selection_length > 0) {
+ start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
+ end = document.selection_start.line.FindTag (document.selection_end.pos);
+ } else {
+ start = document.selection_start.line.FindTag (document.selection_start.pos);
+ end = start;
+ }
+
+ font = start.Font;
+
+ if (selection_length > 1) {
+ tag = start;
+ while (tag != null) {
+
+ if (!font.Equals(tag.Font))
+ return null;
+
+ if (tag == end)
+ break;
+
+ tag = document.NextTag (tag);
+ }
+ }
+
+ return font;
+ }
+
+ set {
+ int sel_start;
+ int sel_end;
+
+ sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
+ sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
+
+ document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
+ document.selection_end.line, document.selection_end.pos + 1, value,
+ Color.Empty, Color.Empty, FormatSpecified.Font);
+
+ document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
+ document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
+
+ document.UpdateView(document.selection_start.line, 0);
+ //Re-Align the caret in case its changed size or position
+ Document.AlignCaret (false);
+
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionHangingIndent {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionIndent {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public override int SelectionLength {
+ get {
+ return base.SelectionLength;
+ }
+
+ set {
+ base.SelectionLength = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public bool SelectionProtected {
+ get {
+ return false;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionRightIndent {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int[] SelectionTabs {
+ get {
+ return new int[0];
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public RichTextBoxSelectionTypes SelectionType {
+ get {
+ if (document.selection_start == document.selection_end) {
+ return RichTextBoxSelectionTypes.Empty;
+ }
+
+ // Lazy, but works
+ if (SelectedText.Length > 1) {
+ return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
+ }
+
+ return RichTextBoxSelectionTypes.Text;
+ }
+ }
+
+ [DefaultValue(false)]
+ [MonoTODO ("Stub, does nothing")]
+ public bool ShowSelectionMargin {
+ get {
+ return false;
+ }
+
+ set {
+ }
+ }
+
+ [Localizable(true)]
+ [RefreshProperties (RefreshProperties.All)]
+ public override string Text {
+ get {
+ return base.Text;
+ }
+
+ set {
+ base.Text = value;
+ }
+ }
+
+ [Browsable(false)]
+ public override int TextLength {
+ get {
+ return base.TextLength;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string UndoActionName {
+ get {
+ return document.undo.UndoActionName;
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue(1)]
+ public float ZoomFactor {
+ get {
+ return zoom;
+ }
+
+ set {
+ zoom = value;
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ return new Size(100, 96);
+ }
+ }
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public bool CanPaste(DataFormats.Format clipFormat) {
+ if ((clipFormat.Name == DataFormats.Rtf) ||
+ (clipFormat.Name == DataFormats.Text) ||
+ (clipFormat.Name == DataFormats.UnicodeText)) {
+ return true;
+ }
+ return false;
+ }
+
+ public int Find(char[] characterSet) {
+ return Find(characterSet, -1, -1);
+ }
+
+ public int Find(char[] characterSet, int start) {
+ return Find(characterSet, start, -1);
+ }
+
+ public int Find(char[] characterSet, int start, int end) {
+ Document.Marker start_mark;
+ Document.Marker end_mark;
+ Document.Marker result;
+
+ if (start == -1) {
+ document.GetMarker(out start_mark, true);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ start_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(start, out line, out tag, out pos);
+ start_mark.line = line;
+ start_mark.tag = tag;
+ start_mark.pos = pos;
+ }
+
+ if (end == -1) {
+ document.GetMarker(out end_mark, false);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ end_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(end, out line, out tag, out pos);
+ end_mark.line = line;
+ end_mark.tag = tag;
+ end_mark.pos = pos;
+ }
+
+ if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
+ return document.LineTagToCharIndex(result.line, result.pos);
+ }
+
+ return -1;
+ }
+
+ public int Find(string str) {
+ return Find(str, -1, -1, RichTextBoxFinds.None);
+ }
+
+ public int Find(string str, int start, int end, RichTextBoxFinds options) {
+ Document.Marker start_mark;
+ Document.Marker end_mark;
+ Document.Marker result;
+
+ if (start == -1) {
+ document.GetMarker(out start_mark, true);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ start_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(start, out line, out tag, out pos);
+
+ start_mark.line = line;
+ start_mark.tag = tag;
+ start_mark.pos = pos;
+ }
+
+ if (end == -1) {
+ document.GetMarker(out end_mark, false);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ end_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(end, out line, out tag, out pos);
+
+ end_mark.line = line;
+ end_mark.tag = tag;
+ end_mark.pos = pos;
+ }
+
+ if (document.Find(str, start_mark, end_mark, out result, options)) {
+ return document.LineTagToCharIndex(result.line, result.pos);
+ }
+
+ return -1;
+ }
+
+ public int Find(string str, int start, RichTextBoxFinds options) {
+ return Find(str, start, -1, options);
+ }
+
+ public int Find(string str, RichTextBoxFinds options) {
+ return Find(str, -1, -1, options);
+ }
+
+
+ internal override char GetCharFromPositionInternal (Point p)
+ {
+ LineTag tag;
+ int pos;
+
+ PointToTagPos (p, out tag, out pos);
+
+ if (pos >= tag.Line.text.Length)
+ return '\n';
+
+ return tag.Line.text[pos];
+ }
+
+ public override int GetCharIndexFromPosition(Point pt) {
+ LineTag tag;
+ int pos;
+
+ PointToTagPos(pt, out tag, out pos);
+
+ return document.LineTagToCharIndex(tag.Line, pos);
+ }
+
+ public override int GetLineFromCharIndex(int index) {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ document.CharIndexToLineTag(index, out line, out tag, out pos);
+
+ return line.LineNo - 1;
+ }
+
+ public override Point GetPositionFromCharIndex(int index) {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ document.CharIndexToLineTag(index, out line, out tag, out pos);
+ return new Point(line.X + (int)line.widths[pos] + document.OffsetX - document.ViewPortX,
+ line.Y + document.OffsetY - document.ViewPortY);
+ }
+
+ public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
+ document.Empty();
+
+
+ // FIXME - ignoring unicode
+ if (fileType == RichTextBoxStreamType.PlainText) {
+ StringBuilder sb;
+ char[] buffer;
+
+ try {
+ sb = new StringBuilder ((int) data.Length);
+ buffer = new char [1024];
+ } catch {
+ throw new IOException("Not enough memory to load document");
+ }
+
+ StreamReader sr = new StreamReader (data, Encoding.Default, true);
+ int charsRead = sr.Read (buffer, 0, buffer.Length);
+ while (charsRead > 0) {
+ sb.Append (buffer, 0, charsRead);
+ charsRead = sr.Read (buffer, 0, buffer.Length);
+ }
+
+ // Remove the EOF converted to an extra EOL by the StreamReader
+ if (sb.Length > 0 && sb [sb.Length - 1] == '\n')
+ sb.Remove (sb.Length - 1, 1);
+
+ base.Text = sb.ToString();
+ return;
+ }
+
+ InsertRTFFromStream(data, 0, 1);
+
+ document.PositionCaret (document.GetLine (1), 0);
+ document.SetSelectionToCaret (true);
+ ScrollToCaret ();
+ }
+
+ public void LoadFile(string path) {
+ LoadFile (path, RichTextBoxStreamType.RichText);
+ }
+
+ public void LoadFile(string path, RichTextBoxStreamType fileType) {
+ FileStream data;
+
+ data = null;
+
+
+ try {
+ data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
+
+ LoadFile(data, fileType);
+ }
+#if !DEBUG
+ catch (Exception ex) {
+ throw new IOException("Could not open file " + path, ex);
+ }
+#endif
+ finally {
+ if (data != null) {
+ data.Close();
+ }
+ }
+ }
+
+ public void Paste(DataFormats.Format clipFormat) {
+ base.Paste(Clipboard.GetDataObject(), clipFormat, false);
+ }
+
+ public void Redo()
+ {
+ if (document.undo.Redo ())
+ OnTextChanged (EventArgs.Empty);
+ }
+
+ public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
+ Encoding encoding;
+ int i;
+ Byte[] bytes;
+
+
+ if (fileType == RichTextBoxStreamType.UnicodePlainText) {
+ encoding = Encoding.Unicode;
+ } else {
+ encoding = Encoding.ASCII;
+ }
+
+ switch(fileType) {
+ case RichTextBoxStreamType.PlainText:
+ case RichTextBoxStreamType.TextTextOleObjs:
+ case RichTextBoxStreamType.UnicodePlainText: {
+ if (!Multiline) {
+ bytes = encoding.GetBytes(document.Root.text.ToString());
+ data.Write(bytes, 0, bytes.Length);
+ return;
+ }
+
+ for (i = 1; i < document.Lines; i++) {
+ // Normalize the new lines to the system ones
+ string line_text = document.GetLine (i).TextWithoutEnding () + Environment.NewLine;
+ bytes = encoding.GetBytes(line_text);
+ data.Write(bytes, 0, bytes.Length);
+ }
+ bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
+ data.Write(bytes, 0, bytes.Length);
+ return;
+ }
+ }
+
+ // If we're here we're saving RTF
+ Line start_line;
+ Line end_line;
+ StringBuilder rtf;
+ int current;
+ int total;
+
+ start_line = document.GetLine(1);
+ end_line = document.GetLine(document.Lines);
+ rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
+ total = rtf.Length;
+ bytes = new Byte[4096];
+
+ // Let's chunk it so we don't use up all memory...
+ for (i = 0; i < total; i += 1024) {
+ if ((i + 1024) < total) {
+ current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
+ } else {
+ current = total - i;
+ current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
+ }
+ data.Write(bytes, 0, current);
+ }
+ }
+
+ public void SaveFile(string path) {
+ if (path.EndsWith(".rtf")) {
+ SaveFile(path, RichTextBoxStreamType.RichText);
+ } else {
+ SaveFile(path, RichTextBoxStreamType.PlainText);
+ }
+ }
+
+ public void SaveFile(string path, RichTextBoxStreamType fileType) {
+ FileStream data;
+
+ data = null;
+
+// try {
+ data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
+ SaveFile(data, fileType);
+// }
+
+// catch {
+// throw new IOException("Could not write document to file " + path);
+// }
+
+// finally {
+ if (data != null) {
+ data.Close();
+ }
+// }
+ }
+
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public new void DrawToBitmap (Bitmap bitmap, Rectangle targetBounds)
+ {
+ using (Graphics dc = Graphics.FromImage (bitmap))
+ Draw (dc, targetBounds);
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected virtual object CreateRichEditOleCallback()
+ {
+ throw new NotImplementedException ();
+ }
+
+ protected override void OnBackColorChanged (EventArgs e)
+ {
+ base.OnBackColorChanged (e);
+ }
+
+ protected virtual void OnContentsResized (ContentsResizedEventArgs e)
+ {
+ ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnContextMenuChanged (EventArgs e)
+ {
+ base.OnContextMenuChanged (e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected virtual void OnHScroll(EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [HScrollEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ [MonoTODO ("Stub, never called")]
+ protected virtual void OnImeChange(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
+ LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnProtected(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnRightToLeftChanged(EventArgs e) {
+ base.OnRightToLeftChanged (e);
+ }
+
+ protected virtual void OnSelectionChanged(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnVScroll(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [VScrollEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void WndProc(ref Message m) {
+ base.WndProc (ref m);
+ }
+
+ protected override bool ProcessCmdKey (ref Message m, Keys keyData)
+ {
+ return base.ProcessCmdKey (ref m, keyData);
+ }
+ #endregion // Protected Instance Methods
+
+ #region Events
+ static object ContentsResizedEvent = new object ();
+ static object HScrollEvent = new object ();
+ static object ImeChangeEvent = new object ();
+ static object LinkClickedEvent = new object ();
+ static object ProtectedEvent = new object ();
+ static object SelectionChangedEvent = new object ();
+ static object VScrollEvent = new object ();
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ public event ContentsResizedEventHandler ContentsResized {
+ add { Events.AddHandler (ContentsResizedEvent, value); }
+ remove { Events.RemoveHandler (ContentsResizedEvent, value); }
+ }
+
+ [Browsable(false)]
+ public new event DragEventHandler DragDrop {
+ add { base.DragDrop += value; }
+ remove { base.DragDrop -= value; }
+ }
+
+ [Browsable(false)]
+ public new event DragEventHandler DragEnter {
+ add { base.DragEnter += value; }
+ remove { base.DragEnter -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler DragLeave {
+ add { base.DragLeave += value; }
+ remove { base.DragLeave -= value; }
+ }
+
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event DragEventHandler DragOver {
+ add { base.DragOver += value; }
+ remove { base.DragOver -= value; }
+ }
+
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event GiveFeedbackEventHandler GiveFeedback {
+ add { base.GiveFeedback += value; }
+ remove { base.GiveFeedback -= value; }
+ }
+
+ public event EventHandler HScroll {
+ add { Events.AddHandler (HScrollEvent, value); }
+ remove { Events.RemoveHandler (HScrollEvent, value); }
+ }
+
+ public event EventHandler ImeChange {
+ add { Events.AddHandler (ImeChangeEvent, value); }
+ remove { Events.RemoveHandler (ImeChangeEvent, value); }
+ }
+
+ public event LinkClickedEventHandler LinkClicked {
+ add { Events.AddHandler (LinkClickedEvent, value); }
+ remove { Events.RemoveHandler (LinkClickedEvent, value); }
+ }
+
+ public event EventHandler Protected {
+ add { Events.AddHandler (ProtectedEvent, value); }
+ remove { Events.RemoveHandler (ProtectedEvent, value); }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event QueryContinueDragEventHandler QueryContinueDrag {
+ add { base.QueryContinueDrag += value; }
+ remove { base.QueryContinueDrag -= value; }
+ }
+
+ [MonoTODO ("Event never raised")]
+ public event EventHandler SelectionChanged {
+ add { Events.AddHandler (SelectionChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectionChangedEvent, value); }
+ }
+
+ public event EventHandler VScroll {
+ add { Events.AddHandler (VScrollEvent, value); }
+ remove { Events.RemoveHandler (VScrollEvent, value); }
+ }
+ #endregion // Events
+
+ #region Private Methods
+
+ internal override void SelectWord ()
+ {
+ document.ExpandSelection(CaretSelection.Word, false);
+ }
+
+ private class RtfSectionStyle : ICloneable {
+ internal Color rtf_color;
+ internal RTF.Font rtf_rtffont;
+ internal int rtf_rtffont_size;
+ internal FontStyle rtf_rtfstyle;
+ internal HorizontalAlignment rtf_rtfalign;
+ internal int rtf_par_line_left_indent;
+ internal bool rtf_visible;
+ internal int rtf_skip_width;
+
+ public object Clone ()
+ {
+ RtfSectionStyle new_style = new RtfSectionStyle ();
+
+ new_style.rtf_color = rtf_color;
+ new_style.rtf_par_line_left_indent = rtf_par_line_left_indent;
+ new_style.rtf_rtfalign = rtf_rtfalign;
+ new_style.rtf_rtffont = rtf_rtffont;
+ new_style.rtf_rtffont_size = rtf_rtffont_size;
+ new_style.rtf_rtfstyle = rtf_rtfstyle;
+ new_style.rtf_visible = rtf_visible;
+ new_style.rtf_skip_width = rtf_skip_width;
+
+ return new_style;
+ }
+ }
+
+ // To allow us to keep track of the sections and revert formatting
+ // as we go in and out of sections of the document.
+ private void HandleGroup (RTF.RTF rtf)
+ {
+ //start group - save the current formatting on to a stack
+ //end group - go back to the formatting at the current group
+ if (rtf_section_stack == null) {
+ rtf_section_stack = new Stack ();
+ }
+
+ if (rtf.Major == RTF.Major.BeginGroup) {
+ rtf_section_stack.Push (rtf_style.Clone ());
+ //spec specifies resetting unicode ignore at begin group as an attempt at error
+ //recovery.
+ rtf_skip_count = 0;
+ } else if (rtf.Major == RTF.Major.EndGroup) {
+ if (rtf_section_stack.Count > 0) {
+ FlushText (rtf, false);
+
+ rtf_style = (RtfSectionStyle) rtf_section_stack.Pop ();
+ }
+ }
+ }
+
+ [MonoInternalNote ("Add QuadJust support for justified alignment")]
+ private void HandleControl(RTF.RTF rtf) {
+ switch(rtf.Major) {
+ case RTF.Major.Unicode: {
+ switch(rtf.Minor) {
+ case RTF.Minor.UnicodeCharBytes: {
+ rtf_style.rtf_skip_width = rtf.Param;
+ break;
+ }
+
+ case RTF.Minor.UnicodeChar: {
+ FlushText (rtf, false);
+ rtf_skip_count += rtf_style.rtf_skip_width;
+ rtf_line.Append((char)rtf.Param);
+ break;
+ }
+ }
+ break;
+ }
+
+ case RTF.Major.Destination: {
+// Console.Write("[Got Destination control {0}]", rtf.Minor);
+ rtf.SkipGroup();
+ break;
+ }
+
+ case RTF.Major.PictAttr:
+ if (rtf.Picture != null && rtf.Picture.IsValid ()) {
+ Line line = document.GetLine (rtf_cursor_y);
+ document.InsertPicture (line, 0, rtf.Picture);
+ rtf_cursor_x++;
+
+ FlushText (rtf, true);
+ rtf.Picture = null;
+ }
+ break;
+
+ case RTF.Major.CharAttr: {
+ switch(rtf.Minor) {
+ case RTF.Minor.ForeColor: {
+ ShiftUI.RTF.Color color;
+
+ color = ShiftUI.RTF.Color.GetColor(rtf, rtf.Param);
+
+ if (color != null) {
+ FlushText(rtf, false);
+ if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
+ this.rtf_style.rtf_color = ForeColor;
+ } else {
+ this.rtf_style.rtf_color = Color.FromArgb(color.Red, color.Green, color.Blue);
+ }
+ FlushText (rtf, false);
+ }
+ break;
+ }
+
+ case RTF.Minor.FontSize: {
+ FlushText(rtf, false);
+ this.rtf_style.rtf_rtffont_size = rtf.Param / 2;
+ break;
+ }
+
+ case RTF.Minor.FontNum: {
+ ShiftUI.RTF.Font font;
+
+ font = ShiftUI.RTF.Font.GetFont(rtf, rtf.Param);
+ if (font != null) {
+ FlushText(rtf, false);
+ this.rtf_style.rtf_rtffont = font;
+ }
+ break;
+ }
+
+ case RTF.Minor.Plain: {
+ FlushText(rtf, false);
+ rtf_style.rtf_rtfstyle = FontStyle.Regular;
+ break;
+ }
+
+ case RTF.Minor.Bold: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Bold;
+ } else {
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Bold;
+ }
+ break;
+ }
+
+ case RTF.Minor.Italic: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Italic;
+ } else {
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Italic;
+ }
+ break;
+ }
+
+ case RTF.Minor.StrikeThru: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Strikeout;
+ } else {
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Strikeout;
+ }
+ break;
+ }
+
+ case RTF.Minor.Underline: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Underline;
+ } else {
+ rtf_style.rtf_rtfstyle = rtf_style.rtf_rtfstyle & ~FontStyle.Underline;
+ }
+ break;
+ }
+
+ case RTF.Minor.Invisible: {
+ FlushText (rtf, false);
+ rtf_style.rtf_visible = false;
+ break;
+ }
+
+ case RTF.Minor.NoUnderline: {
+ FlushText(rtf, false);
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Underline;
+ break;
+ }
+ }
+ break;
+ }
+
+ case RTF.Major.ParAttr: {
+ switch (rtf.Minor) {
+
+ case RTF.Minor.ParDef:
+ FlushText (rtf, false);
+ rtf_style.rtf_par_line_left_indent = 0;
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ break;
+
+ case RTF.Minor.LeftIndent:
+ using (Graphics g = CreateGraphics ())
+ rtf_style.rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * g.DpiX + 0.5F);
+ break;
+
+ case RTF.Minor.QuadCenter:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
+ break;
+
+ case RTF.Minor.QuadJust:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
+ break;
+
+ case RTF.Minor.QuadLeft:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ break;
+
+ case RTF.Minor.QuadRight:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Right;
+ break;
+ }
+ break;
+ }
+
+ case RTF.Major.SpecialChar: {
+ //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
+ SpecialChar (rtf);
+ break;
+ }
+ }
+ }
+
+ private void SpecialChar(RTF.RTF rtf) {
+ switch(rtf.Minor) {
+ case RTF.Minor.Page:
+ case RTF.Minor.Sect:
+ case RTF.Minor.Row:
+ case RTF.Minor.Line:
+ case RTF.Minor.Par: {
+ FlushText(rtf, true);
+ break;
+ }
+
+ case RTF.Minor.Cell: {
+ Console.Write(" ");
+ break;
+ }
+
+ case RTF.Minor.NoBrkSpace: {
+ Console.Write(" ");
+ break;
+ }
+
+ case RTF.Minor.Tab: {
+ rtf_line.Append ("\t");
+// FlushText (rtf, false);
+ break;
+ }
+
+ case RTF.Minor.NoReqHyphen:
+ case RTF.Minor.NoBrkHyphen: {
+ rtf_line.Append ("-");
+// FlushText (rtf, false);
+ break;
+ }
+
+ case RTF.Minor.Bullet: {
+ Console.WriteLine("*");
+ break;
+ }
+
+ case RTF.Minor.WidowCtrl:
+ break;
+
+ case RTF.Minor.EmDash: {
+ rtf_line.Append ("\u2014");
+ break;
+ }
+
+ case RTF.Minor.EnDash: {
+ rtf_line.Append ("\u2013");
+ break;
+ }
+/*
+ case RTF.Minor.LQuote: {
+ Console.Write("\u2018");
+ break;
+ }
+
+ case RTF.Minor.RQuote: {
+ Console.Write("\u2019");
+ break;
+ }
+
+ case RTF.Minor.LDblQuote: {
+ Console.Write("\u201C");
+ break;
+ }
+
+ case RTF.Minor.RDblQuote: {
+ Console.Write("\u201D");
+ break;
+ }
+*/
+ default: {
+// Console.WriteLine ("skipped special char: {0}", rtf.Minor);
+// rtf.SkipGroup();
+ break;
+ }
+ }
+ }
+
+ private void HandleText(RTF.RTF rtf) {
+ string str = rtf.EncodedText;
+
+ //todo - simplistically skips characters, should skip bytes?
+ if (rtf_skip_count > 0 && str.Length > 0) {
+ int iToRemove = Math.Min (rtf_skip_count, str.Length);
+
+ str = str.Substring (iToRemove);
+ rtf_skip_count-=iToRemove;
+ }
+
+ /*
+ if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
+ rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
+ } else {
+ if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
+ rtf_line.Append((char)rtf.Major);
+ } else {
+ //rtf_line.Append((char)rtf.Major);
+ Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
+ }
+ }
+ */
+
+ if (rtf_style.rtf_visible)
+ rtf_line.Append (str);
+ }
+
+ private void FlushText(RTF.RTF rtf, bool newline) {
+ int length;
+ Font font;
+
+ length = rtf_line.Length;
+ if (!newline && (length == 0)) {
+ return;
+ }
+
+ if (rtf_style.rtf_rtffont == null) {
+ // First font in table is default
+ rtf_style.rtf_rtffont = ShiftUI.RTF.Font.GetFont (rtf, 0);
+ }
+
+ font = new Font (rtf_style.rtf_rtffont.Name, rtf_style.rtf_rtffont_size, rtf_style.rtf_rtfstyle);
+
+ if (rtf_style.rtf_color == Color.Empty) {
+ ShiftUI.RTF.Color color;
+
+ // First color in table is default
+ color = ShiftUI.RTF.Color.GetColor (rtf, 0);
+
+ if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
+ rtf_style.rtf_color = ForeColor;
+ } else {
+ rtf_style.rtf_color = Color.FromArgb (color.Red, color.Green, color.Blue);
+ }
+
+ }
+
+ rtf_chars += rtf_line.Length;
+
+ // Try to re-use if we are told so - this usually happens when we are inserting a flow of rtf text
+ // with an already alive line.
+ if (rtf_cursor_x == 0 && !reuse_line) {
+ if (newline && rtf_line.ToString ().EndsWith (Environment.NewLine) == false)
+ rtf_line.Append (Environment.NewLine);
+
+ document.Add (rtf_cursor_y, rtf_line.ToString (), rtf_style.rtf_rtfalign, font, rtf_style.rtf_color,
+ newline ? LineEnding.Rich : LineEnding.Wrap);
+ if (rtf_style.rtf_par_line_left_indent != 0) {
+ Line line = document.GetLine (rtf_cursor_y);
+ line.indent = rtf_style.rtf_par_line_left_indent;
+ }
+ } else {
+ Line line;
+
+ line = document.GetLine (rtf_cursor_y);
+ line.indent = rtf_style.rtf_par_line_left_indent;
+ if (rtf_line.Length > 0) {
+ document.InsertString (line, rtf_cursor_x, rtf_line.ToString ());
+ document.FormatText (line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length,
+ font, rtf_style.rtf_color, Color.Empty,
+ FormatSpecified.Font | FormatSpecified.Color);
+ }
+ if (newline) {
+ line = document.GetLine (rtf_cursor_y);
+ line.ending = LineEnding.Rich;
+
+ if (line.Text.EndsWith (Environment.NewLine) == false)
+ line.Text += Environment.NewLine;
+ }
+
+ reuse_line = false; // sanity assignment - in this case we have already re-used one line.
+ }
+
+ if (newline) {
+ rtf_cursor_x = 0;
+ rtf_cursor_y++;
+ } else {
+ rtf_cursor_x += length;
+ }
+ rtf_line.Length = 0; // Empty line
+ }
+
+ private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
+ int x;
+ int y;
+ int chars;
+
+ InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
+ }
+
+ private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
+ RTF.RTF rtf;
+
+ rtf = new RTF.RTF(data);
+
+ // Prepare
+ rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
+ rtf.ClassCallback[RTF.TokenClass.Widget] = new RTF.ClassDelegate(HandleControl);
+ rtf.ClassCallback[RTF.TokenClass.Group] = new RTF.ClassDelegate(HandleGroup);
+
+ rtf_skip_count = 0;
+ rtf_line = new StringBuilder();
+ rtf_style.rtf_color = Color.Empty;
+ rtf_style.rtf_rtffont_size = (int)this.Font.Size;
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ rtf_style.rtf_rtfstyle = FontStyle.Regular;
+ rtf_style.rtf_rtffont = null;
+ rtf_style.rtf_visible = true;
+ rtf_style.rtf_skip_width = 1;
+ rtf_cursor_x = cursor_x;
+ rtf_cursor_y = cursor_y;
+ rtf_chars = 0;
+ rtf.DefaultFont(this.Font.Name);
+
+ rtf_text_map = new RTF.TextMap();
+ RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
+
+ document.SuspendRecalc ();
+
+ try {
+ rtf.Read(); // That's it
+ FlushText(rtf, false);
+
+ }
+
+
+ catch (RTF.RTFException e) {
+#if DEBUG
+ throw e;
+#endif
+ // Seems to be plain text or broken RTF
+ }
+
+ to_x = rtf_cursor_x;
+ to_y = rtf_cursor_y;
+ chars = rtf_chars;
+
+ // clear the section stack if it was used
+ if (rtf_section_stack != null)
+ rtf_section_stack.Clear();
+
+ document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
+ document.ResumeRecalc (true);
+
+ document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
+ }
+
+ private void RichTextBox_HScrolled(object sender, EventArgs e) {
+ OnHScroll(e);
+ }
+
+ private void RichTextBox_VScrolled(object sender, EventArgs e) {
+ OnVScroll(e);
+ }
+
+ private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
+ Point p;
+
+ p = pt;
+
+ if (p.X >= document.ViewPortWidth) {
+ p.X = document.ViewPortWidth - 1;
+ } else if (p.X < 0) {
+ p.X = 0;
+ }
+
+ if (p.Y >= document.ViewPortHeight) {
+ p.Y = document.ViewPortHeight - 1;
+ } else if (p.Y < 0) {
+ p.Y = 0;
+ }
+
+ tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
+ }
+
+ private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
+ if (prev_index != font_index) {
+ rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
+ }
+
+ if ((prev_font == null) || (prev_font.Size != font.Size)) {
+ rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
+ }
+
+ if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
+ if (font.Bold) {
+ rtf.Append("\\b");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\b0");
+ }
+ }
+ }
+
+ if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
+ if (font.Italic) {
+ rtf.Append("\\i");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\i0");
+ }
+ }
+ }
+
+ if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
+ if (font.Strikeout) {
+ rtf.Append("\\strike");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\strike0");
+ }
+ }
+ }
+
+ if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
+ if (font.Underline) {
+ rtf.Append("\\ul");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\ul0");
+ }
+ }
+ }
+ }
+
+ static readonly char [] ReservedRTFChars = new char [] { '\\', '{', '}' };
+
+ private void EmitRTFText(StringBuilder rtf, string text) {
+ int start = rtf.Length;
+ int count = text.Length;
+
+ // First emit simple unicode chars as escaped
+ EmitEscapedUnicode (rtf, text);
+
+ // This method emits user text *only*, so it's safe to escape any reserved rtf chars
+ // Escape '\' first, since it is used later to escape the other chars
+ if (text.IndexOfAny (ReservedRTFChars) > -1) {
+ rtf.Replace ("\\", "\\\\", start, count);
+ rtf.Replace ("{", "\\{", start, count);
+ rtf.Replace ("}", "\\}", start, count);
+ }
+ }
+
+ // The chars to be escaped use "\'" + its hexadecimal value.
+ private void EmitEscapedUnicode (StringBuilder sb, string text)
+ {
+ int pos;
+ int start = 0;
+
+ while ((pos = IndexOfNonAscii (text, start)) > -1) {
+ sb.Append (text, start, pos - start);
+
+ int n = (int)text [pos];
+ sb.Append ("\\'");
+ sb.Append (n.ToString ("X"));
+
+ start = pos + 1;
+ }
+
+ // Append remaining (maybe all) the text value.
+ if (start < text.Length)
+ sb.Append (text, start, text.Length - start);
+ }
+
+ // MS seems to be escaping values larger than 0x80
+ private int IndexOfNonAscii (string text, int startIndex)
+ {
+ for (int i = startIndex; i < text.Length; i++) {
+ int n = (int)text [i];
+ if (n < 0 || n >= 0x80)
+ return i;
+ }
+
+ return -1;
+ }
+
+ // start_pos and end_pos are 0-based
+ private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
+ StringBuilder sb;
+ ArrayList fonts;
+ ArrayList colors;
+ Color color;
+ Font font;
+ Line line;
+ LineTag tag;
+ int pos;
+ int line_no;
+ int line_len;
+ int i;
+ int length;
+
+ sb = new StringBuilder();
+ fonts = new ArrayList(10);
+ colors = new ArrayList(10);
+
+ // Two runs, first we parse to determine tables;
+ // and unlike most of our processing here we work on tags
+
+ line = start_line;
+ line_no = start_line.line_no;
+ pos = start_pos;
+
+ // Add default font and color; to optimize document content we don't
+ // use this.Font and this.ForeColor but the font/color from the first tag
+ tag = LineTag.FindTag(start_line, pos);
+ font = tag.Font;
+ color = tag.Color;
+ fonts.Add(font.Name);
+ colors.Add(color);
+
+ while (line_no <= end_line.line_no) {
+ line = document.GetLine(line_no);
+ tag = LineTag.FindTag(line, pos);
+
+ if (line_no != end_line.line_no) {
+ line_len = line.text.Length;
+ } else {
+ line_len = end_pos;
+ }
+
+ while (pos < line_len) {
+ if (tag.Font.Name != font.Name) {
+ font = tag.Font;
+ if (!fonts.Contains(font.Name)) {
+ fonts.Add(font.Name);
+ }
+ }
+
+ if (tag.Color != color) {
+ color = tag.Color;
+ if (!colors.Contains(color)) {
+ colors.Add(color);
+ }
+ }
+
+ pos = tag.Start + tag.Length - 1;
+ tag = tag.Next;
+ }
+ pos = 0;
+ line_no++;
+ }
+
+ // We have the tables, emit the header
+ sb.Append("{\\rtf1\\ansi");
+ sb.Append("\\ansicpg1252"); // FIXME - is this correct?
+
+ // Default Font
+ sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
+
+ // Default Language
+ sb.Append("\\deflang1033" + Environment.NewLine); // FIXME - always 1033?
+
+ // Emit the font table
+ sb.Append("{\\fonttbl");
+ for (i = 0; i < fonts.Count; i++) {
+ sb.Append(String.Format("{{\\f{0}", i)); // {Font
+ sb.Append("\\fnil"); // Family
+ sb.Append("\\fcharset0 "); // Charset ANSI<space>
+ sb.Append((string)fonts[i]); // Font name
+ sb.Append(";}"); // }
+ }
+ sb.Append("}");
+ sb.Append(Environment.NewLine);
+
+ // Emit the color table (if needed)
+ if ((colors.Count > 1) || ((((Color)colors[0]).R != this.ForeColor.R) || (((Color)colors[0]).G != this.ForeColor.G) || (((Color)colors[0]).B != this.ForeColor.B))) {
+ sb.Append("{\\colortbl "); // Header and NO! default color
+ for (i = 0; i < colors.Count; i++) {
+ sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
+ sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
+ sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
+ sb.Append(";");
+ }
+ sb.Append("}");
+ sb.Append(Environment.NewLine);
+ }
+
+ sb.Append("{\\*\\generator Mono RichTextBox;}");
+ // Emit initial paragraph settings
+ tag = LineTag.FindTag(start_line, start_pos);
+ sb.Append("\\pard"); // Reset to default paragraph properties
+ EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.Font.Name), null, tag.Font); // Font properties
+ sb.Append(" "); // Space separator
+
+ font = tag.Font;
+ color = (Color)colors[0];
+ line = start_line;
+ line_no = start_line.line_no;
+ pos = start_pos;
+
+ while (line_no <= end_line.line_no) {
+ line = document.GetLine(line_no);
+ tag = LineTag.FindTag(line, pos);
+
+ if (line_no != end_line.line_no) {
+ line_len = line.text.Length;
+ } else {
+ line_len = end_pos;
+ }
+
+ while (pos < line_len) {
+ length = sb.Length;
+
+ if (tag.Font != font) {
+ EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.Font.Name), font, tag.Font);
+ font = tag.Font;
+ }
+
+ if (tag.Color != color) {
+ color = tag.Color;
+ sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
+ }
+ if (length != sb.Length) {
+ sb.Append(" "); // Emit space to separate keywords from text
+ }
+
+ // Emit the string itself
+ if (line_no != end_line.line_no) {
+ EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
+ } else {
+ if (end_pos < (tag.Start + tag.Length - 1)) {
+ // Emit partial tag only, end_pos is inside this tag
+ EmitRTFText(sb, tag.Line.text.ToString(pos, end_pos - pos));
+ } else {
+ EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
+ }
+ }
+
+ pos = tag.Start + tag.Length - 1;
+ tag = tag.Next;
+ }
+ if (pos >= line.text.Length) {
+ if (line.ending != LineEnding.Wrap) {
+ sb.Append("\\par");
+ sb.Append(Environment.NewLine);
+ }
+ }
+ pos = 0;
+ line_no++;
+ }
+
+ sb.Append("}");
+ sb.Append(Environment.NewLine);
+
+ return sb;
+ }
+ #endregion // Private Methods
+ }
+}