private DirectMessage ( int msg, IntPtr wParam, IntPtr lParam ) : IntPtr | ||
msg | int | |
wParam | IntPtr | |
lParam | IntPtr | |
return | IntPtr |
/// <summary> /// Given a document position which is filled with this indicator, will return the document position /// where the use of this indicator ends. /// </summary> /// <param name="position">A zero-based document position using this indicator.</param> /// <returns>The zero-based document position where the use of this indicator ends.</returns> /// <remarks> /// Specifying a <paramref name="position" /> which is not filled with this indicator will cause this method /// to return the end position of the range where this indicator is not in use (the negative space). If this /// indicator is not in use anywhere within the document the return value will be 0. /// </remarks> public int End(int position) { position = Helpers.Clamp(position, 0, scintilla.TextLength); position = scintilla.Lines.CharToBytePosition(position); position = scintilla.DirectMessage(NativeMethods.SCI_INDICATOREND, new IntPtr(Index), new IntPtr(position)).ToInt32(); return(scintilla.Lines.ByteToCharPosition(position)); }
/// <summary> /// Removes all text and styles associated with the annotation. /// </summary> public virtual void Clear() { CheckInvalid(); // Remove the annotation _scintilla.DirectMessage(NativeMethods.SCI_ANNOTATIONSETTEXT, new IntPtr(_lineIndex), IntPtr.Zero); }
public void Copy(bool copyLine) { if (copyLine) { Scintilla.DirectMessage(NativeMethods.SCI_COPYALLOWLINE, IntPtr.Zero, IntPtr.Zero); } else { Scintilla.DirectMessage(NativeMethods.SCI_COPY, IntPtr.Zero, IntPtr.Zero); } }
private static unsafe ArraySegment <byte> GetStyledText(Scintilla scintilla, int startBytePos, int endBytePos, bool addLineBreak) { Debug.Assert(endBytePos > startBytePos); // Make sure the range is styled scintilla.DirectMessage(NativeMethods.SCI_COLOURISE, new IntPtr(startBytePos), new IntPtr(endBytePos)); var byteLength = (endBytePos - startBytePos); var buffer = new byte[(byteLength * 2) + (addLineBreak ? 4 : 0) + 2]; fixed(byte *bp = buffer) { NativeMethods.Sci_TextRange *tr = stackalloc NativeMethods.Sci_TextRange[1]; tr->chrg.cpMin = startBytePos; tr->chrg.cpMax = endBytePos; tr->lpstrText = new IntPtr(bp); scintilla.DirectMessage(NativeMethods.SCI_GETSTYLEDTEXT, IntPtr.Zero, new IntPtr(tr)); byteLength *= 2; } // Add a line break? // We do this when this range is part of a rectangular selection. if (addLineBreak) { var style = buffer[byteLength - 1]; buffer[byteLength++] = (byte)'\r'; buffer[byteLength++] = style; buffer[byteLength++] = (byte)'\n'; buffer[byteLength++] = style; // Fix-up the NULL terminator just in case buffer[byteLength] = 0; buffer[byteLength + 1] = 0; } return(new ArraySegment <byte>(buffer, 0, byteLength)); }
/// <summary> /// Updates the classification of the characters specified. /// </summary> /// <param name="chars">The characters to reclassify.</param> /// <param name="classification">The new classification for the <paramref name="chars" />.</param> /// <remarks> /// The default character classifications are suitable for most text and do not affect the lexing process. /// Rather, character classifications specify how the <see cref="Scintilla" /> control interprets /// characters for operations such as skipping or selecting a word. Specifying the classification of /// Unicode characters is not supported. /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="chars" /> is null.</exception> /// <exception cref="ArgumentException"> /// One or more of the values specified in <paramref name="chars" /> is not an ASCII character. /// </exception> /// <exception cref="InvalidEnumArgumentException"> /// The value assigned to <paramref name="classification" /> is not one of the <see cref="CharClassification" /> values. /// </exception> public void ReclassifyChars(IEnumerable <char> chars, CharClassification classification) { if (chars == null) { throw new ArgumentNullException("chars"); } if (!Enum.IsDefined(typeof(CharClassification), classification)) { throw new InvalidEnumArgumentException("classification", (int)classification, typeof(CharClassification)); } int msg; switch (classification) { case CharClassification.Whitespace: msg = NativeMethods.SCI_SETWHITESPACECHARS; break; case CharClassification.Word: msg = NativeMethods.SCI_SETWORDCHARS; break; default: msg = NativeMethods.SCI_SETPUNCTUATIONCHARS; break; } List <byte> bytes = new List <byte>(); foreach (char c in chars) { if (c < 0 || c >= 256) { throw new ArgumentException(Resources.Exception_MustBeASCII, "value"); } bytes.Add((byte)c); } // Null terminator bytes.Add(0); unsafe { fixed(byte *bp = bytes.ToArray()) Scintilla.DirectMessage(msg, IntPtr.Zero, (IntPtr)bp); } }
/// <summary> /// The number of lines displayed when a line of text is wrapped. /// </summary> /// <param name="lineIndex">The zero-based index of the line to count.</param> /// <returns>The numbers of display lines the line of text occupies.</returns> ///// <exception cref="ArgumentOutOfRangeException"> ///// <paramref name="lineIndex"/> is less than 0 or greater than the line count. ///// </exception> public virtual int GetWrapCount(int lineIndex) { //if (lineIndex < 0 || lineIndex > _scintilla.Lines.Count) //{ // string paramName = "lineIndex"; // string message = string.Format(CultureInfo.InvariantCulture, Resources.Exception_MustBeNonNegativeAndLessThan, paramName, "the line count"); // throw new ArgumentOutOfRangeException(paramName, message); //} return(_scintilla.DirectMessage(NativeMethods.SCI_WRAPCOUNT, new IntPtr(lineIndex), IntPtr.Zero).ToInt32()); }
/// <summary> /// Sets the marker symbol to a custom image. /// </summary> /// <param name="image">The Bitmap to use as a marker symbol.</param> /// <remarks>Calling this method will also update the <see cref="Symbol" /> property to <see cref="MarkerSymbol.RgbaImage" />.</remarks> public unsafe void DefineRgbaImage(Bitmap image) { if (image == null) { return; } scintilla.DirectMessage(NativeMethods.SCI_RGBAIMAGESETWIDTH, new IntPtr(image.Width)); scintilla.DirectMessage(NativeMethods.SCI_RGBAIMAGESETHEIGHT, new IntPtr(image.Height)); var bytes = Helpers.BitmapToArgb(image); fixed(byte *bp = bytes) scintilla.DirectMessage(NativeMethods.SCI_MARKERDEFINERGBAIMAGE, new IntPtr(Index), new IntPtr(bp)); }
internal char[] GetClassificationChars(CharClassification classification) { Debug.Assert(Enum.IsDefined(typeof(CharClassification), classification)); int msg; switch (classification) { case CharClassification.Whitespace: msg = NativeMethods.SCI_GETWHITESPACECHARS; break; case CharClassification.Word: msg = NativeMethods.SCI_GETWORDCHARS; break; default: msg = NativeMethods.SCI_GETPUNCTUATIONCHARS; break; } char[] chars; unsafe { byte *bp = stackalloc byte[256]; // Max possible according to Scintilla code int length = (int)Scintilla.DirectMessage(msg, IntPtr.Zero, (IntPtr)bp); chars = new char[length]; for (int i = 0; i < length; i++) { chars[i] = (char)bp[i]; } } return(chars); }
private static unsafe List<ArraySegment<byte>> GetStyledSegments(Scintilla scintilla, bool currentSelection, bool currentLine, int startBytePos, int endBytePos, out StyleData[] styles) { var segments = new List<ArraySegment<byte>>(); if (currentSelection) { // Get each selection as a segment. // Rectangular selections are ordered top to bottom and have line breaks appended. var ranges = new List<Tuple<int, int>>(); var selCount = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONS).ToInt32(); for (int i = 0; i < selCount; i++) { var selStartBytePos = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONNSTART, new IntPtr(i)).ToInt32(); var selEndBytePos = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONNEND, new IntPtr(i)).ToInt32(); ranges.Add(Tuple.Create(selStartBytePos, selEndBytePos)); } var selIsRect = scintilla.DirectMessage(NativeMethods.SCI_SELECTIONISRECTANGLE) != IntPtr.Zero; if (selIsRect) ranges.OrderBy(r => r.Item1); // Sort top to bottom foreach (var range in ranges) { var styledText = GetStyledText(scintilla, range.Item1, range.Item2, selIsRect); segments.Add(styledText); } } else if (currentLine) { // Get the current line var mainSelection = scintilla.DirectMessage(NativeMethods.SCI_GETMAINSELECTION).ToInt32(); var mainCaretPos = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONNCARET, new IntPtr(mainSelection)).ToInt32(); var lineIndex = scintilla.DirectMessage(NativeMethods.SCI_LINEFROMPOSITION, new IntPtr(mainCaretPos)).ToInt32(); var lineStartBytePos = scintilla.DirectMessage(NativeMethods.SCI_POSITIONFROMLINE, new IntPtr(lineIndex)).ToInt32(); var lineLength = scintilla.DirectMessage(NativeMethods.SCI_POSITIONFROMLINE, new IntPtr(lineIndex)).ToInt32(); var styledText = GetStyledText(scintilla, lineStartBytePos, (lineStartBytePos + lineLength), false); segments.Add(styledText); } else // User-specified range { Debug.Assert(startBytePos != endBytePos); var styledText = GetStyledText(scintilla, startBytePos, endBytePos, false); segments.Add(styledText); } // Build a list of (used) styles styles = new StyleData[NativeMethods.STYLE_MAX + 1]; styles[Style.Default].Used = true; styles[Style.Default].FontName = scintilla.Styles[Style.Default].Font; styles[Style.Default].SizeF = scintilla.Styles[Style.Default].SizeF; styles[Style.Default].Weight = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETWEIGHT, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Italic = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETITALIC, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Underline = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETUNDERLINE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].BackColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETBACK, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].ForeColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETFORE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Case = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETCASE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Visible = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETVISIBLE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); foreach (var seg in segments) { for (int i = 0; i < seg.Count; i += 2) { var style = seg.Array[i + 1]; if (!styles[style].Used) { styles[style].Used = true; styles[style].FontName = scintilla.Styles[style].Font; styles[style].SizeF = scintilla.Styles[style].SizeF; styles[style].Weight = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETWEIGHT, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Italic = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETITALIC, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Underline = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETUNDERLINE, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].BackColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETBACK, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].ForeColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETFORE, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Case = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETCASE, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Visible = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETVISIBLE, new IntPtr(style), IntPtr.Zero).ToInt32(); } } } return segments; }
private static unsafe void CopyRtf(Scintilla scintilla, StyleData[] styles, List<ArraySegment<byte>> styledSegments) { // NppExport -> NppExport.cpp // NppExport -> RTFExporter.cpp // http://en.wikipedia.org/wiki/Rich_Text_Format // https://msdn.microsoft.com/en-us/library/windows/desktop/ms649013.aspx // http://forums.codeguru.com/showthread.php?242982-Converting-pixels-to-twips // http://en.wikipedia.org/wiki/UTF-8 try { // Calculate twips per space int twips; var fontStyle = FontStyle.Regular; if (styles[Style.Default].Weight >= 700) fontStyle |= FontStyle.Bold; if (styles[Style.Default].Italic != 0) fontStyle |= FontStyle.Italic; if (styles[Style.Default].Underline != 0) fontStyle |= FontStyle.Underline; using (var graphics = scintilla.CreateGraphics()) using (var font = new Font(styles[Style.Default].FontName, styles[Style.Default].SizeF, fontStyle)) { var width = graphics.MeasureString(" ", font).Width; twips = (int)((width / graphics.DpiX) * 1440); // TODO The twips value calculated seems too small on my computer } // Write RTF using (var ms = new NativeMemoryStream(styledSegments.Sum(s => s.Count))) using (var tw = new StreamWriter(ms, Encoding.ASCII)) { var tabWidth = scintilla.DirectMessage(NativeMethods.SCI_GETTABWIDTH).ToInt32(); var deftab = tabWidth * twips; tw.WriteLine(@"{{\rtf1\ansi\deff0\deftab{0}", deftab); tw.Flush(); // Build the font table tw.Write(@"{\fonttbl"); tw.Write(@"{{\f0 {0};}}", styles[Style.Default].FontName); var fontIndex = 1; for (int i = 0; i < styles.Length; i++) { if (!styles[i].Used) continue; if (i == Style.Default) continue; // Not a completely unique list, but close enough if (styles[i].FontName != styles[Style.Default].FontName) { styles[i].FontIndex = fontIndex++; tw.Write(@"{{\f{0} {1};}}", styles[i].FontIndex, styles[i].FontName); } } tw.WriteLine("}"); // fonttbl tw.Flush(); // Build the color table tw.Write(@"{\colortbl"); tw.Write(@"\red{0}\green{1}\blue{2};", (styles[Style.Default].ForeColor >> 0) & 0xFF, (styles[Style.Default].ForeColor >> 8) & 0xFF, (styles[Style.Default].ForeColor >> 16) & 0xFF); tw.Write(@"\red{0}\green{1}\blue{2};", (styles[Style.Default].BackColor >> 0) & 0xFF, (styles[Style.Default].BackColor >> 8) & 0xFF, (styles[Style.Default].BackColor >> 16) & 0xFF); styles[Style.Default].ForeColorIndex = 0; styles[Style.Default].BackColorIndex = 1; var colorIndex = 2; for (int i = 0; i < styles.Length; i++) { if (!styles[i].Used) continue; if (i == Style.Default) continue; // Not a completely unique list, but close enough if (styles[i].ForeColor != styles[Style.Default].ForeColor) { styles[i].ForeColorIndex = colorIndex++; tw.Write(@"\red{0}\green{1}\blue{2};", (styles[i].ForeColor >> 0) & 0xFF, (styles[i].ForeColor >> 8) & 0xFF, (styles[i].ForeColor >> 16) & 0xFF); } else { styles[i].ForeColorIndex = styles[Style.Default].ForeColorIndex; } if (styles[i].BackColor != styles[Style.Default].BackColor) { styles[i].BackColorIndex = colorIndex++; tw.Write(@"\red{0}\green{1}\blue{2};", (styles[i].BackColor >> 0) & 0xFF, (styles[i].BackColor >> 8) & 0xFF, (styles[i].BackColor >> 16) & 0xFF); } else { styles[i].BackColorIndex = styles[Style.Default].BackColorIndex; } } tw.WriteLine("}"); // colortbl tw.Flush(); // Start with the default style tw.Write(@"\f{0}\fs{1}\cf{2}\chshdng0\chcbpat{3}\cb{3} ", styles[Style.Default].FontIndex, (int)(styles[Style.Default].SizeF * 2), styles[Style.Default].ForeColorIndex, styles[Style.Default].BackColorIndex); if (styles[Style.Default].Italic != 0) tw.Write(@"\i"); if (styles[Style.Default].Underline != 0) tw.Write(@"\ul"); if (styles[Style.Default].Weight >= 700) tw.Write(@"\b"); tw.AutoFlush = true; var lastStyle = Style.Default; var unicodeLineEndings = ((scintilla.DirectMessage(NativeMethods.SCI_GETLINEENDTYPESACTIVE).ToInt32() & NativeMethods.SC_LINE_END_TYPE_UNICODE) > 0); foreach (var seg in styledSegments) { var endOffset = seg.Offset + seg.Count; for (int i = seg.Offset; i < endOffset; i += 2) { var ch = seg.Array[i]; var style = seg.Array[i + 1]; if (lastStyle != style) { // Change the style if (styles[lastStyle].FontIndex != styles[style].FontIndex) tw.Write(@"\f{0}", styles[style].FontIndex); if (styles[lastStyle].SizeF != styles[style].SizeF) tw.Write(@"\fs{0}", (int)(styles[style].SizeF * 2)); if (styles[lastStyle].ForeColorIndex != styles[style].ForeColorIndex) tw.Write(@"\cf{0}", styles[style].ForeColorIndex); if (styles[lastStyle].BackColorIndex != styles[style].BackColorIndex) tw.Write(@"\chshdng0\chcbpat{0}\cb{0}", styles[style].BackColorIndex); if (styles[lastStyle].Italic != styles[style].Italic) tw.Write(@"\i{0}", styles[style].Italic != 0 ? "" : "0"); if (styles[lastStyle].Underline != styles[style].Underline) tw.Write(@"\ul{0}", styles[style].Underline != 0 ? "" : "0"); if (styles[lastStyle].Weight != styles[style].Weight) { if (styles[style].Weight >= 700 && styles[lastStyle].Weight < 700) tw.Write(@"\b"); else if (styles[style].Weight < 700 && styles[lastStyle].Weight >= 700) tw.Write(@"\b0"); } // NOTE: We don't support StyleData.Visible and StyleData.Case in RTF lastStyle = style; tw.Write(" "); // Delimiter } switch (ch) { case (byte)'{': tw.Write(@"\{"); break; case (byte)'}': tw.Write(@"\}"); break; case (byte)'\\': tw.Write(@"\\"); break; case (byte)'\t': tw.Write(@"\tab "); break; case (byte)'\r': if (i + 2 < endOffset) { if (seg.Array[i + 2] == (byte)'\n') i += 2; } // Either way, this is a line break goto case (byte)'\n'; case 0xC2: if (unicodeLineEndings && i + 2 < endOffset) { if (seg.Array[i + 2] == 0x85) // NEL \u0085 { i += 2; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case 0xE2: if (unicodeLineEndings && i + 4 < endOffset) { if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA8) // LS \u2028 { i += 4; goto case (byte)'\n'; } else if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA9) // PS \u2029 { i += 4; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case (byte)'\n': // All your line breaks are belong to us tw.WriteLine(@"\par"); break; default: if (ch == 0) { // Scintilla behavior is to allow control characters except for // NULL which will cause the Clipboard to truncate the string. tw.Write(" "); // Replace with space break; } if (ch > 0x7F) { // Treat as UTF-8 code point int unicode = 0; if (ch < 0xE0 && i + 2 < endOffset) { unicode |= ((0x1F & ch) << 6); unicode |= (0x3F & seg.Array[i + 2]); tw.Write(@"\u{0}?", unicode); i += 2; break; } else if (ch < 0xF0 && i + 4 < endOffset) { unicode |= ((0xF & ch) << 12); unicode |= ((0x3F & seg.Array[i + 2]) << 6); unicode |= (0x3F & seg.Array[i + 4]); tw.Write(@"\u{0}?", unicode); i += 4; break; } else if (ch < 0xF8 && i + 6 < endOffset) { unicode |= ((0x7 & ch) << 18); unicode |= ((0x3F & seg.Array[i + 2]) << 12); unicode |= ((0x3F & seg.Array[i + 4]) << 6); unicode |= (0x3F & seg.Array[i + 6]); tw.Write(@"\u{0}?", unicode); i += 6; break; } } // Regular ANSI char ms.WriteByte(ch); break; } } } tw.AutoFlush = false; tw.WriteLine("}"); // rtf1 tw.Flush(); // Terminator ms.WriteByte(0); // var str = GetString(ms.Pointer, (int)ms.Length, Encoding.ASCII); if (NativeMethods.SetClipboardData(CF_RTF, ms.Pointer) != IntPtr.Zero) ms.FreeOnDispose = false; // Clipboard will free memory } } catch (Exception ex) { // Yes, we swallow any exceptions. That may seem like code smell but this matches // the behavior of the Clipboard class, Windows Forms controls, and native Scintilla. Debug.Fail(ex.Message, ex.ToString()); } }
private static unsafe void CopyHtml(Scintilla scintilla, StyleData[] styles, List<ArraySegment<byte>> styledSegments) { // NppExport -> NppExport.cpp // NppExport -> HTMLExporter.cpp // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/html-clipboard.aspx // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/sample-code-html-clipboard.aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/ms649015.aspx try { long pos = 0; byte[] bytes; // Write HTML using (var ms = new NativeMemoryStream(styledSegments.Sum(s => s.Count))) using (var tw = new StreamWriter(ms, new UTF8Encoding(false))) { const int INDEX_START_HTML = 23; const int INDEX_START_FRAGMENT = 65; const int INDEX_END_FRAGMENT = 87; const int INDEX_END_HTML = 41; tw.WriteLine("Version:0.9"); tw.WriteLine("StartHTML:00000000"); tw.WriteLine("EndHTML:00000000"); tw.WriteLine("StartFragment:00000000"); tw.WriteLine("EndFragment:00000000"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_START_HTML, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); tw.WriteLine("<html>"); tw.WriteLine("<head>"); tw.WriteLine(@"<meta charset=""utf-8"" />"); tw.WriteLine(@"<title>ScintillaNET v{0}</title>", scintilla.GetType().Assembly.GetName().Version.ToString(3)); tw.WriteLine("</head>"); tw.WriteLine("<body>"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_START_FRAGMENT, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); tw.WriteLine("<!--StartFragment -->"); // Write the styles. // We're doing the style tag in the body to include it in the "fragment". tw.WriteLine(@"<style type=""text/css"" scoped="""">"); tw.Write("div#segments {"); tw.Write(" float: left;"); tw.Write(" white-space: pre;"); tw.Write(" line-height: {0}px;", scintilla.DirectMessage(NativeMethods.SCI_TEXTHEIGHT, new IntPtr(0)).ToInt32()); tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[Style.Default].BackColor >> 0) & 0xFF, (styles[Style.Default].BackColor >> 8) & 0xFF, (styles[Style.Default].BackColor >> 16) & 0xFF); tw.WriteLine(" }"); for (int i = 0; i < styles.Length; i++) { if (!styles[i].Used) continue; tw.Write("span.s{0} {{", i); tw.Write(@" font-family: ""{0}"";", styles[i].FontName); tw.Write(" font-size: {0}pt;", styles[i].SizeF); tw.Write(" font-weight: {0};", styles[i].Weight); if (styles[i].Italic != 0) tw.Write(" font-style: italic;"); if (styles[i].Underline != 0) tw.Write(" text-decoration: underline;"); tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[i].BackColor >> 0) & 0xFF, (styles[i].BackColor >> 8) & 0xFF, (styles[i].BackColor >> 16) & 0xFF); tw.Write(" color: #{0:X2}{1:X2}{2:X2};", (styles[i].ForeColor >> 0) & 0xFF, (styles[i].ForeColor >> 8) & 0xFF, (styles[i].ForeColor >> 16) & 0xFF); switch ((StyleCase)styles[i].Case) { case StyleCase.Upper: tw.Write(" text-transform: uppercase;"); break; case StyleCase.Lower: tw.Write(" text-transform: lowercase;"); break; } if (styles[i].Visible == 0) tw.Write(" visibility: hidden;"); tw.WriteLine(" }"); } tw.WriteLine("</style>"); tw.Write(@"<div id=""segments""><span class=""s{0}"">", Style.Default); tw.Flush(); var tabSize = scintilla.DirectMessage(NativeMethods.SCI_GETTABWIDTH).ToInt32(); var tab = new string(' ', tabSize); tw.AutoFlush = true; var lastStyle = Style.Default; var unicodeLineEndings = ((scintilla.DirectMessage(NativeMethods.SCI_GETLINEENDTYPESACTIVE).ToInt32() & NativeMethods.SC_LINE_END_TYPE_UNICODE) > 0); foreach (var seg in styledSegments) { var endOffset = seg.Offset + seg.Count; for (int i = seg.Offset; i < endOffset; i += 2) { var ch = seg.Array[i]; var style = seg.Array[i + 1]; if (lastStyle != style) { tw.Write(@"</span><span class=""s{0}"">", style); lastStyle = style; } switch (ch) { case (byte)'<': tw.Write("<"); break; case (byte)'>': tw.Write(">"); break; case (byte)'&': tw.Write("&"); break; case (byte)'\t': tw.Write(tab); break; case (byte)'\r': if (i + 2 < endOffset) { if (seg.Array[i + 2] == (byte)'\n') i += 2; } // Either way, this is a line break goto case (byte)'\n'; case 0xC2: if (unicodeLineEndings && i + 2 < endOffset) { if (seg.Array[i + 2] == 0x85) // NEL \u0085 { i += 2; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case 0xE2: if (unicodeLineEndings && i + 4 < endOffset) { if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA8) // LS \u2028 { i += 4; goto case (byte)'\n'; } else if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA9) // PS \u2029 { i += 4; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case (byte)'\n': // All your line breaks are belong to us tw.Write("\r\n"); break; default: if (ch == 0) { // Scintilla behavior is to allow control characters except for // NULL which will cause the Clipboard to truncate the string. tw.Write(" "); // Replace with space break; } ms.WriteByte(ch); break; } } } tw.AutoFlush = false; tw.WriteLine("</span></div>"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_END_FRAGMENT, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); tw.WriteLine("<!--EndFragment-->"); tw.WriteLine("</body>"); tw.WriteLine("</html>"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_END_HTML, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); // Terminator ms.WriteByte(0); var str = GetString(ms.Pointer, (int)ms.Length, Encoding.UTF8); if (NativeMethods.SetClipboardData(CF_HTML, ms.Pointer) != IntPtr.Zero) ms.FreeOnDispose = false; // Clipboard will free memory } } catch (Exception ex) { // Yes, we swallow any exceptions. That may seem like code smell but this matches // the behavior of the Clipboard class, Windows Forms controls, and native Scintilla. Debug.Fail(ex.Message, ex.ToString()); } }
public static void Copy(Scintilla scintilla, CopyFormat format, bool useSelection, bool allowLine, int startBytePos, int endBytePos) { // Plain text if ((format & CopyFormat.Text) > 0) { if (useSelection) { if (allowLine) scintilla.DirectMessage(NativeMethods.SCI_COPYALLOWLINE); else scintilla.DirectMessage(NativeMethods.SCI_COPY); } else { scintilla.DirectMessage(NativeMethods.SCI_COPYRANGE, new IntPtr(startBytePos), new IntPtr(endBytePos)); } } // RTF and/or HTML if ((format & (CopyFormat.Rtf | CopyFormat.Html)) > 0) { // If we ever allow more than UTF-8, this will have to be revisited Debug.Assert(scintilla.DirectMessage(NativeMethods.SCI_GETCODEPAGE).ToInt32() == NativeMethods.SC_CP_UTF8); if (!registeredFormats) { // Register non-standard clipboard formats. // Scintilla -> ScintillaWin.cxx // NppExport -> HTMLExporter.h // NppExport -> RTFExporter.h CF_LINESELECT = NativeMethods.RegisterClipboardFormat("MSDEVLineSelect"); CF_VSLINETAG = NativeMethods.RegisterClipboardFormat("VisualStudioEditorOperationsLineCutCopyClipboardTag"); CF_HTML = NativeMethods.RegisterClipboardFormat("HTML Format"); CF_RTF = NativeMethods.RegisterClipboardFormat("Rich Text Format"); registeredFormats = true; } var lineCopy = false; StyleData[] styles = null; List<ArraySegment<byte>> styledSegments = null; if (useSelection) { var selIsEmpty = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONEMPTY) != IntPtr.Zero; if (selIsEmpty) { if (allowLine) { // Get the current line styledSegments = GetStyledSegments(scintilla, false, true, 0, 0, out styles); lineCopy = true; } } else { // Get every selection styledSegments = GetStyledSegments(scintilla, true, false, 0, 0, out styles); } } else if (startBytePos != endBytePos) { // User-specified range styledSegments = GetStyledSegments(scintilla, false, false, startBytePos, endBytePos, out styles); } // If we have segments and can open the clipboard if (styledSegments != null && styledSegments.Count > 0 && NativeMethods.OpenClipboard(scintilla.Handle)) { if ((format & CopyFormat.Text) == 0) { // Do the things default (plain text) processing would normally give us NativeMethods.EmptyClipboard(); if (lineCopy) { // Clipboard tags NativeMethods.SetClipboardData(CF_LINESELECT, IntPtr.Zero); NativeMethods.SetClipboardData(CF_VSLINETAG, IntPtr.Zero); } } // RTF if ((format & CopyFormat.Rtf) > 0) CopyRtf(scintilla, styles, styledSegments); // HTML if ((format & CopyFormat.Html) > 0) CopyHtml(scintilla, styles, styledSegments); NativeMethods.CloseClipboard(); } } }
/// <summary> /// Removes all text displayed in every <see cref="MarginType.Text" /> and <see cref="MarginType.RightText" /> margins. /// </summary> public void ClearAllText() { scintilla.DirectMessage(NativeMethods.SCI_MARGINTEXTCLEARALL); }
/// <summary> /// Scrolls the display by the specified number of lines and columns. /// </summary> /// <param name="lines">The number of display lines to scroll. Positive numbers scroll down, negative numbers scroll up.</param> /// <param name="columns">The number of columns to scroll. Positive numbers scroll right, negative numbers scroll left.</param> public virtual void ScrollBy(int lines, int columns) { // NOTE: We reverse the order of the params _scintilla.DirectMessage(NativeMethods.SCI_LINESCROLL, new IntPtr(columns), new IntPtr(lines)); }
private static unsafe void CopyHtml(Scintilla scintilla, StyleData[] styles, List <ArraySegment <byte> > styledSegments) { // NppExport -> NppExport.cpp // NppExport -> HTMLExporter.cpp // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/html-clipboard.aspx // http://blogs.msdn.com/b/jmstall/archive/2007/01/21/sample-code-html-clipboard.aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/ms649015.aspx try { long pos = 0; byte[] bytes; // Write HTML using (var ms = new NativeMemoryStream(styledSegments.Sum(s => s.Count))) using (var tw = new StreamWriter(ms, new UTF8Encoding(false))) { const int INDEX_START_HTML = 23; const int INDEX_START_FRAGMENT = 65; const int INDEX_END_FRAGMENT = 87; const int INDEX_END_HTML = 41; tw.WriteLine("Version:0.9"); tw.WriteLine("StartHTML:00000000"); tw.WriteLine("EndHTML:00000000"); tw.WriteLine("StartFragment:00000000"); tw.WriteLine("EndFragment:00000000"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_START_HTML, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); tw.WriteLine("<html>"); tw.WriteLine("<head>"); tw.WriteLine(@"<meta charset=""utf-8"" />"); tw.WriteLine(@"<title>ScintillaNET v{0}</title>", scintilla.GetType().Assembly.GetName().Version.ToString(3)); tw.WriteLine("</head>"); tw.WriteLine("<body>"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_START_FRAGMENT, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); tw.WriteLine("<!--StartFragment -->"); // Write the styles. // We're doing the style tag in the body to include it in the "fragment". tw.WriteLine(@"<style type=""text/css"" scoped="""">"); tw.Write("div#segments {"); tw.Write(" float: left;"); tw.Write(" white-space: pre;"); tw.Write(" line-height: {0}px;", scintilla.DirectMessage(NativeMethods.SCI_TEXTHEIGHT, new IntPtr(0)).ToInt32()); tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[Style.Default].BackColor >> 0) & 0xFF, (styles[Style.Default].BackColor >> 8) & 0xFF, (styles[Style.Default].BackColor >> 16) & 0xFF); tw.WriteLine(" }"); for (int i = 0; i < styles.Length; i++) { if (!styles[i].Used) { continue; } tw.Write("span.s{0} {{", i); tw.Write(@" font-family: ""{0}"";", styles[i].FontName); tw.Write(" font-size: {0}pt;", styles[i].SizeF); tw.Write(" font-weight: {0};", styles[i].Weight); if (styles[i].Italic != 0) { tw.Write(" font-style: italic;"); } if (styles[i].Underline != 0) { tw.Write(" text-decoration: underline;"); } tw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[i].BackColor >> 0) & 0xFF, (styles[i].BackColor >> 8) & 0xFF, (styles[i].BackColor >> 16) & 0xFF); tw.Write(" color: #{0:X2}{1:X2}{2:X2};", (styles[i].ForeColor >> 0) & 0xFF, (styles[i].ForeColor >> 8) & 0xFF, (styles[i].ForeColor >> 16) & 0xFF); switch ((StyleCase)styles[i].Case) { case StyleCase.Upper: tw.Write(" text-transform: uppercase;"); break; case StyleCase.Lower: tw.Write(" text-transform: lowercase;"); break; } if (styles[i].Visible == 0) { tw.Write(" visibility: hidden;"); } tw.WriteLine(" }"); } tw.WriteLine("</style>"); tw.Write(@"<div id=""segments""><span class=""s{0}"">", Style.Default); tw.Flush(); var tabSize = scintilla.DirectMessage(NativeMethods.SCI_GETTABWIDTH).ToInt32(); var tab = new string(' ', tabSize); tw.AutoFlush = true; var lastStyle = Style.Default; var unicodeLineEndings = ((scintilla.DirectMessage(NativeMethods.SCI_GETLINEENDTYPESACTIVE).ToInt32() & NativeMethods.SC_LINE_END_TYPE_UNICODE) > 0); foreach (var seg in styledSegments) { var endOffset = seg.Offset + seg.Count; for (int i = seg.Offset; i < endOffset; i += 2) { var ch = seg.Array[i]; var style = seg.Array[i + 1]; if (lastStyle != style) { tw.Write(@"</span><span class=""s{0}"">", style); lastStyle = style; } switch (ch) { case (byte)'<': tw.Write("<"); break; case (byte)'>': tw.Write(">"); break; case (byte)'&': tw.Write("&"); break; case (byte)'\t': tw.Write(tab); break; case (byte)'\r': if (i + 2 < endOffset) { if (seg.Array[i + 2] == (byte)'\n') { i += 2; } } // Either way, this is a line break goto case (byte)'\n'; case 0xC2: if (unicodeLineEndings && i + 2 < endOffset) { if (seg.Array[i + 2] == 0x85) // NEL \u0085 { i += 2; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case 0xE2: if (unicodeLineEndings && i + 4 < endOffset) { if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA8) // LS \u2028 { i += 4; goto case (byte)'\n'; } else if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA9) // PS \u2029 { i += 4; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case (byte)'\n': // All your line breaks are belong to us tw.Write("\r\n"); break; default: if (ch == 0) { // Scintilla behavior is to allow control characters except for // NULL which will cause the Clipboard to truncate the string. tw.Write(" "); // Replace with space break; } ms.WriteByte(ch); break; } } } tw.AutoFlush = false; tw.WriteLine("</span></div>"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_END_FRAGMENT, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); tw.WriteLine("<!--EndFragment-->"); tw.WriteLine("</body>"); tw.WriteLine("</html>"); tw.Flush(); // Patch header pos = ms.Position; ms.Seek(INDEX_END_HTML, SeekOrigin.Begin); ms.Write((bytes = Encoding.ASCII.GetBytes(ms.Length.ToString("D8"))), 0, bytes.Length); ms.Seek(pos, SeekOrigin.Begin); // Terminator ms.WriteByte(0); var str = GetString(ms.Pointer, (int)ms.Length, Encoding.UTF8); if (NativeMethods.SetClipboardData(CF_HTML, ms.Pointer) != IntPtr.Zero) { ms.FreeOnDispose = false; // Clipboard will free memory } } } catch (Exception ex) { // Yes, we swallow any exceptions. That may seem like code smell but this matches // the behavior of the Clipboard class, Windows Forms controls, and native Scintilla. Debug.Fail(ex.Message, ex.ToString()); } }
private int GetIndent(ScintillaNET.Scintilla scin, int line) { return(scin.DirectMessage(SCI_GETLINEINDENTATION, new IntPtr(line), (IntPtr)null).ToInt32()); }
public void Cut() { Scintilla.DirectMessage(NativeMethods.SCI_CUT, IntPtr.Zero, IntPtr.Zero); }
private void SetIndent(ScintillaNET.Scintilla scin, int line, int indent) { scin.DirectMessage(SCI_SETLINEINDENTATION, new IntPtr(line), new IntPtr(indent)); }
private static unsafe List <ArraySegment <byte> > GetStyledSegments(Scintilla scintilla, bool currentSelection, bool currentLine, int startBytePos, int endBytePos, out StyleData[] styles) { var segments = new List <ArraySegment <byte> >(); if (currentSelection) { // Get each selection as a segment. // Rectangular selections are ordered top to bottom and have line breaks appended. var ranges = new List <Tuple <int, int> >(); var selCount = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONS).ToInt32(); for (int i = 0; i < selCount; i++) { var selStartBytePos = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONNSTART, new IntPtr(i)).ToInt32(); var selEndBytePos = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONNEND, new IntPtr(i)).ToInt32(); ranges.Add(Tuple.Create(selStartBytePos, selEndBytePos)); } var selIsRect = scintilla.DirectMessage(NativeMethods.SCI_SELECTIONISRECTANGLE) != IntPtr.Zero; if (selIsRect) { ranges.OrderBy(r => r.Item1); // Sort top to bottom } foreach (var range in ranges) { var styledText = GetStyledText(scintilla, range.Item1, range.Item2, selIsRect); segments.Add(styledText); } } else if (currentLine) { // Get the current line var mainSelection = scintilla.DirectMessage(NativeMethods.SCI_GETMAINSELECTION).ToInt32(); var mainCaretPos = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONNCARET, new IntPtr(mainSelection)).ToInt32(); var lineIndex = scintilla.DirectMessage(NativeMethods.SCI_LINEFROMPOSITION, new IntPtr(mainCaretPos)).ToInt32(); var lineStartBytePos = scintilla.DirectMessage(NativeMethods.SCI_POSITIONFROMLINE, new IntPtr(lineIndex)).ToInt32(); var lineLength = scintilla.DirectMessage(NativeMethods.SCI_POSITIONFROMLINE, new IntPtr(lineIndex)).ToInt32(); var styledText = GetStyledText(scintilla, lineStartBytePos, (lineStartBytePos + lineLength), false); segments.Add(styledText); } else // User-specified range { Debug.Assert(startBytePos != endBytePos); var styledText = GetStyledText(scintilla, startBytePos, endBytePos, false); segments.Add(styledText); } // Build a list of (used) styles styles = new StyleData[NativeMethods.STYLE_MAX + 1]; styles[Style.Default].Used = true; styles[Style.Default].FontName = scintilla.Styles[Style.Default].Font; styles[Style.Default].SizeF = scintilla.Styles[Style.Default].SizeF; styles[Style.Default].Weight = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETWEIGHT, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Italic = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETITALIC, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Underline = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETUNDERLINE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].BackColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETBACK, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].ForeColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETFORE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Case = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETCASE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); styles[Style.Default].Visible = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETVISIBLE, new IntPtr(Style.Default), IntPtr.Zero).ToInt32(); foreach (var seg in segments) { for (int i = 0; i < seg.Count; i += 2) { var style = seg.Array[i + 1]; if (!styles[style].Used) { styles[style].Used = true; styles[style].FontName = scintilla.Styles[style].Font; styles[style].SizeF = scintilla.Styles[style].SizeF; styles[style].Weight = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETWEIGHT, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Italic = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETITALIC, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Underline = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETUNDERLINE, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].BackColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETBACK, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].ForeColor = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETFORE, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Case = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETCASE, new IntPtr(style), IntPtr.Zero).ToInt32(); styles[style].Visible = scintilla.DirectMessage(NativeMethods.SCI_STYLEGETVISIBLE, new IntPtr(style), IntPtr.Zero).ToInt32(); } } } return(segments); }
private static unsafe void CopyRtf(Scintilla scintilla, StyleData[] styles, List <ArraySegment <byte> > styledSegments) { // NppExport -> NppExport.cpp // NppExport -> RTFExporter.cpp // http://en.wikipedia.org/wiki/Rich_Text_Format // https://msdn.microsoft.com/en-us/library/windows/desktop/ms649013.aspx // http://forums.codeguru.com/showthread.php?242982-Converting-pixels-to-twips // http://en.wikipedia.org/wiki/UTF-8 try { // Calculate twips per space int twips; var fontStyle = FontStyle.Regular; if (styles[Style.Default].Weight >= 700) { fontStyle |= FontStyle.Bold; } if (styles[Style.Default].Italic != 0) { fontStyle |= FontStyle.Italic; } if (styles[Style.Default].Underline != 0) { fontStyle |= FontStyle.Underline; } using (var graphics = scintilla.CreateGraphics()) using (var font = new Font(styles[Style.Default].FontName, styles[Style.Default].SizeF, fontStyle)) { var width = graphics.MeasureString(" ", font).Width; twips = (int)((width / graphics.DpiX) * 1440); // TODO The twips value calculated seems too small on my computer } // Write RTF using (var ms = new NativeMemoryStream(styledSegments.Sum(s => s.Count))) using (var tw = new StreamWriter(ms, Encoding.ASCII)) { var tabWidth = scintilla.DirectMessage(NativeMethods.SCI_GETTABWIDTH).ToInt32(); var deftab = tabWidth * twips; tw.WriteLine(@"{{\rtf1\ansi\deff0\deftab{0}", deftab); tw.Flush(); // Build the font table tw.Write(@"{\fonttbl"); tw.Write(@"{{\f0 {0};}}", styles[Style.Default].FontName); var fontIndex = 1; for (int i = 0; i < styles.Length; i++) { if (!styles[i].Used) { continue; } if (i == Style.Default) { continue; } // Not a completely unique list, but close enough if (styles[i].FontName != styles[Style.Default].FontName) { styles[i].FontIndex = fontIndex++; tw.Write(@"{{\f{0} {1};}}", styles[i].FontIndex, styles[i].FontName); } } tw.WriteLine("}"); // fonttbl tw.Flush(); // Build the color table tw.Write(@"{\colortbl"); tw.Write(@"\red{0}\green{1}\blue{2};", (styles[Style.Default].ForeColor >> 0) & 0xFF, (styles[Style.Default].ForeColor >> 8) & 0xFF, (styles[Style.Default].ForeColor >> 16) & 0xFF); tw.Write(@"\red{0}\green{1}\blue{2};", (styles[Style.Default].BackColor >> 0) & 0xFF, (styles[Style.Default].BackColor >> 8) & 0xFF, (styles[Style.Default].BackColor >> 16) & 0xFF); styles[Style.Default].ForeColorIndex = 0; styles[Style.Default].BackColorIndex = 1; var colorIndex = 2; for (int i = 0; i < styles.Length; i++) { if (!styles[i].Used) { continue; } if (i == Style.Default) { continue; } // Not a completely unique list, but close enough if (styles[i].ForeColor != styles[Style.Default].ForeColor) { styles[i].ForeColorIndex = colorIndex++; tw.Write(@"\red{0}\green{1}\blue{2};", (styles[i].ForeColor >> 0) & 0xFF, (styles[i].ForeColor >> 8) & 0xFF, (styles[i].ForeColor >> 16) & 0xFF); } else { styles[i].ForeColorIndex = styles[Style.Default].ForeColorIndex; } if (styles[i].BackColor != styles[Style.Default].BackColor) { styles[i].BackColorIndex = colorIndex++; tw.Write(@"\red{0}\green{1}\blue{2};", (styles[i].BackColor >> 0) & 0xFF, (styles[i].BackColor >> 8) & 0xFF, (styles[i].BackColor >> 16) & 0xFF); } else { styles[i].BackColorIndex = styles[Style.Default].BackColorIndex; } } tw.WriteLine("}"); // colortbl tw.Flush(); // Start with the default style tw.Write(@"\f{0}\fs{1}\cf{2}\chshdng0\chcbpat{3}\cb{3} ", styles[Style.Default].FontIndex, (int)(styles[Style.Default].SizeF * 2), styles[Style.Default].ForeColorIndex, styles[Style.Default].BackColorIndex); if (styles[Style.Default].Italic != 0) { tw.Write(@"\i"); } if (styles[Style.Default].Underline != 0) { tw.Write(@"\ul"); } if (styles[Style.Default].Weight >= 700) { tw.Write(@"\b"); } tw.AutoFlush = true; var lastStyle = Style.Default; var unicodeLineEndings = ((scintilla.DirectMessage(NativeMethods.SCI_GETLINEENDTYPESACTIVE).ToInt32() & NativeMethods.SC_LINE_END_TYPE_UNICODE) > 0); foreach (var seg in styledSegments) { var endOffset = seg.Offset + seg.Count; for (int i = seg.Offset; i < endOffset; i += 2) { var ch = seg.Array[i]; var style = seg.Array[i + 1]; if (lastStyle != style) { // Change the style if (styles[lastStyle].FontIndex != styles[style].FontIndex) { tw.Write(@"\f{0}", styles[style].FontIndex); } if (styles[lastStyle].SizeF != styles[style].SizeF) { tw.Write(@"\fs{0}", (int)(styles[style].SizeF * 2)); } if (styles[lastStyle].ForeColorIndex != styles[style].ForeColorIndex) { tw.Write(@"\cf{0}", styles[style].ForeColorIndex); } if (styles[lastStyle].BackColorIndex != styles[style].BackColorIndex) { tw.Write(@"\chshdng0\chcbpat{0}\cb{0}", styles[style].BackColorIndex); } if (styles[lastStyle].Italic != styles[style].Italic) { tw.Write(@"\i{0}", styles[style].Italic != 0 ? "" : "0"); } if (styles[lastStyle].Underline != styles[style].Underline) { tw.Write(@"\ul{0}", styles[style].Underline != 0 ? "" : "0"); } if (styles[lastStyle].Weight != styles[style].Weight) { if (styles[style].Weight >= 700 && styles[lastStyle].Weight < 700) { tw.Write(@"\b"); } else if (styles[style].Weight < 700 && styles[lastStyle].Weight >= 700) { tw.Write(@"\b0"); } } // NOTE: We don't support StyleData.Visible and StyleData.Case in RTF lastStyle = style; tw.Write(" "); // Delimiter } switch (ch) { case (byte)'{': tw.Write(@"\{"); break; case (byte)'}': tw.Write(@"\}"); break; case (byte)'\\': tw.Write(@"\\"); break; case (byte)'\t': tw.Write(@"\tab "); break; case (byte)'\r': if (i + 2 < endOffset) { if (seg.Array[i + 2] == (byte)'\n') { i += 2; } } // Either way, this is a line break goto case (byte)'\n'; case 0xC2: if (unicodeLineEndings && i + 2 < endOffset) { if (seg.Array[i + 2] == 0x85) // NEL \u0085 { i += 2; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case 0xE2: if (unicodeLineEndings && i + 4 < endOffset) { if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA8) // LS \u2028 { i += 4; goto case (byte)'\n'; } else if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA9) // PS \u2029 { i += 4; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case (byte)'\n': // All your line breaks are belong to us tw.WriteLine(@"\par"); break; default: if (ch == 0) { // Scintilla behavior is to allow control characters except for // NULL which will cause the Clipboard to truncate the string. tw.Write(" "); // Replace with space break; } if (ch > 0x7F) { // Treat as UTF-8 code point int unicode = 0; if (ch < 0xE0 && i + 2 < endOffset) { unicode |= ((0x1F & ch) << 6); unicode |= (0x3F & seg.Array[i + 2]); tw.Write(@"\u{0}?", unicode); i += 2; break; } else if (ch < 0xF0 && i + 4 < endOffset) { unicode |= ((0xF & ch) << 12); unicode |= ((0x3F & seg.Array[i + 2]) << 6); unicode |= (0x3F & seg.Array[i + 4]); tw.Write(@"\u{0}?", unicode); i += 4; break; } else if (ch < 0xF8 && i + 6 < endOffset) { unicode |= ((0x7 & ch) << 18); unicode |= ((0x3F & seg.Array[i + 2]) << 12); unicode |= ((0x3F & seg.Array[i + 4]) << 6); unicode |= (0x3F & seg.Array[i + 6]); tw.Write(@"\u{0}?", unicode); i += 6; break; } } // Regular ANSI char ms.WriteByte(ch); break; } } } tw.AutoFlush = false; tw.WriteLine("}"); // rtf1 tw.Flush(); // Terminator ms.WriteByte(0); // var str = GetString(ms.Pointer, (int)ms.Length, Encoding.ASCII); if (NativeMethods.SetClipboardData(CF_RTF, ms.Pointer) != IntPtr.Zero) { ms.FreeOnDispose = false; // Clipboard will free memory } } } catch (Exception ex) { // Yes, we swallow any exceptions. That may seem like code smell but this matches // the behavior of the Clipboard class, Windows Forms controls, and native Scintilla. Debug.Fail(ex.Message, ex.ToString()); } }
private static unsafe ArraySegment<byte> GetStyledText(Scintilla scintilla, int startBytePos, int endBytePos, bool addLineBreak) { Debug.Assert(endBytePos > startBytePos); // Make sure the range is styled scintilla.DirectMessage(NativeMethods.SCI_COLOURISE, new IntPtr(startBytePos), new IntPtr(endBytePos)); var byteLength = (endBytePos - startBytePos); var buffer = new byte[(byteLength * 2) + (addLineBreak ? 4 : 0) + 2]; fixed (byte* bp = buffer) { NativeMethods.Sci_TextRange* tr = stackalloc NativeMethods.Sci_TextRange[1]; tr->chrg.cpMin = startBytePos; tr->chrg.cpMax = endBytePos; tr->lpstrText = new IntPtr(bp); scintilla.DirectMessage(NativeMethods.SCI_GETSTYLEDTEXT, IntPtr.Zero, new IntPtr(tr)); byteLength *= 2; } // Add a line break? // We do this when this range is part of a rectangular selection. if (addLineBreak) { var style = buffer[byteLength - 1]; buffer[byteLength++] = (byte)'\r'; buffer[byteLength++] = style; buffer[byteLength++] = (byte)'\n'; buffer[byteLength++] = style; // Fix-up the NULL terminator just in case buffer[byteLength] = 0; buffer[byteLength + 1] = 0; } return new ArraySegment<byte>(buffer, 0, byteLength); }
public static string GetHtml(Scintilla scintilla, int startBytePos, int endBytePos) { // If we ever allow more than UTF-8, this will have to be revisited Debug.Assert(scintilla.DirectMessage(NativeMethods.SCI_GETCODEPAGE).ToInt32() == NativeMethods.SC_CP_UTF8); if (startBytePos == endBytePos) return string.Empty; StyleData[] styles = null; List<ArraySegment<byte>> styledSegments = GetStyledSegments(scintilla, false, false, startBytePos, endBytePos, out styles); using (var ms = new NativeMemoryStream(styledSegments.Sum(s => s.Count))) // Hint using (var sw = new StreamWriter(ms, new UTF8Encoding(false))) { // Write the styles sw.WriteLine(@"<style type=""text/css"" scoped="""">"); sw.Write("div#segments {"); sw.Write(" float: left;"); sw.Write(" white-space: pre;"); sw.Write(" line-height: {0}px;", scintilla.DirectMessage(NativeMethods.SCI_TEXTHEIGHT, new IntPtr(0)).ToInt32()); sw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[Style.Default].BackColor >> 0) & 0xFF, (styles[Style.Default].BackColor >> 8) & 0xFF, (styles[Style.Default].BackColor >> 16) & 0xFF); sw.WriteLine(" }"); for (int i = 0; i < styles.Length; i++) { if (!styles[i].Used) continue; sw.Write("span.s{0} {{", i); sw.Write(@" font-family: ""{0}"";", styles[i].FontName); sw.Write(" font-size: {0}pt;", styles[i].SizeF); sw.Write(" font-weight: {0};", styles[i].Weight); if (styles[i].Italic != 0) sw.Write(" font-style: italic;"); if (styles[i].Underline != 0) sw.Write(" text-decoration: underline;"); sw.Write(" background-color: #{0:X2}{1:X2}{2:X2};", (styles[i].BackColor >> 0) & 0xFF, (styles[i].BackColor >> 8) & 0xFF, (styles[i].BackColor >> 16) & 0xFF); sw.Write(" color: #{0:X2}{1:X2}{2:X2};", (styles[i].ForeColor >> 0) & 0xFF, (styles[i].ForeColor >> 8) & 0xFF, (styles[i].ForeColor >> 16) & 0xFF); switch ((StyleCase)styles[i].Case) { case StyleCase.Upper: sw.Write(" text-transform: uppercase;"); break; case StyleCase.Lower: sw.Write(" text-transform: lowercase;"); break; } if (styles[i].Visible == 0) sw.Write(" visibility: hidden;"); sw.WriteLine(" }"); } sw.WriteLine("</style>"); var unicodeLineEndings = ((scintilla.DirectMessage(NativeMethods.SCI_GETLINEENDTYPESACTIVE).ToInt32() & NativeMethods.SC_LINE_END_TYPE_UNICODE) > 0); var tabSize = scintilla.DirectMessage(NativeMethods.SCI_GETTABWIDTH).ToInt32(); var tab = new string(' ', tabSize); var lastStyle = Style.Default; // Write the styled text sw.Write(@"<div id=""segments""><span class=""s{0}"">", Style.Default); sw.Flush(); sw.AutoFlush = true; foreach (var seg in styledSegments) { var endOffset = seg.Offset + seg.Count; for (int i = seg.Offset; i < endOffset; i += 2) { var ch = seg.Array[i]; var style = seg.Array[i + 1]; if (lastStyle != style) { sw.Write(@"</span><span class=""s{0}"">", style); lastStyle = style; } switch (ch) { case (byte)'<': sw.Write("<"); break; case (byte)'>': sw.Write(">"); break; case (byte)'&': sw.Write("&"); break; case (byte)'\t': sw.Write(tab); break; case (byte)'\r': if (i + 2 < endOffset) { if (seg.Array[i + 2] == (byte)'\n') i += 2; } // Either way, this is a line break goto case (byte)'\n'; case 0xC2: if (unicodeLineEndings && i + 2 < endOffset) { if (seg.Array[i + 2] == 0x85) // NEL \u0085 { i += 2; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case 0xE2: if (unicodeLineEndings && i + 4 < endOffset) { if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA8) // LS \u2028 { i += 4; goto case (byte)'\n'; } else if (seg.Array[i + 2] == 0x80 && seg.Array[i + 4] == 0xA9) // PS \u2029 { i += 4; goto case (byte)'\n'; } } // Not a Unicode line break goto default; case (byte)'\n': // All your line breaks are belong to us sw.Write("\r\n"); break; default: if (ch == 0) { // Replace NUL with space sw.Write(" "); break; } ms.WriteByte(ch); break; } } } sw.AutoFlush = false; sw.WriteLine("</span></div>"); sw.Flush(); return GetString(ms.Pointer, (int)ms.Length, Encoding.UTF8); } }
/// <summary> /// Expands any parent folds to ensure the line is visible. /// </summary> public void EnsureVisible() { scintilla.DirectMessage(NativeMethods.SCI_ENSUREVISIBLE, new IntPtr(Index)); }
public static void Copy(Scintilla scintilla, CopyFormat format, bool useSelection, bool allowLine, int startBytePos, int endBytePos) { // Plain text if ((format & CopyFormat.Text) > 0) { if (useSelection) { if (allowLine) { scintilla.DirectMessage(NativeMethods.SCI_COPYALLOWLINE); } else { scintilla.DirectMessage(NativeMethods.SCI_COPY); } } else { scintilla.DirectMessage(NativeMethods.SCI_COPYRANGE, new IntPtr(startBytePos), new IntPtr(endBytePos)); } } // RTF and/or HTML if ((format & (CopyFormat.Rtf | CopyFormat.Html)) > 0) { // If we ever allow more than UTF-8, this will have to be revisited Debug.Assert(scintilla.DirectMessage(NativeMethods.SCI_GETCODEPAGE).ToInt32() == NativeMethods.SC_CP_UTF8); if (!registeredFormats) { // Register non-standard clipboard formats. // Scintilla -> ScintillaWin.cxx // NppExport -> HTMLExporter.h // NppExport -> RTFExporter.h CF_LINESELECT = NativeMethods.RegisterClipboardFormat("MSDEVLineSelect"); CF_VSLINETAG = NativeMethods.RegisterClipboardFormat("VisualStudioEditorOperationsLineCutCopyClipboardTag"); CF_HTML = NativeMethods.RegisterClipboardFormat("HTML Format"); CF_RTF = NativeMethods.RegisterClipboardFormat("Rich Text Format"); registeredFormats = true; } var lineCopy = false; StyleData[] styles = null; List <ArraySegment <byte> > styledSegments = null; if (useSelection) { var selIsEmpty = scintilla.DirectMessage(NativeMethods.SCI_GETSELECTIONEMPTY) != IntPtr.Zero; if (selIsEmpty) { if (allowLine) { // Get the current line styledSegments = GetStyledSegments(scintilla, false, true, 0, 0, out styles); lineCopy = true; } } else { // Get every selection styledSegments = GetStyledSegments(scintilla, true, false, 0, 0, out styles); } } else if (startBytePos != endBytePos) { // User-specified range styledSegments = GetStyledSegments(scintilla, false, false, startBytePos, endBytePos, out styles); } // If we have segments and can open the clipboard if (styledSegments != null && styledSegments.Count > 0 && NativeMethods.OpenClipboard(scintilla.Handle)) { if ((format & CopyFormat.Text) == 0) { // Do the things default (plain text) processing would normally give us NativeMethods.EmptyClipboard(); if (lineCopy) { // Clipboard tags NativeMethods.SetClipboardData(CF_LINESELECT, IntPtr.Zero); NativeMethods.SetClipboardData(CF_VSLINETAG, IntPtr.Zero); } } // RTF if ((format & CopyFormat.Rtf) > 0) { CopyRtf(scintilla, styles, styledSegments); } // HTML if ((format & CopyFormat.Html) > 0) { CopyHtml(scintilla, styles, styledSegments); } NativeMethods.CloseClipboard(); } } }
public void Copy(int startPosition, int endPosition) { Scintilla.DirectMessage(NativeMethods.SCI_COPYRANGE, new IntPtr(startPosition), new IntPtr(endPosition)); }
/// <summary> /// Navigates the caret to the start of the line. /// </summary> /// <remarks>Any selection is discarded.</remarks> public void Goto() { scintilla.DirectMessage(NativeMethods.SCI_GOTOLINE, new IntPtr(Index)); }
public void Paste() { Scintilla.DirectMessage(NativeMethods.SCI_PASTE, IntPtr.Zero, IntPtr.Zero); }
public void Join(int startLine, int endLine) { NativeScintilla.SetTargetStart(startLine); NativeScintilla.SetTargetEnd(endLine); Scintilla.DirectMessage(NativeMethods.SCI_LINESJOIN, IntPtr.Zero, IntPtr.Zero); }