网站建设准备,网站建设实习目的,企业官方网站地址,分销网站怎么做本文将完成使用SWT实现我们自己的RichText编辑器组件的任务。 在为我的一位客户开发基于桌面的应用程序时#xff0c;我遇到了这样一个可视化组件的需求#xff0c;并希望添加一项功能#xff0c;以允许用户使用粗体#xff0c;斜体#xff0c;删除线等功能来写富文本注释… 本文将完成使用SWT实现我们自己的RichText编辑器组件的任务。 在为我的一位客户开发基于桌面的应用程序时我遇到了这样一个可视化组件的需求并希望添加一项功能以允许用户使用粗体斜体删除线等功能来写富文本注释。 那时我开始研究网络以便找到一个开放源代码库该库可以使我免于从头开始部署它的工作而我遇到了“已完成”实现的列表 。 让我们在这里列出我对这种组件的需求 它应该是本地SWT组件而不是Eclipse View或Editor并且必须可嵌入任何SWT组合中。 它应允许使用粗体斜体删除线等基本格式。 它应该支持剪贴板的基本操作例如复制剪切和粘贴。 它应该使用基本HTML输出文本。 它需要能够解析所生成的基本HTML以允许版本。 因此我发现的大多数解决方案都不符合我的需求因为它们要么是Eclipse Views / Editor要么太笨拙无法集成要么试图实现太多功能要么与我用来为自己的企业打上烙印的图标集不符应用程序尽管这是一个小问题但总括来说这为我做出决定提供了更多原因。 因此最后我最终决定基于StyledText SWT标准组件从头开始编写它并以该示例为起点。 RichText控件 几乎所有的工作都将由RichText控件本身执行。 我将添加一些支持类这些支持类将用作将结果文本格式化为基本HTML的助手以及图像本地化字符串等的提供者。 import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;import javax.xml.parsers.ParserConfigurationException;import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.swt.custom.ExtendedModifyEvent;
import org.eclipse.swt.custom.ExtendedModifyListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.xml.sax.SAXException;public class RichText extends Composite {private List cachedStyles Collections.synchronizedList(new LinkedList());private ToolBar toolBar;private StyledText styledText;private ToolItem boldBtn;private ToolItem italicBtn;private ToolItem strikeThroughBtn;private ToolItem underlineBtn;private ToolItem pasteBtn;private ToolItem eraserBtn;public RichText(Composite parent, int style) {super(parent, style);initComponents();}public void addCaretListener(CaretListener listener) {styledText.addCaretListener(listener);}public void removeCaretListener(CaretListener listener) {styledText.removeCaretListener(listener);}public void addExtendedModifyListener(ExtendedModifyListener listener) {styledText.addExtendedModifyListener(listener);}public void removeExtendedModifyListener(ExtendedModifyListener listener) {styledText.removeExtendedModifyListener(listener);}public void addModifyListener(ModifyListener listener) {styledText.addModifyListener(listener);}public void removeModifyListener(ModifyListener listener) {styledText.removeModifyListener(listener);}public void addVerifyKeyListener(VerifyKeyListener listener) {styledText.addVerifyKeyListener(listener);}public void removeVerifyKeyListener(VerifyKeyListener listener) {styledText.removeVerifyKeyListener(listener);}public void addVerifyListener(VerifyListener listener) {styledText.addVerifyListener(listener);}public void removeVerifyListener(VerifyListener listener) {styledText.removeVerifyListener(listener);}public int getCharCount() {return styledText.getCharCount();}public Caret getCaret() {return styledText.getCaret();}public int getCaretOffset() {return styledText.getCaretOffset();}/*** Obtain an HTML formatted text from the component contents** return an HTML formatted text*/public String getFormattedText() {String plainText styledText.getText();RichStringBuilder builder new RichStringBuilder();Integer[] lineBreaks getLineBreaks();int brIdx 0;int start 0;int end (lineBreaks.length brIdx ? lineBreaks[brIdx] : plainText.length() - 1);while (start end) {builder.startParagraph();StyleRange[] ranges styledText.getStyleRanges(start, (end - start));if (ranges ! null ranges.length 0) {for (int i 0;i ranges.length;i) {if (start ranges[i].start) {builder.append(plainText.substring(start, ranges[i].start));}List styles translateStyle(ranges[i]);builder.startFontStyles(styles.toArray(new FontStyle[styles.size()]));builder.append(plainText.substring(ranges[i].start,ranges[i].start ranges[i].length));builder.endFontStyles(styles.size());start (ranges[i].start ranges[i].length) 1;}}if (start end) {builder.append(plainText.substring(start, end));}start end styledText.getLineDelimiter().length();end (lineBreaks.length brIdx ? lineBreaks[brIdx] : plainText.length() - 1);builder.endParagraph();}return builder.toString();}public void setFormattedText(String text)throws ParserConfigurationException, SAXException, IOException {RichTextParser parser RichTextParser.parse(text);styledText.setText(parser.getText());styledText.setStyleRanges(parser.getStyleRanges());}public int getLineAtOffset(int offset) {return styledText.getLineAtOffset(offset);}public int getLineCount() {return styledText.getLineCount();}public int getLineSpacing() {return styledText.getLineSpacing();}public String getText() {return styledText.getText();}protected void applyFontStyleToSelection(FontStyle style) {Point sel styledText.getSelectionRange();if ((sel null) || (sel.y 0)) {return ;}StyleRange newStyle;for (int i sel.x; i (sel.x sel.y); i) {StyleRange range styledText.getStyleRangeAtOffset(i);if (range ! null) {newStyle (StyleRange) range.clone();newStyle.start i;newStyle.length 1;} else {newStyle new StyleRange(i, 1, null, null, SWT.NORMAL);}switch (style) {case BOLD:newStyle.fontStyle ^ SWT.BOLD;break;case ITALIC:newStyle.fontStyle ^ SWT.ITALIC;break;case STRIKE_THROUGH:newStyle.strikeout !newStyle.strikeout;break;case UNDERLINE:newStyle.underline !newStyle.underline;break;}styledText.setStyleRange(newStyle);}styledText.setSelectionRange(sel.x sel.y, 0);}/*** Clear all styled data*/protected void clearStylesFromSelection() {Point sel styledText.getSelectionRange();if ((sel ! null) (sel.y ! 0)) {StyleRange style new StyleRange(sel.x, sel.y, null, null, SWT.NORMAL);styledText.setStyleRange(style);}styledText.setSelectionRange(sel.x sel.y, 0);}private Integer[] getLineBreaks() {List list new ArrayList();int lastIdx 0;while (lastIdx styledText.getCharCount()) {int br styledText.getText().indexOf(styledText.getLineDelimiter(), lastIdx);if (br lastIdx !list.contains(br)) {list.add(br);}lastIdx styledText.getLineDelimiter().length() 1;}Collections.sort(list);return list.toArray(new Integer[list.size()]);}protected void handleCutCopy() {// Save the cut/copied style info so that during paste we will maintain// the style information. Cut/copied text is put in the clipboard in// RTF format, but is not pasted in RTF format. The other way to// handle the pasting of styles would be to access the Clipboard// directly and// parse the RTF text.cachedStyles Collections.synchronizedList(new LinkedList());Point sel styledText.getSelectionRange();int startX sel.x;for (int i sel.x; i sel.x sel.y - 1; i) {StyleRange style styledText.getStyleRangeAtOffset(i);if (style ! null) {style.start style.start - startX;if (!cachedStyles.isEmpty()) {StyleRange lastStyle cachedStyles.get(cachedStyles.size() - 1);if (lastStyle.similarTo(style) lastStyle.start lastStyle.length style.start) {lastStyle.length;} else {cachedStyles.add(style);}} else {cachedStyles.add(style);}}}pasteBtn.setEnabled(true);}private void handleExtendedModified(ExtendedModifyEvent event) {if (event.length 0) return;StyleRange style;if (event.length 1|| styledText.getTextRange(event.start, event.length).equals(styledText.getLineDelimiter())) {// Have the new text take on the style of the text to its right// (during// typing) if no style information is active.int caretOffset styledText.getCaretOffset();style null;if (caretOffset styledText.getCharCount())style styledText.getStyleRangeAtOffset(caretOffset);if (style ! null) {style (StyleRange) style.clone();style.start event.start;style.length event.length;} else {style new StyleRange(event.start, event.length, null, null,SWT.NORMAL);}if (boldBtn.getSelection())style.fontStyle | SWT.BOLD;if (italicBtn.getSelection())style.fontStyle | SWT.ITALIC;style.underline underlineBtn.getSelection();style.strikeout strikeThroughBtn.getSelection();if (!style.isUnstyled())styledText.setStyleRange(style);} else {// paste occurring, have text take on the styles it had when it was// cut/copiedfor (int i 0; i cachedStyles.size(); i) {style cachedStyles.get(i);StyleRange newStyle (StyleRange) style.clone();newStyle.start style.start event.start;styledText.setStyleRange(newStyle);}}}private void handleTextSelected(SelectionEvent event) {Point sel styledText.getSelectionRange();if ((sel ! null) (sel.y ! 0)) {StyleRange[] styles styledText.getStyleRanges(sel.x, sel.y);eraserBtn.setEnabled((styles ! null) (styles.length 0));} else {eraserBtn.setEnabled(false);}}private void handleKeyReleased(KeyEvent event) {if ((event.keyCode SWT.ARROW_LEFT) || (event.keyCode SWT.ARROW_UP)|| (event.keyCode SWT.ARROW_RIGHT) || (event.keyCode SWT.ARROW_DOWN)) {updateStyleButtons();}}private void updateStyleButtons() {int caretOffset styledText.getCaretOffset();StyleRange style null;if (caretOffset 0 caretOffset styledText.getCharCount()) {style styledText.getStyleRangeAtOffset(caretOffset);}if (style ! null) {boldBtn.setSelection((style.fontStyle SWT.BOLD) ! 0);italicBtn.setSelection((style.fontStyle SWT.ITALIC) ! 0);underlineBtn.setSelection(style.underline);strikeThroughBtn.setSelection(style.strikeout);} else {boldBtn.setSelection(false);italicBtn.setSelection(false);underlineBtn.setSelection(false);strikeThroughBtn.setSelection(false);}}private void initComponents() {GridLayout layout new GridLayout();layout.numColumns 1;setLayout(layout);toolBar createToolBar(this);toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));styledText new StyledText(this, SWT.BORDER | SWT.MULTI |SWT.V_SCROLL | SWT.H_SCROLL);styledText.setLayoutData(new GridData(GridData.FILL_BOTH));styledText.addKeyListener(new KeyAdapter() {Overridepublic void keyReleased(KeyEvent e) {handleKeyReleased(e);}});styledText.addExtendedModifyListener(new ExtendedModifyListener() {Overridepublic void modifyText(ExtendedModifyEvent event) {handleExtendedModified(event);}});styledText.addMouseListener(new MouseAdapter() {Overridepublic void mouseUp(MouseEvent e) {updateStyleButtons();}});styledText.addSelectionListener(new SelectionAdapter() {Overridepublic void widgetSelected(SelectionEvent event) {handleTextSelected(event);}});}private ToolBar createToolBar(Composite parent) {ToolBar toolBar new ToolBar(parent, SWT.FLAT);boldBtn new ToolItem(toolBar, SWT.CHECK);boldBtn.setImage(RichTextImages.IMG_BOLD);boldBtn.setToolTipText(RichTextStrings.boldBtn_tooltipText);boldBtn.addSelectionListener(new FontStyleButtonListener(FontStyle.BOLD));italicBtn new ToolItem(toolBar, SWT.CHECK);italicBtn.setImage(RichTextImages.IMG_ITALIC);italicBtn.setToolTipText(RichTextStrings.italicBtn_tooltipText);italicBtn.addSelectionListener(new FontStyleButtonListener(FontStyle.ITALIC));underlineBtn new ToolItem(toolBar, SWT.CHECK);underlineBtn.setImage(RichTextImages.IMG_UNDERLINE);underlineBtn.setToolTipText(RichTextStrings.underlineBtn_tooltipText);underlineBtn.addSelectionListener(new FontStyleButtonListener(FontStyle.UNDERLINE));strikeThroughBtn new ToolItem(toolBar, SWT.CHECK);strikeThroughBtn.setImage(RichTextImages.IMG_STRIKE_THROUGH);strikeThroughBtn.setToolTipText(RichTextStrings.strikeThroughBtn_tooltipText);strikeThroughBtn.addSelectionListener(new FontStyleButtonListener(FontStyle.STRIKE_THROUGH));new ToolItem(toolBar, SWT.SEPARATOR);ToolItem cutBtn new ToolItem(toolBar, SWT.PUSH);cutBtn.setImage(RichTextImages.IMG_CUT);cutBtn.setToolTipText(RichTextStrings.cutBtn_tooltipText);cutBtn.addSelectionListener(new SelectionAdapter() {Overridepublic void widgetSelected(SelectionEvent e) {handleCutCopy();styledText.cut();}});ToolItem copyBtn new ToolItem(toolBar, SWT.PUSH);copyBtn.setImage(RichTextImages.IMG_COPY);copyBtn.setToolTipText(RichTextStrings.copyBtn_tooltipText);copyBtn.addSelectionListener(new SelectionAdapter() {Overridepublic void widgetSelected(SelectionEvent e) {handleCutCopy();styledText.copy();}});pasteBtn new ToolItem(toolBar, SWT.PUSH);pasteBtn.setEnabled(false);pasteBtn.setImage(RichTextImages.IMG_PASTE);pasteBtn.setToolTipText(RichTextStrings.pasteBtn_tooltipText);pasteBtn.addSelectionListener(new SelectionAdapter() {Overridepublic void widgetSelected(SelectionEvent e) {styledText.paste();}});new ToolItem(toolBar, SWT.SEPARATOR);eraserBtn new ToolItem(toolBar, SWT.PUSH);eraserBtn.setEnabled(false);eraserBtn.setImage(RichTextImages.IMG_ERASER);eraserBtn.setToolTipText(RichTextStrings.eraserBtn_tooltipText);eraserBtn.addSelectionListener(new SelectionAdapter() {Overridepublic void widgetSelected(SelectionEvent e) {clearStylesFromSelection();}});return toolBar;}private List translateStyle(StyleRange range) {List list new ArrayList();if ((range.fontStyle SWT.BOLD) ! 0) {list.add(FontStyle.BOLD);}if ((range.fontStyle SWT.ITALIC) ! 0) {list.add(FontStyle.ITALIC);}if (range.strikeout) {list.add(FontStyle.STRIKE_THROUGH);}if (range.underline) {list.add(FontStyle.UNDERLINE);}return list;}private class FontStyleButtonListener extends SelectionAdapter {private FontStyle style;public FontStyleButtonListener(FontStyle style) {this.style style;}Overridepublic void widgetSelected(SelectionEvent e) {applyFontStyleToSelection(style);}}} 如您所见我们的RichText控件基本上是ToolBar和StyledText组件的包装具有不同事件侦听器的这两个控件都与这两个控件挂钩。 支持班 在本节中我将显示用于实现富文本编辑器控件使用的一些支持类的代码。 我将在这里省略为控件提供图像和本地化字符串的类因为那里有太多关于如何在SWT中进行操作的示例我将重点介绍格式化和解析控件的输出/输入所需的类。 第一个是Java枚举将用于标识支持的不同字体样式 public enum FontStyle {BOLD, ITALIC, STRIKE_THROUGH, UNDERLINE
} 下一个称为RichStringBuilder它将用作帮助程序类以将StyledText组件的内容格式化为基本HTML import java.util.Stack;public final class RichStringBuilder {public static final String LINE_DELIMITER br/;private StringBuilder builder;private Stack fontStyleStack;public RichStringBuilder() {builder new StringBuilder();fontStyleStack new Stack();}public RichStringBuilder append(String text) {builder.append(text);return this;}public RichStringBuilder appendLineBreak() {builder.append(LINE_DELIMITER);return this;}public RichStringBuilder startParagraph() {builder.append(p);return this;}public RichStringBuilder startFontStyle(FontStyle fontStyle) {fontStyleStack.push(fontStyle);internalStartFontStyle(fontStyle);return this;}public RichStringBuilder startFontStyles(FontStyle... fontStyles) {for (FontStyle fs : fontStyles) {startFontStyle(fs);}return this;}public RichStringBuilder endFontStyles(int count) {for (int i 0;i count;i) {endStyle();}return this;}public RichStringBuilder endStyle() {if (fontStyleStack.size() 0) {FontStyle style fontStyleStack.pop();internalEndFontStyle(style);}return this;}public RichStringBuilder endParagraph() {flushStyles();builder.append(/p);return this;}public void flushStyles() {while (fontStyleStack.size() 0) {endStyle();}}Overridepublic boolean equals(Object o) {if (this o) return true;if (null o) return false;if (!(o instanceof RichStringBuilder)) return false;return ((RichStringBuilder) o).builder.equals(builder);}Overridepublic int hashCode() {return builder.hashCode();}Overridepublic String toString() {return builder.toString();}private void internalStartFontStyle(FontStyle fontStyle) {switch (fontStyle) {case BOLD:builder.append(b);break;case ITALIC:builder.append(i);break;case STRIKE_THROUGH:builder.append(del);break;case UNDERLINE:builder.append(ins);break;}}private void internalEndFontStyle(FontStyle fontStyle) {switch (fontStyle) {case BOLD:builder.append(/b);break;case ITALIC:builder.append(/i);break;case STRIKE_THROUGH:builder.append(/del);break;case UNDERLINE:builder.append(/ins);break;}}} 第三个是基于SAX的内容处理程序它将基本HTML解析为StyledText控件时将启动事件 import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;public final class RichTextParser {public static RichTextParser parse(String formattedText)throws ParserConfigurationException, SAXException, IOException {return new RichTextParser(formattedText);}private StringBuilder text new StringBuilder();private List styleRanges new ArrayList();private RichTextParser(String formattedText)throws ParserConfigurationException, SAXException, IOException {StringReader reader new StringReader(formattedText);SAXParserFactory factory SAXParserFactory.newInstance();SAXParser parser factory.newSAXParser();DefaultHandler handler new RichTextContentHandler();parser.parse(new InputSource(reader), handler);}public String getText() {return text.toString();}public StyleRange[] getStyleRanges() {return styleRanges.toArray(new StyleRange[styleRanges.size()]);}private class RichTextContentHandler extends DefaultHandler {private StackList stylesStack new StackList();private String lastTextChunk null;Overridepublic void characters(char[] ch, int start, int length)throws SAXException {lastTextChunk new String(ch, start, length);}Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {// If there is not any previous text chunk parsed then returnif (lastTextChunk null) return;// If the tag found is not a supported one then returnif (!p.equals(qName) || !b.equals(qName) || !i.equals(qName) ||!ins.equals(qName) || !del.equals(qName)) {return;}List lastStyles lastFontStyles(true);if (lastStyles ! null) {StyleRange range transform(lastStyles);range.start currentIndex() 1;range.length lastTextChunk.length();styleRanges.add(range);}text.append(lastTextChunk);lastTextChunk null;}Overridepublic void startElement(String uri, String localName, String qName,Attributes atts) throws SAXException {// If the tag found is not a supported one then returnif (!p.equals(qName) || !b.equals(qName) || !i.equals(qName) ||!ins.equals(qName) || !del.equals(qName)) {return;}List lastStyles lastFontStyles(false);if (lastTextChunk null) {if (lastStyles null) {lastStyles new ArrayList();stylesStack.add(lastStyles);}} else {if (lastStyles ! null) {StyleRange range transform(lastStyles);range.start currentIndex() 1;range.length lastTextChunk.length();styleRanges.add(range);}text.append(lastTextChunk);lastTextChunk null;}if (b.equals(qName)) {lastStyles.add(FontStyle.BOLD);} else if (i.equals(qName)) {lastStyles.add(FontStyle.ITALIC);} else if (ins.equals(qName)) {lastStyles.add(FontStyle.UNDERLINE);} else {lastStyles.add(FontStyle.STRIKE_THROUGH);}}private StyleRange transform(List styles) {StyleRange range new StyleRange();range.start currentIndex() 1;range.length lastTextChunk.length();for (FontStyle fs : styles) {if (FontStyle.BOLD fs) {range.fontStyle (range.fontStyle SWT.BOLD);} else if (FontStyle.ITALIC fs) {range.fontStyle (range.fontStyle SWT.ITALIC);} else if (FontStyle.STRIKE_THROUGH fs) {range.strikeout true;} else if (FontStyle.UNDERLINE fs) {range.underline true;}}return range;}private List lastFontStyles(boolean remove) {List lastStyles null;if (stylesStack.size() 0) {if (remove) {lastStyles stylesStack.pop();} else {lastStyles stylesStack.peek();}}return lastStyles;}private int currentIndex() {return text.length() - 1;}}} 结论 实现您自己的SWT RichText控件可能不是满足您需求的最佳选择您将需要权衡这样做的利弊以及是否有必要投资其中一种现成的商业解决方案。 但是我想通过本文演示如何在SWT对话框和视图中嵌入您自己的简单且轻量级的富文本编辑器与从中获得的好处相比它很容易实现并且需要花费很少的精力。 参考来自Code Nibbles博客的JCG合作伙伴 Alonso Dominguez的RichText编辑器组件用于基于SWT的应用程序 。 翻译自: https://www.javacodegeeks.com/2012/07/richtext-editor-component-for-swt-based.html