mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
make richCopy functionality work properly on Linux (IDEA-67767)
This commit is contained in:
@@ -56,15 +56,15 @@ public class CopyHandler extends EditorActionHandler {
|
||||
|
||||
final CodeInsightSettings settings = CodeInsightSettings.getInstance();
|
||||
|
||||
if (file == null || settings.ADD_IMPORTS_ON_PASTE == CodeInsightSettings.NO){
|
||||
if (myOriginalAction != null){
|
||||
if (file == null || settings.ADD_IMPORTS_ON_PASTE == CodeInsightSettings.NO) {
|
||||
if (myOriginalAction != null) {
|
||||
myOriginalAction.execute(editor, dataContext);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final SelectionModel selectionModel = editor.getSelectionModel();
|
||||
if(!selectionModel.hasSelection(true) && !selectionModel.hasBlockSelection()) {
|
||||
if (!selectionModel.hasSelection(true) && !selectionModel.hasBlockSelection()) {
|
||||
if (Registry.is(CopyAction.SKIP_COPY_AND_CUT_FOR_EMPTY_SELECTION_KEY)) {
|
||||
return;
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class CopyHandler extends EditorActionHandler {
|
||||
final int[] endOffsets = selectionModel.getBlockSelectionEnds();
|
||||
|
||||
List<TextBlockTransferableData> transferableDatas = new ArrayList<TextBlockTransferableData>();
|
||||
for(CopyPastePostProcessor processor: Extensions.getExtensions(CopyPastePostProcessor.EP_NAME)) {
|
||||
for (CopyPastePostProcessor processor : Extensions.getExtensions(CopyPastePostProcessor.EP_NAME)) {
|
||||
final TextBlockTransferableData e = processor.collectTransferableData(file, editor, startOffsets, endOffsets);
|
||||
if (e != null) {
|
||||
transferableDatas.add(e);
|
||||
@@ -98,7 +98,7 @@ public class CopyHandler extends EditorActionHandler {
|
||||
|
||||
String rawText = TextBlockTransferable.convertLineSeparators(selectionModel.getSelectedText(true), "\n", transferableDatas);
|
||||
String escapedText = null;
|
||||
for(CopyPastePreProcessor processor: Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) {
|
||||
for (CopyPastePreProcessor processor : Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) {
|
||||
escapedText = processor.preprocessOnCopy(file, startOffsets, endOffsets, rawText);
|
||||
if (escapedText != null) {
|
||||
break;
|
||||
|
||||
+17
-16
@@ -27,37 +27,38 @@ import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class TextBlockTransferable implements Transferable {
|
||||
private final Collection<TextBlockTransferableData> myExtraData;
|
||||
private final RawText myRawText;
|
||||
private final String myText;
|
||||
private final DataFlavor[] myTransferDataFlavors;
|
||||
|
||||
public TextBlockTransferable(String text, Collection<TextBlockTransferableData> extraData, RawText rawText) {
|
||||
myText = text;
|
||||
myExtraData = extraData;
|
||||
myRawText = rawText;
|
||||
|
||||
List<DataFlavor> dataFlavors = new ArrayList<DataFlavor>();
|
||||
Collections.addAll(dataFlavors, DataFlavor.stringFlavor, DataFlavor.plainTextFlavor);
|
||||
final DataFlavor flavor = RawText.getDataFlavor();
|
||||
if (myRawText != null && flavor != null) {
|
||||
dataFlavors.add(flavor);
|
||||
}
|
||||
for(TextBlockTransferableData data: extraData) {
|
||||
final DataFlavor blockFlavor = data.getFlavor();
|
||||
if (blockFlavor != null) {
|
||||
dataFlavors.add(blockFlavor);
|
||||
}
|
||||
}
|
||||
myTransferDataFlavors = dataFlavors.toArray(new DataFlavor[dataFlavors.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
// We don't cache the result to allow certain dataflavors to 'expire'.
|
||||
// This is used e.g. for RTF and HTML flavors to avoid memory leaking.
|
||||
List<DataFlavor> flavors = new ArrayList<DataFlavor>(myExtraData.size() + 3);
|
||||
flavors.add(DataFlavor.stringFlavor);
|
||||
flavors.add(DataFlavor.plainTextFlavor);
|
||||
final DataFlavor flavor = RawText.getDataFlavor();
|
||||
if (myRawText != null && flavor != null) {
|
||||
flavors.add(flavor);
|
||||
}
|
||||
for(TextBlockTransferableData data : myExtraData) {
|
||||
final DataFlavor blockFlavor = data.getFlavor();
|
||||
if (blockFlavor != null) {
|
||||
flavors.add(blockFlavor);
|
||||
}
|
||||
}
|
||||
return flavors.toArray(new DataFlavor[flavors.size()]);
|
||||
return myTransferDataFlavors;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+31
-62
@@ -16,32 +16,24 @@
|
||||
package com.intellij.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor;
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePreProcessor;
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.RawText;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.openapi.editor.richcopy.view.RawTextHolder;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
|
||||
public abstract class BaseTextWithMarkupCopyPasteProcessor<T extends TextBlockTransferableData> implements CopyPastePostProcessor<T>, TextWithMarkupBuilder {
|
||||
private static final Logger LOG = Logger.getInstance("#" + BaseTextWithMarkupCopyPasteProcessor.class.getName());
|
||||
private static final String TRUNCATED_MESSAGE = "... truncated ...";
|
||||
|
||||
public abstract class BaseTextWithMarkupCopyPasteProcessor<T extends RawTextHolder & TextBlockTransferableData>
|
||||
implements CopyPastePostProcessor<T>, TextWithMarkupBuilder {
|
||||
private T myData;
|
||||
private int mySizeLimit;
|
||||
protected StringBuilder myBuilder;
|
||||
protected String myDefaultFontFamily;
|
||||
protected Color myDefaultForeground;
|
||||
protected Color myDefaultBackground;
|
||||
protected int myFontSize;
|
||||
|
||||
protected BaseTextWithMarkupCopyPasteProcessor(TextWithMarkupProcessor processor) {
|
||||
processor.addBuilder(this);
|
||||
@@ -53,10 +45,7 @@ public abstract class BaseTextWithMarkupCopyPasteProcessor<T extends TextBlockTr
|
||||
if (!Registry.is("editor.richcopy.enable")) {
|
||||
return null;
|
||||
}
|
||||
releaseBuilder(); // to avoid leakage in case 'complete' method wasn't called due to some exception
|
||||
T data = myData;
|
||||
myData = null;
|
||||
return data;
|
||||
return myData;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -76,58 +65,38 @@ public abstract class BaseTextWithMarkupCopyPasteProcessor<T extends TextBlockTr
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Color defaultForeground, Color defaultBackground, String defaultFontFamily, int fontSize) {
|
||||
public void build(SyntaxInfo syntaxInfo) {
|
||||
myData = doBuild(syntaxInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
myData = null;
|
||||
myDefaultForeground = defaultForeground;
|
||||
myDefaultBackground = defaultBackground;
|
||||
myDefaultFontFamily = defaultFontFamily;
|
||||
myFontSize = fontSize;
|
||||
allocateBuilder();
|
||||
mySizeLimit = Registry.intValue("editor.richcopy.max.size.megabytes") * 1048576;
|
||||
doInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowed() {
|
||||
boolean overflowed = myBuilder.length() > mySizeLimit;
|
||||
if (overflowed) {
|
||||
setFontFamily(myDefaultFontFamily);
|
||||
setForeground(myDefaultForeground);
|
||||
setBackground(myDefaultBackground);
|
||||
addTextFragment(TRUNCATED_MESSAGE, 0, TRUNCATED_MESSAGE.length());
|
||||
protected abstract T doBuild(SyntaxInfo info);
|
||||
|
||||
public static class RawTextSetter implements CopyPastePreProcessor {
|
||||
private final BaseTextWithMarkupCopyPasteProcessor myProcessor;
|
||||
|
||||
public RawTextSetter(BaseTextWithMarkupCopyPasteProcessor processor) {
|
||||
myProcessor = processor;
|
||||
}
|
||||
return overflowed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete() {
|
||||
try {
|
||||
doComplete();
|
||||
String data = myBuilder.toString();
|
||||
if (Registry.is("editor.richcopy.debug")) {
|
||||
LOG.info("Resulting text: \n" + data);
|
||||
@Override
|
||||
@Nullable
|
||||
public String preprocessOnCopy(final PsiFile file, final int[] startOffsets, final int[] endOffsets, String text) {
|
||||
if (myProcessor.myData != null) {
|
||||
myProcessor.myData.setRawText(text);
|
||||
myProcessor.myData = null;
|
||||
}
|
||||
myData = createTransferable(data);
|
||||
return null; // noop
|
||||
}
|
||||
finally {
|
||||
releaseBuilder();
|
||||
|
||||
@Override
|
||||
public String preprocessOnPaste(final Project project, final PsiFile file, final Editor editor, String text, final RawText rawText) {
|
||||
return text; // noop
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract T createTransferable(@NotNull String data);
|
||||
protected abstract void doInit();
|
||||
protected abstract void doComplete();
|
||||
|
||||
private void allocateBuilder() {
|
||||
if (myBuilder == null) {
|
||||
myBuilder = StringBuilderSpinAllocator.alloc();
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseBuilder() {
|
||||
if (myBuilder != null) {
|
||||
StringBuilderSpinAllocator.dispose(myBuilder);
|
||||
myBuilder = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-166
@@ -15,184 +15,27 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.editor.richcopy.view.ReaderTransferableData;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.openapi.editor.richcopy.view.HtmlTransferableData;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 1:05 PM
|
||||
*/
|
||||
public class HtmlCopyPasteProcessor extends BaseTextWithMarkupCopyPasteProcessor<ReaderTransferableData> {
|
||||
@NotNull public static final DataFlavor FLAVOR = new DataFlavor("text/html;class=java.io.Reader", "HTML text");
|
||||
|
||||
private Map<Color, String> myRenderedColors = new HashMap<Color, String>();
|
||||
|
||||
private Color myForeground;
|
||||
private Color myBackground;
|
||||
private String myFontFamily;
|
||||
private boolean myBold;
|
||||
private boolean myItalic;
|
||||
|
||||
public class HtmlCopyPasteProcessor extends BaseTextWithMarkupCopyPasteProcessor<HtmlTransferableData> {
|
||||
public HtmlCopyPasteProcessor(TextWithMarkupProcessor processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
myForeground = null;
|
||||
myBackground = null;
|
||||
myFontFamily = null;
|
||||
myBold = false;
|
||||
myItalic = false;
|
||||
myBuilder.append("<div style=\"border:1px inset;padding:2%;\"><pre style=\"margin:0;padding:6px;background-color:");
|
||||
appendColor(myBuilder, myDefaultBackground);
|
||||
myBuilder.append(';');
|
||||
appendFontFamilyRule(myBuilder, myDefaultFontFamily);
|
||||
myBuilder.append("font-size:").append(myFontSize).append("pt;");
|
||||
myBuilder.append("\" bgcolor=\"");
|
||||
appendColor(myBuilder, myDefaultBackground);
|
||||
myBuilder.append("\">");
|
||||
protected HtmlTransferableData doBuild(SyntaxInfo info) {
|
||||
return new HtmlTransferableData(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doComplete() {
|
||||
myBuilder.append("</pre></div>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontFamily(String fontFamily) {
|
||||
myFontFamily = myDefaultFontFamily.equals(fontFamily) ? null : fontFamily;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontStyle(int fontStyle) {
|
||||
myBold = (Font.BOLD & fontStyle) != 0;
|
||||
myItalic = (Font.ITALIC & fontStyle) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForeground(Color foreground) {
|
||||
myForeground = foreground;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackground(Color background) {
|
||||
myBackground = myDefaultBackground.equals(background) ? null : background;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTextFragment(CharSequence charSequence, int startOffset, int endOffset) {
|
||||
boolean formattedText = myForeground != null || myBackground != null || myFontFamily != null || myBold || myItalic;
|
||||
if (!formattedText) {
|
||||
escapeAndAdd(charSequence, startOffset, endOffset);
|
||||
return;
|
||||
@SuppressWarnings("ClassNameSameAsAncestorName")
|
||||
public static class RawTextSetter extends BaseTextWithMarkupCopyPasteProcessor.RawTextSetter {
|
||||
public RawTextSetter(HtmlCopyPasteProcessor processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
StringBuilder styleBuffer = StringBuilderSpinAllocator.alloc();
|
||||
StringBuilder closeTagBuffer = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
if (myForeground != null) {
|
||||
defineForeground(myForeground, styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myBackground != null) {
|
||||
defineBackground(myBackground, styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myBold) {
|
||||
defineBold(styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myItalic) {
|
||||
defineItalic(styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myFontFamily != null) {
|
||||
appendFontFamilyRule(styleBuffer, myFontFamily);
|
||||
}
|
||||
myBuilder.append("<span style=\"");
|
||||
myBuilder.append(styleBuffer);
|
||||
myBuilder.append("\">");
|
||||
escapeAndAdd(charSequence, startOffset, endOffset);
|
||||
myBuilder.append("</span>");
|
||||
myBuilder.append(closeTagBuffer);
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(styleBuffer);
|
||||
StringBuilderSpinAllocator.dispose(closeTagBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void defineForeground(Color foreground, @NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myBuilder.append("<font color=\"");
|
||||
appendColor(myBuilder, foreground);
|
||||
myBuilder.append("\">");
|
||||
styleBuffer.append("color:");
|
||||
appendColor(styleBuffer, foreground);
|
||||
styleBuffer.append(";");
|
||||
closeTagBuffer.insert(0, "</font>");
|
||||
}
|
||||
|
||||
private void defineBackground(Color background, @NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myBuilder.append("<font bgcolor=\"");
|
||||
appendColor(myBuilder, background);
|
||||
myBuilder.append("\">");
|
||||
styleBuffer.append("background-color:");
|
||||
appendColor(styleBuffer, background);
|
||||
styleBuffer.append(";");
|
||||
closeTagBuffer.insert(0, "</font>");
|
||||
}
|
||||
|
||||
private void defineBold(@NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myBuilder.append("<b>");
|
||||
styleBuffer.append("font-weight:bold;");
|
||||
closeTagBuffer.insert(0, "</b>");
|
||||
}
|
||||
|
||||
private void defineItalic(@NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myBuilder.append("<i>");
|
||||
styleBuffer.append("font-style:italic;");
|
||||
closeTagBuffer.insert(0, "</i>");
|
||||
}
|
||||
|
||||
private void appendColor(StringBuilder builder, Color color) {
|
||||
String colorAsString = myRenderedColors.get(color);
|
||||
if (colorAsString == null) {
|
||||
StringBuilder b = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
b.append('#');
|
||||
UIUtil.appendColor(color, b);
|
||||
colorAsString = b.toString();
|
||||
myRenderedColors.put(color, colorAsString);
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(b);
|
||||
}
|
||||
}
|
||||
builder.append(colorAsString);
|
||||
}
|
||||
|
||||
private static void appendFontFamilyRule(@NotNull StringBuilder styleBuffer, String fontFamily) {
|
||||
styleBuffer.append("font-family:'").append(fontFamily).append("';");
|
||||
}
|
||||
|
||||
private void escapeAndAdd(CharSequence charSequence, int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = charSequence.charAt(i);
|
||||
switch (c) {
|
||||
case '<': myBuilder.append("<"); break;
|
||||
case '>': myBuilder.append(">"); break;
|
||||
case '&': myBuilder.append("&"); break;
|
||||
default: myBuilder.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReaderTransferableData createTransferable(@NotNull String data) {
|
||||
return new ReaderTransferableData(data, FLAVOR);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-166
@@ -15,184 +15,27 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.editor.richcopy.model.*;
|
||||
import com.intellij.openapi.editor.richcopy.view.InputStreamTransferableData;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.openapi.editor.richcopy.view.RtfTransferableData;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/25/13 2:18 PM
|
||||
*/
|
||||
public class RtfCopyPasteProcessor extends BaseTextWithMarkupCopyPasteProcessor<InputStreamTransferableData> {
|
||||
@NotNull public static final DataFlavor FLAVOR = new DataFlavor("text/rtf;class=java.io.InputStream", "RTF text");
|
||||
|
||||
private static final String CHARSET = "US-ASCII";
|
||||
|
||||
@NotNull private static final String HEADER_PREFIX = "{\\rtf1\\ansi\\deff0";
|
||||
@NotNull private static final String HEADER_SUFFIX = "}";
|
||||
@NotNull private static final String TAB = "\\tab\n";
|
||||
@NotNull private static final String NEW_LINE = "\\line\n";
|
||||
@NotNull private static final String BOLD = "\\b";
|
||||
@NotNull private static final String ITALIC = "\\i";
|
||||
@NotNull private static final String PLAIN = "\\plain\n";
|
||||
|
||||
private int myBackgroundId;
|
||||
private int myForegroundId;
|
||||
private int myFontNameId;
|
||||
private ColorRegistry myColorRegistry;
|
||||
private FontNameRegistry myFontNameRegistry;
|
||||
|
||||
public class RtfCopyPasteProcessor extends BaseTextWithMarkupCopyPasteProcessor<RtfTransferableData> {
|
||||
public RtfCopyPasteProcessor(TextWithMarkupProcessor processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
myBackgroundId = -1;
|
||||
myForegroundId = -1;
|
||||
myFontNameId = -1;
|
||||
myColorRegistry = new ColorRegistry();
|
||||
myFontNameRegistry = new FontNameRegistry();
|
||||
|
||||
int defaultForegroundId = myColorRegistry.getId(myDefaultForeground);
|
||||
int defaultBackgroundId = myColorRegistry.getId(myDefaultBackground);
|
||||
|
||||
myBuilder.append("\n\\s0\\box\\brdrhair\\brdrcf").append(defaultForegroundId).append("\\brsp317").append("\\cbpat").append(defaultBackgroundId);
|
||||
saveBackground(defaultBackgroundId);
|
||||
protected RtfTransferableData doBuild(SyntaxInfo info) {
|
||||
return new RtfTransferableData(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doComplete() {
|
||||
StringBuilder builder = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
builder.append(HEADER_PREFIX);
|
||||
|
||||
// Color table.
|
||||
builder.append("{\\colortbl;");
|
||||
for (int id : myColorRegistry.getAllIds()) {
|
||||
Color color = myColorRegistry.dataById(id);
|
||||
builder.append(String.format("\\red%d\\green%d\\blue%d;", color.getRed(), color.getGreen(), color.getBlue()));
|
||||
}
|
||||
builder.append("}\n");
|
||||
|
||||
// Font table.
|
||||
builder.append("{\\fonttbl");
|
||||
for (int id : myFontNameRegistry.getAllIds()) {
|
||||
String fontName = myFontNameRegistry.dataById(id);
|
||||
builder.append(String.format("{\\f%d %s;}", id, fontName));
|
||||
}
|
||||
builder.append("}\n");
|
||||
|
||||
myBuilder.insert(0, builder);
|
||||
@SuppressWarnings("ClassNameSameAsAncestorName")
|
||||
public static class RawTextSetter extends BaseTextWithMarkupCopyPasteProcessor.RawTextSetter {
|
||||
public RawTextSetter(RtfCopyPasteProcessor processor) {
|
||||
super(processor);
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(builder);
|
||||
}
|
||||
myBuilder.append("\\par");
|
||||
myBuilder.append(HEADER_SUFFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontFamily(String fontFamily) {
|
||||
int id = myFontNameRegistry.getId(fontFamily);
|
||||
saveFontName(id);
|
||||
myFontNameId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontStyle(int fontStyle) {
|
||||
// Reset formatting settings
|
||||
myBuilder.append(PLAIN);
|
||||
|
||||
// Restore target formatting settings.
|
||||
if (myForegroundId >= 0) {
|
||||
saveForeground(myForegroundId);
|
||||
}
|
||||
if (myBackgroundId >= 0) {
|
||||
saveBackground(myBackgroundId);
|
||||
}
|
||||
if (myFontNameId >= 0) {
|
||||
saveFontName(myFontNameId);
|
||||
}
|
||||
saveFontSize(myFontSize);
|
||||
|
||||
if ((fontStyle & Font.ITALIC) > 0) {
|
||||
myBuilder.append(ITALIC);
|
||||
}
|
||||
if ((fontStyle & Font.BOLD) > 0) {
|
||||
myBuilder.append(BOLD);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForeground(Color foreground) {
|
||||
int id = myColorRegistry.getId(foreground);
|
||||
saveForeground(id);
|
||||
myForegroundId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackground(Color background) {
|
||||
int id = myColorRegistry.getId(background);
|
||||
saveBackground(id);
|
||||
myBackgroundId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTextFragment(CharSequence charSequence, int startOffset, int endOffset) {
|
||||
myBuilder.append("\n");
|
||||
for (int i = startOffset; i < endOffset; i++) {
|
||||
char c = charSequence.charAt(i);
|
||||
if (c > 127) {
|
||||
// Escape non-ascii symbols.
|
||||
myBuilder.append(String.format("\\u%04d?", (int)c));
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '\t':
|
||||
myBuilder.append(TAB);
|
||||
continue;
|
||||
case '\n':
|
||||
myBuilder.append(NEW_LINE);
|
||||
continue;
|
||||
case '\\':
|
||||
case '{':
|
||||
case '}':
|
||||
myBuilder.append('\\');
|
||||
}
|
||||
myBuilder.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStreamTransferableData createTransferable(@NotNull String data) {
|
||||
try {
|
||||
return new InputStreamTransferableData(data.getBytes(CHARSET), FLAVOR);
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // shouldn't happen
|
||||
}
|
||||
}
|
||||
|
||||
private void saveBackground(int id) {
|
||||
myBuilder.append("\\cb").append(id);
|
||||
}
|
||||
|
||||
private void saveForeground(int id) {
|
||||
myBuilder.append("\\cf").append(id);
|
||||
}
|
||||
|
||||
private void saveFontName(int id) {
|
||||
myBuilder.append("\\f").append(id);
|
||||
}
|
||||
|
||||
private void saveFontSize(int size) {
|
||||
myBuilder.append("\\fs").append(size * 2);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-12
@@ -15,17 +15,9 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.EventListener;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
|
||||
// we're extending EventListener to be able to use EventDispatcher in TextWithMarkupProcessor
|
||||
public interface TextWithMarkupBuilder extends EventListener {
|
||||
void init(Color defaultForeground, Color defaultBackground, String defaultFontFamily, int fontSize);
|
||||
boolean isOverflowed();
|
||||
void setFontFamily(String fontFamily);
|
||||
void setFontStyle(int fontStyle);
|
||||
void setForeground(Color foreground);
|
||||
void setBackground(Color background);
|
||||
void addTextFragment(CharSequence charSequence, int startOffset, int endOffset);
|
||||
void complete();
|
||||
public interface TextWithMarkupBuilder {
|
||||
void reset();
|
||||
void build(SyntaxInfo syntaxInfo);
|
||||
}
|
||||
|
||||
+579
-482
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -19,14 +19,13 @@ import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TObjectIntHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:17 PM
|
||||
*/
|
||||
public abstract class AbstractRegistry<T> implements Serializable {
|
||||
public abstract class AbstractRegistry<T> {
|
||||
|
||||
@NotNull private final TIntObjectHashMap<T> myDataById = new TIntObjectHashMap<T>();
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.model;
|
||||
|
||||
public interface MarkupHandler {
|
||||
void handleText(int startOffset, int endOffset) throws Exception;
|
||||
void handleForeground(int foregroundId) throws Exception;
|
||||
void handleBackground(int backgroundId) throws Exception;
|
||||
void handleFont(int fontNameId) throws Exception;
|
||||
void handleStyle(int style) throws Exception;
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.model;
|
||||
|
||||
import com.intellij.util.io.CompactDataInput;
|
||||
import com.intellij.util.io.CompactDataOutput;
|
||||
import org.iq80.snappy.SnappyInputStream;
|
||||
import org.iq80.snappy.SnappyOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Not synchronized, stream implementations must be used from one thread at a time only
|
||||
*/
|
||||
public class OutputInfoSerializer {
|
||||
private static final int TEXT_ID = 0;
|
||||
private static final int STYLE_ID = 1;
|
||||
private static final int FOREGROUND_ID = 2;
|
||||
private static final int BACKGROUND_ID = 3;
|
||||
private static final int FONT_ID = 4;
|
||||
|
||||
public static class OutputStream implements MarkupHandler {
|
||||
private final CompactDataOutput myOutputStream;
|
||||
private final java.io.OutputStream myUnderlyingOutputStream;
|
||||
private int myCurrentOffset;
|
||||
|
||||
public OutputStream(java.io.OutputStream stream) throws IOException {
|
||||
myUnderlyingOutputStream = new SnappyOutputStream(stream);
|
||||
myOutputStream = new CompactDataOutput(myUnderlyingOutputStream);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
myUnderlyingOutputStream.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleText(int startOffset, int endOffset) throws IOException {
|
||||
myOutputStream.write(TEXT_ID);
|
||||
myOutputStream.writeInt(startOffset - myCurrentOffset);
|
||||
myOutputStream.writeInt(endOffset - startOffset);
|
||||
myCurrentOffset = endOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleForeground(int foregroundId) throws IOException {
|
||||
myOutputStream.write(FOREGROUND_ID);
|
||||
myOutputStream.writeInt(foregroundId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBackground(int backgroundId) throws IOException {
|
||||
myOutputStream.write(BACKGROUND_ID);
|
||||
myOutputStream.writeInt(backgroundId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFont(int fontNameId) throws IOException {
|
||||
myOutputStream.write(FONT_ID);
|
||||
myOutputStream.writeInt(fontNameId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStyle(int style) throws IOException {
|
||||
myOutputStream.write(STYLE_ID);
|
||||
myOutputStream.writeInt(style);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InputStream {
|
||||
private final CompactDataInput myInputStream;
|
||||
private final java.io.InputStream myUnderlyingInputStream;
|
||||
private int myCurrentOffset;
|
||||
|
||||
public InputStream(java.io.InputStream stream) throws IOException {
|
||||
myUnderlyingInputStream = new SnappyInputStream(stream);
|
||||
myInputStream = new CompactDataInput(myUnderlyingInputStream);
|
||||
}
|
||||
|
||||
public void read(MarkupHandler handler) throws Exception {
|
||||
int id = myInputStream.readByte();
|
||||
switch (id) {
|
||||
case TEXT_ID:
|
||||
int startOffset = myCurrentOffset + myInputStream.readInt();
|
||||
myCurrentOffset = startOffset;
|
||||
int endOffset = myCurrentOffset + myInputStream.readInt();
|
||||
myCurrentOffset = endOffset;
|
||||
handler.handleText(startOffset, endOffset);
|
||||
break;
|
||||
case STYLE_ID:
|
||||
handler.handleStyle(myInputStream.readInt());
|
||||
break;
|
||||
case FOREGROUND_ID:
|
||||
handler.handleForeground(myInputStream.readInt());
|
||||
break;
|
||||
case BACKGROUND_ID:
|
||||
handler.handleBackground(myInputStream.readInt());
|
||||
break;
|
||||
case FONT_ID:
|
||||
handler.handleFont(myInputStream.readInt());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown tag id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
myUnderlyingInputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/25/13 1:19 PM
|
||||
*/
|
||||
public class SyntaxInfo {
|
||||
private final int myOutputInfoCount;
|
||||
private final byte[] myOutputInfosSerialized;
|
||||
@NotNull private final ColorRegistry myColorRegistry;
|
||||
@NotNull private final FontNameRegistry myFontNameRegistry;
|
||||
|
||||
private final int myDefaultForeground;
|
||||
private final int myDefaultBackground;
|
||||
private final int mySingleFontSize;
|
||||
|
||||
private SyntaxInfo(int outputInfoCount,
|
||||
byte[] outputInfosSerialized,
|
||||
int defaultForeground,
|
||||
int defaultBackground,
|
||||
int singleFontSize,
|
||||
@NotNull FontNameRegistry fontNameRegistry,
|
||||
@NotNull ColorRegistry colorRegistry)
|
||||
{
|
||||
myOutputInfoCount = outputInfoCount;
|
||||
myOutputInfosSerialized = outputInfosSerialized;
|
||||
myDefaultForeground = defaultForeground;
|
||||
myDefaultBackground = defaultBackground;
|
||||
mySingleFontSize = singleFontSize;
|
||||
myFontNameRegistry = fontNameRegistry;
|
||||
myColorRegistry = colorRegistry;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ColorRegistry getColorRegistry() {
|
||||
return myColorRegistry;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FontNameRegistry getFontNameRegistry() {
|
||||
return myFontNameRegistry;
|
||||
}
|
||||
|
||||
public int getDefaultForeground() {
|
||||
return myDefaultForeground;
|
||||
}
|
||||
|
||||
public int getDefaultBackground() {
|
||||
return myDefaultBackground;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return positive value if all tokens have the same font size (returned value);
|
||||
* non-positive value otherwise
|
||||
*/
|
||||
public int getSingleFontSize() {
|
||||
return mySingleFontSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder b = new StringBuilder();
|
||||
b.append("default colors: foreground=").append(myDefaultForeground).append(", background=").append(myDefaultBackground).append("; output infos: ");
|
||||
boolean first = true;
|
||||
MarkupIterator it = new MarkupIterator();
|
||||
try {
|
||||
while(it.hasNext()) {
|
||||
if (first) {
|
||||
b.append(',');
|
||||
}
|
||||
it.processNext(new MarkupHandler() {
|
||||
@Override
|
||||
public void handleText(int startOffset, int endOffset) throws Exception {
|
||||
b.append("text(").append(startOffset).append(",").append(endOffset).append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleForeground(int foregroundId) throws Exception {
|
||||
b.append("foreground(").append(foregroundId).append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBackground(int backgroundId) throws Exception {
|
||||
b.append("background(").append(backgroundId).append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFont(int fontNameId) throws Exception {
|
||||
b.append("font(").append(fontNameId).append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStyle(int style) throws Exception {
|
||||
b.append("style(").append(style).append(")");
|
||||
}
|
||||
});
|
||||
first = false;
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
finally {
|
||||
it.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final ColorRegistry myColorRegistry = new ColorRegistry();
|
||||
private final FontNameRegistry myFontNameRegistry = new FontNameRegistry();
|
||||
private final int myDefaultForeground;
|
||||
private final int myDefaultBackground;
|
||||
private final int myFontSize;
|
||||
private final ByteArrayOutputStream myStream = new ByteArrayOutputStream();
|
||||
private final OutputInfoSerializer.OutputStream myOutputInfoStream;
|
||||
private int myOutputInfoCount;
|
||||
|
||||
public Builder(Color defaultForeground, Color defaultBackground, int fontSize) {
|
||||
myDefaultForeground = myColorRegistry.getId(defaultForeground);
|
||||
myDefaultBackground = myColorRegistry.getId(defaultBackground);
|
||||
myFontSize = fontSize;
|
||||
try {
|
||||
myOutputInfoStream = new OutputInfoSerializer.OutputStream(myStream);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addFontStyle(int fontStyle) {
|
||||
try {
|
||||
myOutputInfoStream.handleStyle(fontStyle);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
myOutputInfoCount++;
|
||||
}
|
||||
|
||||
public void addFontFamilyName(String fontFamilyName) {
|
||||
try {
|
||||
myOutputInfoStream.handleFont(myFontNameRegistry.getId(fontFamilyName));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
myOutputInfoCount++;
|
||||
}
|
||||
|
||||
public void addForeground(Color foreground) {
|
||||
try {
|
||||
myOutputInfoStream.handleForeground(myColorRegistry.getId(foreground));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
myOutputInfoCount++;
|
||||
}
|
||||
|
||||
public void addBackground(Color background) {
|
||||
try {
|
||||
myOutputInfoStream.handleBackground(myColorRegistry.getId(background));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
myOutputInfoCount++;
|
||||
}
|
||||
|
||||
public void addText(int startOffset, int endOffset) {
|
||||
try {
|
||||
myOutputInfoStream.handleText(startOffset, endOffset);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
myOutputInfoCount++;
|
||||
}
|
||||
|
||||
public SyntaxInfo build() {
|
||||
myColorRegistry.seal();
|
||||
myFontNameRegistry.seal();
|
||||
try {
|
||||
myOutputInfoStream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new SyntaxInfo(myOutputInfoCount, myStream.toByteArray(), myDefaultForeground, myDefaultBackground, myFontSize, myFontNameRegistry, myColorRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
public class MarkupIterator {
|
||||
private int pos;
|
||||
private final OutputInfoSerializer.InputStream myOutputInfoStream;
|
||||
|
||||
public MarkupIterator() {
|
||||
try {
|
||||
myOutputInfoStream = new OutputInfoSerializer.InputStream(new ByteArrayInputStream(myOutputInfosSerialized));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return pos < myOutputInfoCount;
|
||||
}
|
||||
|
||||
public void processNext(MarkupHandler handler) {
|
||||
if (!hasNext()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
pos++;
|
||||
try {
|
||||
myOutputInfoStream.read(handler);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
try {
|
||||
myOutputInfoStream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 1:20 PM
|
||||
*/
|
||||
public abstract class AbstractSyntaxAwareInputStreamTransferableData extends InputStream implements TextBlockTransferableData, RawTextHolder
|
||||
{
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#" + AbstractSyntaxAwareInputStreamTransferableData.class.getName());
|
||||
|
||||
protected String myRawText;
|
||||
@NotNull
|
||||
protected final SyntaxInfo mySyntaxInfo;
|
||||
@NotNull
|
||||
private final DataFlavor myDataFlavor;
|
||||
|
||||
@Nullable private transient InputStream myDelegate;
|
||||
|
||||
public AbstractSyntaxAwareInputStreamTransferableData(@NotNull SyntaxInfo syntaxInfo, @NotNull DataFlavor flavor) {
|
||||
mySyntaxInfo = syntaxInfo;
|
||||
myDataFlavor = flavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor getFlavor() {
|
||||
return myDataFlavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return getDelegate().read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NotNull byte[] b, int off, int len) throws IOException {
|
||||
return getDelegate().read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myDelegate = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRawText(String rawText) {
|
||||
myRawText = rawText;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private InputStream getDelegate() {
|
||||
if (myDelegate != null) {
|
||||
return myDelegate;
|
||||
}
|
||||
|
||||
int maxLength = Registry.intValue("editor.richcopy.max.size.megabytes") * 1048576;
|
||||
final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
try {
|
||||
build(buffer, maxLength);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
String s = buffer.toString();
|
||||
if (Registry.is("editor.richcopy.debug")) {
|
||||
LOG.info("Resulting text: \n" + s);
|
||||
}
|
||||
try {
|
||||
myDelegate = new ByteArrayInputStream(s.getBytes(getCharset()));
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return myDelegate;
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void build(@NotNull StringBuilder holder, int maxLength);
|
||||
|
||||
@NotNull
|
||||
protected abstract String getCharset();
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 7:09 PM
|
||||
*/
|
||||
public abstract class AbstractSyntaxAwareReaderTransferableData extends Reader implements TextBlockTransferableData, RawTextHolder
|
||||
{
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#" + AbstractSyntaxAwareReaderTransferableData.class.getName());
|
||||
|
||||
protected String myRawText;
|
||||
@NotNull
|
||||
protected final SyntaxInfo mySyntaxInfo;
|
||||
@NotNull
|
||||
private final DataFlavor myDataFlavor;
|
||||
|
||||
@Nullable private transient Reader myDelegate;
|
||||
|
||||
public AbstractSyntaxAwareReaderTransferableData(@NotNull SyntaxInfo syntaxInfo, @NotNull DataFlavor dataFlavor) {
|
||||
mySyntaxInfo = syntaxInfo;
|
||||
myDataFlavor = dataFlavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor getFlavor() {
|
||||
return myDataFlavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return getDelegate().read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NotNull char[] cbuf, int off, int len) throws IOException {
|
||||
return getDelegate().read(cbuf, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myDelegate = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRawText(String rawText) {
|
||||
myRawText = rawText;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Reader getDelegate() {
|
||||
if (myDelegate != null) {
|
||||
return myDelegate;
|
||||
}
|
||||
|
||||
int maxLength = Registry.intValue("editor.richcopy.max.size.megabytes") * 1048576;
|
||||
final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
try {
|
||||
build(buffer, maxLength);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
String s = buffer.toString();
|
||||
if (Registry.is("editor.richcopy.debug")) {
|
||||
LOG.info("Resulting text: \n" + s);
|
||||
}
|
||||
myDelegate = new StringReader(s);
|
||||
return myDelegate;
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void build(@NotNull StringBuilder holder, int maxLength);
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.openapi.editor.richcopy.model.ColorRegistry;
|
||||
import com.intellij.openapi.editor.richcopy.model.FontNameRegistry;
|
||||
import com.intellij.openapi.editor.richcopy.model.MarkupHandler;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 1:06 PM
|
||||
*/
|
||||
public class HtmlTransferableData extends AbstractSyntaxAwareReaderTransferableData implements MarkupHandler {
|
||||
|
||||
@NotNull public static final DataFlavor FLAVOR = new DataFlavor("text/html;class=java.io.Reader", "HTML text");
|
||||
|
||||
private StringBuilder myResultBuffer;
|
||||
private ColorRegistry myColorRegistry;
|
||||
private FontNameRegistry myFontNameRegistry;
|
||||
|
||||
private int myForeground;
|
||||
private int myBackground;
|
||||
private int myFontFamily;
|
||||
private boolean myBold;
|
||||
private boolean myItalic;
|
||||
|
||||
private final TIntObjectHashMap<String> myColors = new TIntObjectHashMap<String>();
|
||||
|
||||
public HtmlTransferableData(@NotNull SyntaxInfo syntaxInfo) {
|
||||
super(syntaxInfo, FLAVOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void build(@NotNull StringBuilder holder, int maxLength) {
|
||||
myResultBuffer = holder;
|
||||
myColorRegistry = mySyntaxInfo.getColorRegistry();
|
||||
myFontNameRegistry = mySyntaxInfo.getFontNameRegistry();
|
||||
try {
|
||||
buildColorMap();
|
||||
myResultBuffer.append("<div style=\"border:1px inset;padding:2%;\">")
|
||||
.append("<pre style=\"margin:0;padding:6px;background-color:");
|
||||
// .append("<pre style='height:30%;overflow:auto;margin:0;padding:6px;background-color:")
|
||||
appendColor(myResultBuffer, mySyntaxInfo.getDefaultBackground());
|
||||
myResultBuffer.append(';');
|
||||
if (myFontNameRegistry.size() == 1) {
|
||||
appendFontFamilyRule(myResultBuffer, myFontNameRegistry.getAllIds()[0]);
|
||||
myFontNameRegistry = null;
|
||||
}
|
||||
appendFontSizeRule(myResultBuffer, mySyntaxInfo.getSingleFontSize());
|
||||
myResultBuffer.append("\" bgcolor=\"");
|
||||
appendColor(myResultBuffer, mySyntaxInfo.getDefaultBackground());
|
||||
myResultBuffer.append("\">");
|
||||
|
||||
SyntaxInfo.MarkupIterator it = mySyntaxInfo.new MarkupIterator();
|
||||
try {
|
||||
while(it.hasNext()) {
|
||||
it.processNext(this);
|
||||
if (myResultBuffer.length() > maxLength) {
|
||||
myResultBuffer.append("... truncated ...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
it.dispose();
|
||||
}
|
||||
myResultBuffer.append("</pre></div>");
|
||||
}
|
||||
finally {
|
||||
myResultBuffer = null;
|
||||
myRawText = null;
|
||||
myColorRegistry = null;
|
||||
myFontNameRegistry = null;
|
||||
myColors.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void defineForeground(int id, @NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<font color=\"");
|
||||
appendColor(myResultBuffer, id);
|
||||
myResultBuffer.append("\">");
|
||||
styleBuffer.append("color:");
|
||||
appendColor(styleBuffer, id);
|
||||
styleBuffer.append(";");
|
||||
closeTagBuffer.insert(0, "</font>");
|
||||
}
|
||||
|
||||
private void defineBackground(int id, @NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<font bgcolor=\"");
|
||||
appendColor(myResultBuffer, id);
|
||||
myResultBuffer.append("\">");
|
||||
styleBuffer.append("background-color:");
|
||||
appendColor(styleBuffer, id);
|
||||
styleBuffer.append(";");
|
||||
closeTagBuffer.insert(0, "</font>");
|
||||
}
|
||||
|
||||
private void defineBold(@NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<b>");
|
||||
styleBuffer.append("font-weight:bold;");
|
||||
closeTagBuffer.insert(0, "</b>");
|
||||
}
|
||||
|
||||
private void defineItalic(@NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<i>");
|
||||
styleBuffer.append("font-style:italic;");
|
||||
closeTagBuffer.insert(0, "</i>");
|
||||
}
|
||||
|
||||
private void appendColor(StringBuilder builder, int id) {
|
||||
builder.append(myColors.get(id));
|
||||
}
|
||||
|
||||
private void buildColorMap() {
|
||||
for (int id : myColorRegistry.getAllIds()) {
|
||||
StringBuilder b = new StringBuilder("#");
|
||||
UIUtil.appendColor(myColorRegistry.dataById(id), b);
|
||||
myColors.put(id, b.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendFontFamilyRule(@NotNull StringBuilder styleBuffer, int fontFamilyId) {
|
||||
styleBuffer.append("font-family:'").append(myFontNameRegistry.dataById(fontFamilyId)).append("';");
|
||||
}
|
||||
|
||||
private static void appendFontSizeRule(@NotNull StringBuilder styleBuffer, int fontSize) {
|
||||
styleBuffer.append("font-size:").append(fontSize).append("pt;");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleText(int startOffset, int endOffset) {
|
||||
boolean formattedText = myForeground > 0 || myBackground > 0 || myFontFamily > 0 || myBold || myItalic;
|
||||
if (!formattedText) {
|
||||
escapeAndAdd(startOffset, endOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder styleBuffer = StringBuilderSpinAllocator.alloc();
|
||||
StringBuilder closeTagBuffer = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
if (myForeground > 0) {
|
||||
defineForeground(myForeground, styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myBackground > 0) {
|
||||
defineBackground(myBackground, styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myBold) {
|
||||
defineBold(styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myItalic) {
|
||||
defineItalic(styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myFontFamily > 0) {
|
||||
appendFontFamilyRule(styleBuffer, myFontFamily);
|
||||
}
|
||||
myResultBuffer.append("<span style=\"");
|
||||
myResultBuffer.append(styleBuffer);
|
||||
myResultBuffer.append("\">");
|
||||
escapeAndAdd(startOffset, endOffset);
|
||||
myResultBuffer.append("</span>");
|
||||
myResultBuffer.append(closeTagBuffer);
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(styleBuffer);
|
||||
StringBuilderSpinAllocator.dispose(closeTagBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void escapeAndAdd(int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = myRawText.charAt(i);
|
||||
switch (c) {
|
||||
case '<': myResultBuffer.append("<"); break;
|
||||
case '>': myResultBuffer.append(">"); break;
|
||||
case '&': myResultBuffer.append("&"); break;
|
||||
default: myResultBuffer.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleForeground(int foregroundId) throws Exception {
|
||||
myForeground = foregroundId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBackground(int backgroundId) throws Exception {
|
||||
myBackground = backgroundId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFont(int fontNameId) throws Exception {
|
||||
if (myFontNameRegistry != null) {
|
||||
myFontFamily = fontNameId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStyle(int style) throws Exception {
|
||||
myBold = (Font.BOLD & style) != 0;
|
||||
myItalic = (Font.ITALIC & style) != 0;
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 1:20 PM
|
||||
*/
|
||||
public class InputStreamTransferableData extends InputStream implements TextBlockTransferableData
|
||||
{
|
||||
@NotNull
|
||||
private final DataFlavor myDataFlavor;
|
||||
@Nullable
|
||||
private InputStream myDelegate;
|
||||
|
||||
public InputStreamTransferableData(@NotNull byte[] data, @NotNull DataFlavor flavor) {
|
||||
myDataFlavor = flavor;
|
||||
myDelegate = new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor getFlavor() {
|
||||
return myDelegate == null ? null : myDataFlavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NotNull byte[] b, int off, int len) throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myDelegate = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.view;
|
||||
|
||||
public interface RawTextHolder {
|
||||
void setRawText(String rawText);
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 7:09 PM
|
||||
*/
|
||||
public class ReaderTransferableData extends Reader implements TextBlockTransferableData
|
||||
{
|
||||
@NotNull
|
||||
private final DataFlavor myDataFlavor;
|
||||
@Nullable
|
||||
private Reader myDelegate;
|
||||
|
||||
@SuppressWarnings("AbstractMethodCallInConstructor")
|
||||
public ReaderTransferableData(@NotNull String data, @NotNull DataFlavor dataFlavor) {
|
||||
myDataFlavor = dataFlavor;
|
||||
myDelegate = new StringReader(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor getFlavor() {
|
||||
return myDelegate == null ? null : myDataFlavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NotNull char[] cbuf, int off, int len) throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read(cbuf, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myDelegate = null;
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.openapi.editor.richcopy.model.ColorRegistry;
|
||||
import com.intellij.openapi.editor.richcopy.model.FontNameRegistry;
|
||||
import com.intellij.openapi.editor.richcopy.model.MarkupHandler;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
public class RtfTransferableData extends AbstractSyntaxAwareInputStreamTransferableData {
|
||||
|
||||
@NotNull public static final DataFlavor FLAVOR = new DataFlavor("text/rtf;class=java.io.InputStream", "RTF text");
|
||||
|
||||
@NotNull private static final String HEADER_PREFIX = "{\\rtf1\\ansi\\deff0";
|
||||
@NotNull private static final String HEADER_SUFFIX = "}";
|
||||
@NotNull private static final String TAB = "\\tab\n";
|
||||
@NotNull private static final String NEW_LINE = "\\line\n";
|
||||
@NotNull private static final String BOLD = "\\b";
|
||||
@NotNull private static final String ITALIC = "\\i";
|
||||
@NotNull private static final String PLAIN = "\\plain\n";
|
||||
|
||||
public RtfTransferableData(@NotNull SyntaxInfo syntaxInfo) {
|
||||
super(syntaxInfo, FLAVOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void build(@NotNull final StringBuilder holder, final int maxLength) {
|
||||
header(mySyntaxInfo, holder, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
rectangularBackground(mySyntaxInfo, holder, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
content(mySyntaxInfo, holder, myRawText, maxLength);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getCharset() {
|
||||
return "US-ASCII";
|
||||
}
|
||||
|
||||
private static void header(@NotNull SyntaxInfo syntaxInfo, @NotNull StringBuilder buffer, @NotNull Runnable next) {
|
||||
buffer.append(HEADER_PREFIX);
|
||||
|
||||
// Color table.
|
||||
buffer.append("{\\colortbl;");
|
||||
ColorRegistry colorRegistry = syntaxInfo.getColorRegistry();
|
||||
for (int id : colorRegistry.getAllIds()) {
|
||||
Color color = colorRegistry.dataById(id);
|
||||
buffer.append(String.format("\\red%d\\green%d\\blue%d;", color.getRed(), color.getGreen(), color.getBlue()));
|
||||
}
|
||||
buffer.append("}\n");
|
||||
|
||||
// Font table.
|
||||
buffer.append("{\\fonttbl");
|
||||
FontNameRegistry fontNameRegistry = syntaxInfo.getFontNameRegistry();
|
||||
for (int id : fontNameRegistry.getAllIds()) {
|
||||
String fontName = fontNameRegistry.dataById(id);
|
||||
buffer.append(String.format("{\\f%d %s;}", id, fontName));
|
||||
}
|
||||
buffer.append("}\n");
|
||||
|
||||
next.run();
|
||||
buffer.append(HEADER_SUFFIX);
|
||||
}
|
||||
|
||||
private static void rectangularBackground(@NotNull SyntaxInfo syntaxInfo, @NotNull StringBuilder buffer, @NotNull Runnable next) {
|
||||
buffer.append("\n\\s0\\box\\brdrhair\\brdrcf").append(syntaxInfo.getDefaultForeground()).append("\\brsp317").append("\\cbpat")
|
||||
.append(syntaxInfo.getDefaultBackground());
|
||||
saveBackground(buffer, syntaxInfo.getDefaultBackground());
|
||||
next.run();
|
||||
buffer.append("\\par");
|
||||
}
|
||||
|
||||
private static void content(@NotNull SyntaxInfo syntaxInfo, @NotNull StringBuilder buffer, @NotNull String rawText, int maxLength) {
|
||||
MyVisitor visitor = new MyVisitor(buffer, rawText, syntaxInfo.getSingleFontSize());
|
||||
SyntaxInfo.MarkupIterator it = syntaxInfo.new MarkupIterator();
|
||||
try {
|
||||
while(it.hasNext()) {
|
||||
it.processNext(visitor);
|
||||
if (buffer.length() > maxLength) {
|
||||
buffer.append("... truncated ...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
it.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveBackground(@NotNull StringBuilder buffer, int id) {
|
||||
buffer.append("\\cb").append(id);
|
||||
}
|
||||
|
||||
private static void saveForeground(@NotNull StringBuilder buffer, int id) {
|
||||
buffer.append("\\cf").append(id);
|
||||
}
|
||||
|
||||
private static void saveFontName(@NotNull StringBuilder buffer, int id) {
|
||||
buffer.append("\\f").append(id);
|
||||
}
|
||||
|
||||
private static void saveFontSize(@NotNull StringBuilder buffer, int size) {
|
||||
buffer.append("\\fs").append(size * 2);
|
||||
}
|
||||
|
||||
private static class MyVisitor implements MarkupHandler {
|
||||
|
||||
@NotNull private final StringBuilder myBuffer;
|
||||
@NotNull private final String myRawText;
|
||||
|
||||
private int myBackgroundId = -1;
|
||||
private int myForegroundId = -1;
|
||||
private int myFontNameId = -1;
|
||||
private int myFontStyle = -1;
|
||||
private int myFontSize = -1;
|
||||
|
||||
MyVisitor(@NotNull StringBuilder buffer, @NotNull String rawText, int fontSize) {
|
||||
myBuffer = buffer;
|
||||
myRawText = rawText;
|
||||
myFontSize = fontSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleText(int startOffset, int endOffset) throws Exception {
|
||||
myBuffer.append("\n");
|
||||
for (int i = startOffset; i < endOffset; i++) {
|
||||
char c = myRawText.charAt(i);
|
||||
if (c > 127) {
|
||||
// Escape non-ascii symbols.
|
||||
myBuffer.append(String.format("\\u%04d?", (int)c));
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '\t':
|
||||
myBuffer.append(TAB);
|
||||
continue;
|
||||
case '\n':
|
||||
myBuffer.append(NEW_LINE);
|
||||
continue;
|
||||
case '\\':
|
||||
case '{':
|
||||
case '}':
|
||||
myBuffer.append('\\');
|
||||
}
|
||||
myBuffer.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleForeground(int foregroundId) throws Exception {
|
||||
saveForeground(myBuffer, foregroundId);
|
||||
myForegroundId = foregroundId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBackground(int backgroundId) throws Exception {
|
||||
saveBackground(myBuffer, backgroundId);
|
||||
myBackgroundId = backgroundId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFont(int fontNameId) throws Exception {
|
||||
saveFontName(myBuffer, fontNameId);
|
||||
myFontNameId = fontNameId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStyle(int style) throws Exception {
|
||||
// Reset formatting settings
|
||||
myBuffer.append(PLAIN);
|
||||
|
||||
// Restore target formatting settings.
|
||||
if (myForegroundId >= 0) {
|
||||
saveForeground(myBuffer, myForegroundId);
|
||||
}
|
||||
if (myBackgroundId >= 0) {
|
||||
saveBackground(myBuffer, myBackgroundId);
|
||||
}
|
||||
if (myFontNameId >= 0) {
|
||||
saveFontName(myBuffer, myFontNameId);
|
||||
}
|
||||
if (myFontSize > 0) {
|
||||
saveFontSize(myBuffer, myFontSize);
|
||||
}
|
||||
|
||||
myFontStyle = style;
|
||||
if ((myFontStyle & Font.ITALIC) > 0) {
|
||||
myBuffer.append(ITALIC);
|
||||
}
|
||||
if ((myFontStyle & Font.BOLD) > 0) {
|
||||
myBuffer.append(BOLD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ public class CopyPasteManagerEx extends CopyPasteManager implements ClipboardOwn
|
||||
addToTheTopOfTheStack(content);
|
||||
}
|
||||
else {
|
||||
moveContentToStackTop(same);
|
||||
moveContentToStackTop(same, false); // notification is done in setContents() method
|
||||
}
|
||||
}
|
||||
catch (UnsupportedFlavorException ignore) { }
|
||||
@@ -283,12 +283,18 @@ public class CopyPasteManagerEx extends CopyPasteManager implements ClipboardOwn
|
||||
}
|
||||
|
||||
public void moveContentToStackTop(Transferable t) {
|
||||
moveContentToStackTop(t, true);
|
||||
}
|
||||
|
||||
private void moveContentToStackTop(Transferable t, boolean notifyOthers) {
|
||||
Transferable current = myData.isEmpty() ? null : myData.get(0);
|
||||
if (!Comparing.equal(t, current)) {
|
||||
myData.remove(t);
|
||||
myData.add(0, t);
|
||||
setSystemClipboardContent(t);
|
||||
fireContentChanged(current, t);
|
||||
if (notifyOthers) {
|
||||
setSystemClipboardContent(t);
|
||||
fireContentChanged(current, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -884,6 +884,9 @@
|
||||
<copyPastePostProcessor implementation="com.intellij.openapi.editor.richcopy.TextWithMarkupProcessor"/>
|
||||
<copyPastePostProcessor implementation="com.intellij.openapi.editor.richcopy.RtfCopyPasteProcessor"/>
|
||||
<copyPastePostProcessor implementation="com.intellij.openapi.editor.richcopy.HtmlCopyPasteProcessor"/>
|
||||
<!--the following bindings use 'first' order to make sure they capture raw text before any other processor modifies it -->
|
||||
<copyPastePreProcessor implementation="com.intellij.openapi.editor.richcopy.RtfCopyPasteProcessor$RawTextSetter" order="first" id="richcopy-rtf"/>
|
||||
<copyPastePreProcessor implementation="com.intellij.openapi.editor.richcopy.HtmlCopyPasteProcessor$RawTextSetter" order="first" id="richcopy-html"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
|
||||
+6
-4
@@ -16,6 +16,8 @@
|
||||
package com.intellij.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.intellij.openapi.editor.richcopy.view.HtmlTransferableData;
|
||||
import com.intellij.openapi.editor.richcopy.view.RtfTransferableData;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
|
||||
@@ -48,14 +50,14 @@ public class RichCopyTest extends LightPlatformCodeInsightFixtureTestCase {
|
||||
Transferable contents = CopyPasteManager.getInstance().getContents();
|
||||
assertNotNull(contents);
|
||||
|
||||
assertTrue(contents.isDataFlavorSupported(HtmlCopyPasteProcessor.FLAVOR));
|
||||
assertTrue(contents.isDataFlavorSupported(HtmlTransferableData.FLAVOR));
|
||||
String expectedHtml = getFileContents(getTestName(false) + ".html");
|
||||
String actualHtml = readFully((Reader)contents.getTransferData(HtmlCopyPasteProcessor.FLAVOR));
|
||||
String actualHtml = readFully((Reader)contents.getTransferData(HtmlTransferableData.FLAVOR));
|
||||
assertMatches("HTML contents differs", expectedHtml, actualHtml);
|
||||
|
||||
assertTrue(contents.isDataFlavorSupported(RtfCopyPasteProcessor.FLAVOR));
|
||||
assertTrue(contents.isDataFlavorSupported(RtfTransferableData.FLAVOR));
|
||||
String expectedRtf = getFileContents(getTestName(false) + ".rtf");
|
||||
String actualRtf = readFully((InputStream)contents.getTransferData(RtfCopyPasteProcessor.FLAVOR));
|
||||
String actualRtf = readFully((InputStream)contents.getTransferData(RtfTransferableData.FLAVOR));
|
||||
assertMatches("RTF contents differs", expectedRtf, actualRtf);
|
||||
}
|
||||
|
||||
|
||||
+46
-37
@@ -19,12 +19,13 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.SelectionModel;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.richcopy.model.ColorRegistry;
|
||||
import com.intellij.openapi.editor.richcopy.model.MarkupHandler;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
|
||||
import com.intellij.ui.JBColor;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/27/13 11:11 AM
|
||||
@@ -235,56 +236,64 @@ public class SyntaxInfoConstructionTest extends LightPlatformCodeInsightFixtureT
|
||||
}
|
||||
|
||||
private String getSyntaxInfo() {
|
||||
final StringBuilder syntaxInfo = new StringBuilder();
|
||||
Editor editor = myFixture.getEditor();
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final Editor editor = myFixture.getEditor();
|
||||
final String text = editor.getSelectionModel().getSelectedText(true);
|
||||
assertNotNull(text);
|
||||
|
||||
TextWithMarkupProcessor processor = new TextWithMarkupProcessor();
|
||||
processor.addBuilder(new TextWithMarkupBuilder() {
|
||||
@Override
|
||||
public void init(Color defaultForeground, Color defaultBackground, String defaultFontFamily, int fontSize) {
|
||||
assertEquals(JBColor.BLACK, defaultForeground);
|
||||
assertEquals(JBColor.WHITE, defaultBackground);
|
||||
assertEquals(getFontSize(), fontSize);
|
||||
public void reset() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowed() {
|
||||
return false;
|
||||
}
|
||||
public void build(SyntaxInfo syntaxInfo) {
|
||||
final ColorRegistry colorRegistry = syntaxInfo.getColorRegistry();
|
||||
assertEquals(JBColor.BLACK, colorRegistry.dataById(syntaxInfo.getDefaultForeground()));
|
||||
assertEquals(JBColor.WHITE, colorRegistry.dataById(syntaxInfo.getDefaultBackground()));
|
||||
assertEquals(getFontSize(), syntaxInfo.getSingleFontSize());
|
||||
SyntaxInfo.MarkupIterator it = syntaxInfo.new MarkupIterator();
|
||||
try {
|
||||
while (it.hasNext()) {
|
||||
it.processNext(new MarkupHandler() {
|
||||
@Override
|
||||
public void handleText(int startOffset, int endOffset) throws Exception {
|
||||
builder.append("text=").append(text.substring(startOffset, endOffset)).append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontFamily(String fontFamily) {
|
||||
}
|
||||
@Override
|
||||
public void handleForeground(int foregroundId) throws Exception {
|
||||
builder.append("foreground=").append(colorRegistry.dataById(foregroundId)).append(',');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontStyle(int fontStyle) {
|
||||
syntaxInfo.append("fontStyle=").append(fontStyle).append(',');
|
||||
}
|
||||
@Override
|
||||
public void handleBackground(int backgroundId) throws Exception {
|
||||
builder.append("background=").append(colorRegistry.dataById(backgroundId)).append(',');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForeground(Color foreground) {
|
||||
syntaxInfo.append("foreground=").append(foreground).append(',');
|
||||
}
|
||||
@Override
|
||||
public void handleFont(int fontNameId) throws Exception {
|
||||
assertEquals(1, fontNameId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackground(Color background) {
|
||||
syntaxInfo.append("background=").append(background).append(',');
|
||||
@Override
|
||||
public void handleStyle(int style) throws Exception {
|
||||
builder.append("fontStyle=").append(style).append(',');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
finally {
|
||||
it.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTextFragment(CharSequence charSequence, int startOffset, int endOffset) {
|
||||
syntaxInfo.append("text=").append(charSequence.subSequence(startOffset, endOffset)).append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete() {
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
SelectionModel selectionModel = editor.getSelectionModel();
|
||||
processor.collectTransferableData(myFixture.getFile(), editor, selectionModel.getBlockSelectionStarts(), selectionModel.getBlockSelectionEnds());
|
||||
|
||||
return syntaxInfo.toString();
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void init(String text) {
|
||||
|
||||
Reference in New Issue
Block a user