blob: 5a70b278b6170d3083fa65dbcd51c9cd63be8ce8 [file] [log] [blame]
/*
* 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.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; charset=UTF-8", "HTML text");
private final int myTabSize;
private StringBuilder myResultBuffer;
private ColorRegistry myColorRegistry;
private FontNameRegistry myFontNameRegistry;
private int myMaxLength;
private int myDefaultForeground;
private int myDefaultBackground;
private int myDefaultFontFamily;
private int myForeground;
private int myBackground;
private int myFontFamily;
private boolean myBold;
private boolean myItalic;
private int myCurrentColumn;
private final TIntObjectHashMap<String> myColors = new TIntObjectHashMap<String>();
public HtmlTransferableData(@NotNull SyntaxInfo syntaxInfo, int tabSize) {
super(syntaxInfo, FLAVOR);
myTabSize = tabSize;
}
@Override
protected void build(@NotNull StringBuilder holder, int maxLength) {
myResultBuffer = holder;
myColorRegistry = mySyntaxInfo.getColorRegistry();
myFontNameRegistry = mySyntaxInfo.getFontNameRegistry();
myDefaultForeground = myForeground = mySyntaxInfo.getDefaultForeground();
myDefaultBackground = myBackground = mySyntaxInfo.getDefaultBackground();
myBold = myItalic = false;
myCurrentColumn = 0;
myMaxLength = maxLength;
try {
buildColorMap();
myResultBuffer.append("<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"></head><body>")
.append("<pre style=\"background-color:");
appendColor(myResultBuffer, myDefaultBackground);
myResultBuffer.append(";color:");
appendColor(myResultBuffer, myDefaultForeground);
myResultBuffer.append(';');
int[] fontIds = myFontNameRegistry.getAllIds();
if (fontIds.length > 0) {
myFontFamily = myDefaultFontFamily = fontIds[0];
appendFontFamilyRule(myResultBuffer, myDefaultFontFamily);
}
else {
myFontFamily = myDefaultFontFamily = -1;
}
myResultBuffer.append("font-size:").append(mySyntaxInfo.getFontSize()).append("pt;\">");
mySyntaxInfo.processOutputInfo(this);
myResultBuffer.append("</pre></body></html>");
}
finally {
myResultBuffer = null;
myColorRegistry = null;
myFontNameRegistry = null;
myColors.clear();
}
}
private void appendFontFamilyRule(@NotNull StringBuilder styleBuffer, int fontFamilyId) {
styleBuffer.append("font-family:'").append(myFontNameRegistry.dataById(fontFamilyId)).append("';");
}
private static void defineBold(@NotNull StringBuilder styleBuffer) {
styleBuffer.append("font-weight:bold;");
}
private static void defineItalic(@NotNull StringBuilder styleBuffer) {
styleBuffer.append("font-style:italic;");
}
private void defineForeground(int id, @NotNull StringBuilder styleBuffer) {
styleBuffer.append("color:");
appendColor(styleBuffer, id);
styleBuffer.append(";");
}
private void defineBackground(int id, @NotNull StringBuilder styleBuffer) {
styleBuffer.append("background-color:");
appendColor(styleBuffer, id);
styleBuffer.append(";");
}
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());
}
}
@Override
public void handleText(int startOffset, int endOffset) {
boolean formattedText = myForeground != myDefaultForeground || myBackground != myDefaultBackground || myFontFamily != myDefaultFontFamily || myBold || myItalic;
if (!formattedText) {
escapeAndAdd(startOffset, endOffset);
return;
}
myResultBuffer.append("<span style=\"");
if (myForeground != myDefaultForeground) {
defineForeground(myForeground, myResultBuffer);
}
if (myBackground != myDefaultBackground) {
defineBackground(myBackground, myResultBuffer);
}
if (myBold) {
defineBold(myResultBuffer);
}
if (myItalic) {
defineItalic(myResultBuffer);
}
if (myFontFamily != myDefaultFontFamily) {
appendFontFamilyRule(myResultBuffer, myFontFamily);
}
myResultBuffer.append("\">");
escapeAndAdd(startOffset, endOffset);
myResultBuffer.append("</span>");
}
private void escapeAndAdd(int start, int end) {
for (int i = start; i < end; i++) {
char c = myRawText.charAt(i);
switch (c) {
case '<': myResultBuffer.append("&lt;"); break;
case '>': myResultBuffer.append("&gt;"); break;
case '&': myResultBuffer.append("&amp;"); break;
case ' ': myResultBuffer.append("&#32;"); break;
case '\n': myResultBuffer.append("<br>"); myCurrentColumn = 0; break;
case '\t':
int newColumn = (myCurrentColumn / myTabSize + 1) * myTabSize;
for (; myCurrentColumn < newColumn; myCurrentColumn++) myResultBuffer.append("&#32;");
break;
default: myResultBuffer.append(c);
}
myCurrentColumn++;
}
}
@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 {
myFontFamily = fontNameId;
}
@Override
public void handleStyle(int style) throws Exception {
myBold = (Font.BOLD & style) != 0;
myItalic = (Font.ITALIC & style) != 0;
}
@Override
public boolean canHandleMore() {
if (myResultBuffer.length() > myMaxLength) {
myResultBuffer.append("... truncated ...");
return false;
}
return true;
}
}