/** * OpenKM, Open Document Management System (http://www.openkm.com) * Copyright (c) 2006-2011 Paco Avila & Josep Llort * * No bytes were intentionally harmed during the development of this application. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.openkm.frontend.client.widget.richtext; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.IFrameElement; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.PushButton; import com.google.gwt.user.client.ui.RichTextArea; import com.google.gwt.user.client.ui.ToggleButton; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.RichTextArea.Formatter; import com.openkm.frontend.client.Main; import com.openkm.frontend.client.util.OKMBundleResources; /** * @author jllort * */ public class RichTextToolbar extends Composite implements RichTextAction { //HTML Related (styles made by SPAN and DIV) private static final String HTML_STYLE_CLOSE_SPAN = "</span>"; private static final String HTML_STYLE_CLOSE_DIV = "</div>"; private static final String HTML_STYLE_OPEN_BOLD = "<span style=\"font-weight: bold;\">"; private static final String HTML_STYLE_OPEN_ITALIC = "<span style=\"font-weight: italic;\">"; private static final String HTML_STYLE_OPEN_UNDERLINE = "<span style=\"font-weight: underline;\">"; private static final String HTML_STYLE_OPEN_LINETHROUGH = "<span style=\"font-weight: line-through;\">"; private static final String HTML_STYLE_OPEN_ALIGNLEFT = "<div style=\"text-align: left;\">"; private static final String HTML_STYLE_OPEN_ALIGNCENTER = "<div style=\"text-align: center;\">"; private static final String HTML_STYLE_OPEN_ALIGNRIGHT = "<div style=\"text-align: right;\">"; private static final String HTML_STYLE_OPEN_INDENTRIGHT = "<div style=\"margin-left: 40px;\">"; //HTML Related (styles made by custom HTML-Tags) private static final String HTML_STYLE_OPEN_SUBSCRIPT = "<sub>"; private static final String HTML_STYLE_CLOSE_SUBSCRIPT = "</sub>"; private static final String HTML_STYLE_OPEN_SUPERSCRIPT = "<sup>"; private static final String HTML_STYLE_CLOSE_SUPERSCRIPT = "</sup>"; private static final String HTML_STYLE_OPEN_ORDERLIST = "<ol><li>"; private static final String HTML_STYLE_CLOSE_ORDERLIST = "</ol></li>"; private static final String HTML_STYLE_OPEN_UNORDERLIST = "<ul><li>"; private static final String HTML_STYLE_CLOSE_UNORDERLIST = "</ul></li>"; //HTML Related (styles without closing Tag) private static final String HTML_STYLE_HLINE = "<hr style=\"width: 100%; height: 2px;\">"; /** Private Variables **/ //The main (Vertical)-Panel and the two inner (Horizontal)-Panels private VerticalPanel outer; private HorizontalPanel topPanel; private HorizontalPanel bottomPanel; //The RichTextArea this Toolbar referes to and the Interfaces to access the RichTextArea private RichTextArea styleText; private Formatter styleTextFormatter; //We use an internal class of the ClickHandler and the KeyUpHandler to be private to others with these events private EventHandler evHandler; //The Buttons of the Menubar private ToggleButton bold; private ToggleButton italic; private ToggleButton underline; private ToggleButton stroke; private ToggleButton subscript; private ToggleButton superscript; private PushButton alignleft; private PushButton alignmiddle; private PushButton justify; private PushButton alignright; private PushButton orderlist; private PushButton unorderlist; private PushButton indentleft; private PushButton indentright; private PushButton generatelink; private PushButton breaklink; private PushButton insertline; private PushButton insertimage; private PushButton removeformatting; private ToggleButton texthtml; private ListBox fontList; private ListBox colorlist; RichTextPopup richTextPopup; /** Constructor of the Toolbar **/ public RichTextToolbar(final RichTextArea richtext) { //Initialize the main-panel outer = new VerticalPanel(); //Initialize the two inner panels topPanel = new HorizontalPanel(); bottomPanel = new HorizontalPanel(); topPanel.setStyleName("RichTextToolbar"); bottomPanel.setStyleName("RichTextToolbar"); //Save the reference to the RichText area we refer to and get the interfaces to the stylings styleText = richtext; styleTextFormatter = styleText.getFormatter(); //Set some graphical options, so this toolbar looks how we like it. topPanel.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT); bottomPanel.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT); //Add the two inner panels to the main panel outer.add(topPanel); outer.add(bottomPanel); //Some graphical stuff to the main panel and the initialisation of the new widget outer.setStyleName("RichTextToolbar"); initWidget(outer); // evHandler = new EventHandler(); //Add KeyUp and Click-Handler to the RichText, so that we can actualize the toolbar if neccessary styleText.addKeyUpHandler(evHandler); styleText.addClickHandler(evHandler); // Changing styles IFrameElement e = IFrameElement.as(richtext.getElement()); e.setSrc("iframe_richtext.html"); e.setFrameBorder(0); // removing frame border richTextPopup = new RichTextPopup(this); richTextPopup.setWidth("300px"); richTextPopup.setHeight("50px"); richTextPopup.setStyleName("okm-Popup"); //Now lets fill the new toolbar with life buildTools(); } /** Click Handler of the Toolbar **/ private class EventHandler implements ClickHandler,KeyUpHandler, ChangeHandler { public void onClick(ClickEvent event) { if (event.getSource().equals(bold)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_BOLD,HTML_STYLE_CLOSE_SPAN); } else { styleTextFormatter.toggleBold(); } } else if (event.getSource().equals(italic)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_ITALIC,HTML_STYLE_CLOSE_SPAN); } else { styleTextFormatter.toggleItalic(); } } else if (event.getSource().equals(underline)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_UNDERLINE,HTML_STYLE_CLOSE_SPAN); } else { styleTextFormatter.toggleUnderline(); } } else if (event.getSource().equals(stroke)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_LINETHROUGH,HTML_STYLE_CLOSE_SPAN); } else { styleTextFormatter.toggleStrikethrough(); } } else if (event.getSource().equals(subscript)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_SUBSCRIPT,HTML_STYLE_CLOSE_SUBSCRIPT); } else { styleTextFormatter.toggleSubscript(); } } else if (event.getSource().equals(superscript)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_SUPERSCRIPT,HTML_STYLE_CLOSE_SUPERSCRIPT); } else { styleTextFormatter.toggleSuperscript(); } } else if (event.getSource().equals(alignleft)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_ALIGNLEFT,HTML_STYLE_CLOSE_DIV); } else { styleTextFormatter.setJustification(RichTextArea.Justification.LEFT); } } else if (event.getSource().equals(alignmiddle)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_ALIGNCENTER,HTML_STYLE_CLOSE_DIV); } else { styleTextFormatter.setJustification(RichTextArea.Justification.CENTER); } } else if (event.getSource().equals(justify)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_ALIGNCENTER,HTML_STYLE_CLOSE_DIV); } else { styleTextFormatter.setJustification(RichTextArea.Justification.FULL); } } else if (event.getSource().equals(alignright)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_ALIGNRIGHT,HTML_STYLE_CLOSE_DIV); } else { styleTextFormatter.setJustification(RichTextArea.Justification.RIGHT); } } else if (event.getSource().equals(orderlist)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_ORDERLIST,HTML_STYLE_CLOSE_ORDERLIST); } else { styleTextFormatter.insertOrderedList(); } } else if (event.getSource().equals(unorderlist)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_UNORDERLIST,HTML_STYLE_CLOSE_UNORDERLIST); } else { styleTextFormatter.insertUnorderedList(); } } else if (event.getSource().equals(indentright)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_OPEN_INDENTRIGHT,HTML_STYLE_CLOSE_DIV); } else { styleTextFormatter.rightIndent(); } } else if (event.getSource().equals(indentleft)) { if (isHTMLMode()) { } else { styleTextFormatter.leftIndent(); } } else if (event.getSource().equals(generatelink)) { richTextPopup.setAction(RichTextPopup.ACTION_ENTER_URL); richTextPopup.show(); } else if (event.getSource().equals(breaklink)) { if (isHTMLMode()) { } else { styleTextFormatter.removeLink(); } } else if (event.getSource().equals(insertimage)) { richTextPopup.setAction(RichTextPopup.ACTION_ENTER_IMAGE_URL); richTextPopup.show(); } else if (event.getSource().equals(insertline)) { if (isHTMLMode()) { changeHtmlStyle(HTML_STYLE_HLINE,""); } else { styleTextFormatter.insertHorizontalRule(); } } else if (event.getSource().equals(removeformatting)) { if (isHTMLMode()) { } else { styleTextFormatter.removeFormat(); } } else if (event.getSource().equals(texthtml)) { if (texthtml.isDown()) { styleText.setText(styleText.getHTML()); } else { styleText.setHTML(styleText.getText()); } } else if (event.getSource().equals(styleText)) { //Change invoked by the richtextArea } updateStatus(); } public void onKeyUp(KeyUpEvent event) { updateStatus(); } public void onChange(ChangeEvent event) { if (event.getSource().equals(fontList)) { if (isHTMLMode()) { changeHtmlStyle("<span style=\"font-family: "+fontList.getValue(fontList.getSelectedIndex())+";\">",HTML_STYLE_CLOSE_SPAN); } else { styleTextFormatter.setFontName(fontList.getValue(fontList.getSelectedIndex())); } } else if (event.getSource().equals(colorlist)) { if (isHTMLMode()) { changeHtmlStyle("<span style=\"color: "+colorlist.getValue(colorlist.getSelectedIndex())+";\">",HTML_STYLE_CLOSE_SPAN); } else { styleTextFormatter.setForeColor(colorlist.getValue(colorlist.getSelectedIndex())); } } } } /** Native JavaScript that returns the selected text and position of the start **/ public static native JsArrayString getSelection(Element elem) /*-{ var txt = ""; var pos = 0; var range; var parentElement; var container; if (elem.contentWindow.getSelection) { txt = elem.contentWindow.getSelection(); pos = elem.contentWindow.getSelection().getRangeAt(0).startOffset; } else if (elem.contentWindow.document.getSelection) { txt = elem.contentWindow.document.getSelection(); pos = elem.contentWindow.document.getSelection().getRangeAt(0).startOffset; } else if (elem.contentWindow.document.selection) { range = elem.contentWindow.document.selection.createRange(); txt = range.text; parentElement = range.parentElement(); container = range.duplicate(); container.moveToElementText(parentElement); container.setEndPoint('EndToEnd', range); pos = container.text.length - range.text.length; } return [""+txt,""+pos]; }-*/; /** Method called to toggle the style in HTML-Mode **/ private void changeHtmlStyle(String startTag, String stopTag) { JsArrayString tx = getSelection(styleText.getElement()); String txbuffer = styleText.getText(); Integer startpos = Integer.parseInt(tx.get(1)); String selectedText = tx.get(0); styleText.setText(txbuffer.substring(0, startpos)+startTag+selectedText+stopTag+txbuffer.substring(startpos+selectedText.length())); } /** Private method with a more understandable name to get if HTML mode is on or not **/ private Boolean isHTMLMode() { return texthtml.isDown(); } /** Private method to set the toggle buttons and disable/enable buttons which do not work in html-mode **/ private void updateStatus() { if (styleTextFormatter != null) { bold.setDown(styleTextFormatter.isBold()); italic.setDown(styleTextFormatter.isItalic()); underline.setDown(styleTextFormatter.isUnderlined()); subscript.setDown(styleTextFormatter.isSubscript()); superscript.setDown(styleTextFormatter.isSuperscript()); stroke.setDown(styleTextFormatter.isStrikethrough()); } if (isHTMLMode()) { removeformatting.setEnabled(false); indentleft.setEnabled(false); breaklink.setEnabled(false); } else { removeformatting.setEnabled(true); indentleft.setEnabled(true); breaklink.setEnabled(true); } } /** Initialize the options on the toolbar **/ private void buildTools() { //Init the TOP Panel forst topPanel.add(bold = createToggleButton(OKMBundleResources.INSTANCE.bold(),Main.i18n("richtext.bold"))); topPanel.add(italic = createToggleButton(OKMBundleResources.INSTANCE.italic(),Main.i18n("richtext.italic"))); topPanel.add(underline = createToggleButton(OKMBundleResources.INSTANCE.underline(),Main.i18n("richtext.underline"))); topPanel.add(stroke = createToggleButton(OKMBundleResources.INSTANCE.stroke(),Main.i18n("richtext.stroke"))); topPanel.add(new HTML(" ")); topPanel.add(subscript = createToggleButton(OKMBundleResources.INSTANCE.subScript(),Main.i18n("richtext.subscript"))); topPanel.add(superscript = createToggleButton(OKMBundleResources.INSTANCE.superScript(),Main.i18n("richtext.superscript"))); topPanel.add(new HTML(" ")); topPanel.add(alignleft = createPushButton(OKMBundleResources.INSTANCE.justifyLeft(),Main.i18n("richtext.justify.left"))); topPanel.add(alignmiddle = createPushButton(OKMBundleResources.INSTANCE.justifyCenter(),Main.i18n("richtext.justify.center"))); topPanel.add(justify = createPushButton(OKMBundleResources.INSTANCE.justify(),Main.i18n("richtext.justify"))); topPanel.add(alignright = createPushButton(OKMBundleResources.INSTANCE.justifyRight(),Main.i18n("richtext.justify.right"))); topPanel.add(new HTML(" ")); topPanel.add(orderlist = createPushButton(OKMBundleResources.INSTANCE.ordered(),Main.i18n("richtext.list.ordered"))); topPanel.add(unorderlist = createPushButton(OKMBundleResources.INSTANCE.unOrdered(),Main.i18n("richtext.list.unordered"))); topPanel.add(indentright = createPushButton(OKMBundleResources.INSTANCE.identRight(),Main.i18n("richtext.ident.right"))); topPanel.add(indentleft = createPushButton(OKMBundleResources.INSTANCE.identLeft(),Main.i18n("richtext.ident.left"))); topPanel.add(new HTML(" ")); topPanel.add(generatelink = createPushButton(OKMBundleResources.INSTANCE.createEditorLink(),Main.i18n("richtext.link.create"))); topPanel.add(breaklink = createPushButton(OKMBundleResources.INSTANCE.breakEditorLink(),Main.i18n("richtext.link.break"))); topPanel.add(new HTML(" ")); topPanel.add(insertline = createPushButton(OKMBundleResources.INSTANCE.line(),Main.i18n("richtext.line"))); topPanel.add(insertimage = createPushButton(OKMBundleResources.INSTANCE.picture(),Main.i18n("richtext.image"))); topPanel.add(new HTML(" ")); topPanel.add(removeformatting = createPushButton(OKMBundleResources.INSTANCE.removeFormat(),Main.i18n("richtext.remove.format"))); topPanel.add(new HTML(" ")); topPanel.add(texthtml = createToggleButton(OKMBundleResources.INSTANCE.html(),Main.i18n("richtext.switch.view"))); //Init the BOTTOM Panel fontList = new ListBox(); fontList.addChangeHandler(evHandler); fontList.setVisibleItemCount(1); refreshFontList(); colorlist = new ListBox(); colorlist.addChangeHandler(evHandler); colorlist.setVisibleItemCount(1); refreshColorList(); bottomPanel.add(fontList); bottomPanel.add(new HTML(" ")); bottomPanel.add(colorlist); fontList.setStyleName("okm-Input"); colorlist.setStyleName("okm-Input"); } /** Method to create a Toggle button for the toolbar **/ private ToggleButton createToggleButton(ImageResource resource, String tip) { ToggleButton tb = new ToggleButton(new Image(resource)); tb.addClickHandler(evHandler); if (tip != null) { tb.setTitle(tip); } return tb; } /** Method to create a Push button for the toolbar **/ private PushButton createPushButton(ImageResource resource, String tip) { PushButton tb = new PushButton(new Image(resource)); tb.addClickHandler(evHandler); if (tip != null) { tb.setTitle(tip); } return tb; } /** Method to refresh the fontlist values for the toolbar **/ private void refreshFontList() { fontList.clear(); fontList.addItem(Main.i18n("richtext.fonts")); fontList.addItem("Times New Roman", "Times New Roman"); fontList.addItem("Arial", "Arial"); fontList.addItem("Courier New", "Courier New"); fontList.addItem("Georgia", "Georgia"); fontList.addItem("Trebuchet", "Trebuchet"); fontList.addItem("Verdana", "Verdana"); } /** Method to refresh the colorlist for the toolbar **/ private void refreshColorList() { colorlist.clear(); colorlist.addItem(Main.i18n("richtext.colors")); colorlist.addItem(Main.i18n("richtext.white"), "#FFFFFF"); colorlist.addItem(Main.i18n("richtext.black"), "#000000"); colorlist.addItem(Main.i18n("richtext.red"), "red"); colorlist.addItem(Main.i18n("richtext.green"), "green"); colorlist.addItem(Main.i18n("richtext.yellow"), "yellow"); colorlist.addItem(Main.i18n("richtext.blue"), "blue"); } /** * langRefresh */ public void langRefresh() { refreshFontList(); refreshColorList(); bold.setTitle(Main.i18n("richtext.bold")); italic.setTitle(Main.i18n("richtext.italic")); underline.setTitle(Main.i18n("richtext.underline")); stroke.setTitle(Main.i18n("richtext.stroke")); subscript.setTitle(Main.i18n("richtext.subscript")); superscript.setTitle(Main.i18n("richtext.superscript")); alignleft.setTitle(Main.i18n("richtext.justify.left")); alignmiddle.setTitle(Main.i18n("richtext.justify.center")); justify.setTitle(Main.i18n("richtext.justify")); alignright.setTitle(Main.i18n("richtext.justify.right")); orderlist.setTitle(Main.i18n("richtext.list.ordered")); unorderlist.setTitle(Main.i18n("richtext.list.unordered")); indentright.setTitle(Main.i18n("richtext.ident.right")); indentleft.setTitle(Main.i18n("richtext.ident.left")); generatelink.setTitle(Main.i18n("richtext.link.create")); breaklink.setTitle(Main.i18n("richtext.link.break")); insertline.setTitle(Main.i18n("richtext.line")); insertimage.setTitle(Main.i18n("richtext.image")); removeformatting.setTitle(Main.i18n("richtext.remove.format")); texthtml.setTitle(Main.i18n("richtext.switch.view")); richTextPopup.langRefresh(); } @Override public void insertURL(String url) { if (url != null) { if (isHTMLMode()) { changeHtmlStyle("<a href=\""+url+"\">","</a>"); } else { styleTextFormatter.createLink(url); } } } @Override public void insertImageURL(String url) { if (url != null) { if (isHTMLMode()) { changeHtmlStyle("<img src=\""+url+"\">",""); } else { styleTextFormatter.insertImage(url); } } } }