com.sun.javafx.scene.web.skin.HTMLEditorSkin Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.scene.web.skin;
import java.util.ResourceBundle;
import com.sun.javafx.application.PlatformImpl;
import org.w3c.dom.html.HTMLDocument;
import org.w3c.dom.html.HTMLElement;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.css.StyleableProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.NodeOrientation;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.Separator;
import javafx.scene.control.TextInputControl;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.web.HTMLEditor;
import javafx.scene.web.WebView;
import javafx.util.Callback;
import com.sun.javafx.PlatformUtil;
import com.sun.javafx.scene.control.behavior.BehaviorBase;
import com.sun.javafx.scene.control.skin.ColorPickerSkin;
import com.sun.javafx.scene.control.skin.FXVK;
import com.sun.javafx.scene.web.behavior.HTMLEditorBehavior;
import com.sun.javafx.scene.traversal.TraversalEngine;
import com.sun.javafx.scene.traversal.TraverseListener;
import com.sun.webkit.WebPage;
import com.sun.webkit.event.WCFocusEvent;
import com.sun.javafx.webkit.Accessor;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import com.sun.javafx.scene.control.skin.BehaviorSkinBase;
import javafx.collections.ListChangeListener;
import static javafx.geometry.NodeOrientation.*;
import javafx.print.PrinterJob;
/**
* HTML editor skin.
*/
public class HTMLEditorSkin extends BehaviorSkinBase implements TraverseListener {
private GridPane gridPane;
private ToolBar toolbar1;
private ToolBar toolbar2;
private Button cutButton;
private Button copyButton;
private Button pasteButton;
// private Button undoButton;
// private Button redoButton;
private Button insertHorizontalRuleButton;
private ToggleGroup alignmentToggleGroup;
private ToggleButton alignLeftButton;
private ToggleButton alignCenterButton;
private ToggleButton alignRightButton;
private ToggleButton alignJustifyButton;
private ToggleButton bulletsButton;
private ToggleButton numbersButton;
private Button indentButton;
private Button outdentButton;
private ComboBox formatComboBox;
private Map formatStyleMap;
private Map styleFormatMap;
private ComboBox fontFamilyComboBox;
private ComboBox fontSizeComboBox;
private Map fontSizeMap;
private Map sizeFontMap;
private ToggleButton boldButton;
private ToggleButton italicButton;
private ToggleButton underlineButton;
private ToggleButton strikethroughButton;
private ColorPicker fgColorButton;
private ColorPicker bgColorButton;
private WebView webView;
private WebPage webPage;
private static final String CUT_COMMAND = "cut";
private static final String COPY_COMMAND = "copy";
private static final String PASTE_COMMAND = "paste";
private static final String UNDO_COMMAND = "undo";
private static final String REDO_COMMAND = "redo";
private static final String INSERT_HORIZONTAL_RULE_COMMAND = "inserthorizontalrule";
private static final String ALIGN_LEFT_COMMAND = "justifyleft";
private static final String ALIGN_CENTER_COMMAND = "justifycenter";
private static final String ALIGN_RIGHT_COMMAND = "justifyright";
private static final String ALIGN_JUSTIFY_COMMAND = "justifyfull";
private static final String BULLETS_COMMAND = "insertUnorderedList";
private static final String NUMBERS_COMMAND = "insertOrderedList";
private static final String INDENT_COMMAND = "indent";
private static final String OUTDENT_COMMAND = "outdent";
private static final String FORMAT_COMMAND = "formatblock";
private static final String FONT_FAMILY_COMMAND = "fontname";
private static final String FONT_SIZE_COMMAND = "fontsize";
private static final String BOLD_COMMAND = "bold";
private static final String ITALIC_COMMAND = "italic";
private static final String UNDERLINE_COMMAND = "underline";
private static final String STRIKETHROUGH_COMMAND = "strikethrough";
private static final String FOREGROUND_COLOR_COMMAND = "forecolor";
private static final String BACKGROUND_COLOR_COMMAND = "backcolor";
private static final Color DEFAULT_BG_COLOR = Color.WHITE;
private static final Color DEFAULT_FG_COLOR = Color.BLACK;
private static final String FORMAT_PARAGRAPH = "";
private static final String FORMAT_HEADING_1 = "
";
private static final String FORMAT_HEADING_2 = "";
private static final String FORMAT_HEADING_3 = "";
private static final String FORMAT_HEADING_4 = "";
private static final String FORMAT_HEADING_5 = "";
private static final String FORMAT_HEADING_6 = "";
private static final String SIZE_XX_SMALL = "1";
private static final String SIZE_X_SMALL = "2";
private static final String SIZE_SMALL = "3";
private static final String SIZE_MEDIUM = "4";
private static final String SIZE_LARGE = "5";
private static final String SIZE_X_LARGE = "6";
private static final String SIZE_XX_LARGE = "7";
private static final String INSERT_NEW_LINE_COMMAND = "insertnewline";
private static final String INSERT_TAB_COMMAND = "inserttab";
// As per RT-16330: default format -> bold/size mappings are as follows:
private static final String[][] DEFAULT_FORMAT_MAPPINGS = {
{ FORMAT_PARAGRAPH, "", SIZE_SMALL },
{ FORMAT_HEADING_1, BOLD_COMMAND, SIZE_X_LARGE },
{ FORMAT_HEADING_2, BOLD_COMMAND, SIZE_LARGE },
{ FORMAT_HEADING_3, BOLD_COMMAND, SIZE_MEDIUM },
{ FORMAT_HEADING_4, BOLD_COMMAND, SIZE_SMALL },
{ FORMAT_HEADING_5, BOLD_COMMAND, SIZE_X_SMALL },
{ FORMAT_HEADING_6, BOLD_COMMAND, SIZE_XX_SMALL },
};
// As per RT-16379: default OS -> font mappings:
private static final String[] DEFAULT_WINDOWS_7_MAPPINGS = {
"Windows 7", "Segoe UI", "12px", "", "120"
};
private static final String[][] DEFAULT_OS_MAPPINGS = {
// OS Font name size weight DPI
{ "Windows XP", "Tahoma", "12px", "", "96" },
{ "Windows Vista", "Segoe UI", "12px", "", "96" },
DEFAULT_WINDOWS_7_MAPPINGS,
{ "Mac OS X", "Lucida Grande", "12px", "", "72" },
{ "Linux", "Lucida Sans", "12px", "", "96" },
};
private static final String DEFAULT_OS_FONT = getOSMappings()[1];
private static String[] getOSMappings() {
String os = System.getProperty("os.name");
for (int i = 0; i < DEFAULT_OS_MAPPINGS.length; i++) {
if (os.equals(DEFAULT_OS_MAPPINGS[i][0])) {
return DEFAULT_OS_MAPPINGS[i];
}
}
return DEFAULT_WINDOWS_7_MAPPINGS;
}
private TraversalEngine engine;
private boolean resetToolbarState = false;
private String cachedHTMLText = "";
private ListChangeListener itemsListener = new ListChangeListener() {
@Override public void onChanged(ListChangeListener.Change c) {
while (c.next()) {
if (c.getRemovedSize() > 0) {
for (Node n : c.getList()) {
if (n instanceof WebView) {
// RT-28611 webView removed - set associated webPage to null
webPage.dispose();
}
}
}
}
}
};
public HTMLEditorSkin(HTMLEditor htmlEditor) {
super(htmlEditor, new HTMLEditorBehavior(htmlEditor));
getChildren().clear();
gridPane = new GridPane();
gridPane.getStyleClass().add("grid");
getChildren().addAll(gridPane);
toolbar1 = new ToolBar();
toolbar1.getStyleClass().add("top-toolbar");
gridPane.add(toolbar1, 0, 0);
toolbar2 = new ToolBar();
toolbar2.getStyleClass().add("bottom-toolbar");
gridPane.add(toolbar2, 0, 1);
// populateToolbars();
webView = new WebView();
gridPane.add(webView, 0, 2);
ColumnConstraints column = new ColumnConstraints();
column.setHgrow(Priority.ALWAYS);
gridPane.getColumnConstraints().add(column);
webPage = Accessor.getPageFor(webView.getEngine());
webView.addEventHandler(MouseEvent.ANY, new EventHandler() {
@Override public void handle(MouseEvent event) {
Platform.runLater(new Runnable() {
@Override public void run() {
updateToolbarState(true);
}
});
}
});
webView.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler() {
@Override public void handle(final KeyEvent event) {
applyTextFormatting();
if (event.getCode() == KeyCode.CONTROL || event.getCode() == KeyCode.META) {
return;
}
if (event.getCode() == KeyCode.TAB && !event.isControlDown()) {
if (!event.isShiftDown()) {
/*
** if we are in either Bullet or Numbers mode then the
** TAB key tells us to indent again.
*/
if (getCommandState(BULLETS_COMMAND) || getCommandState(NUMBERS_COMMAND)) {
executeCommand(INDENT_COMMAND, null);
}
else {
executeCommand(INSERT_TAB_COMMAND, null);
}
}
else {
/*
** if we are in either Bullet or Numbers mode then the
** Shift-TAB key tells us to outdent.
*/
if (getCommandState(BULLETS_COMMAND) || getCommandState(NUMBERS_COMMAND)) {
executeCommand(OUTDENT_COMMAND, null);
}
}
return;
}
// Work around for bug that sends events from ColorPicker to this Scene
if ((fgColorButton != null && fgColorButton.isShowing()) ||
(bgColorButton != null && bgColorButton.isShowing())) {
return;
}
Platform.runLater(new Runnable() {
@Override public void run() {
if (webPage.getClientSelectedText().isEmpty()) {
if (event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN ||
event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT ||
event.getCode() == KeyCode.HOME || event.getCode() == KeyCode.END) {
updateToolbarState(true);
} else if (event.isControlDown() || event.isMetaDown()) {
if (event.getCode() == KeyCode.B) {
keyboardShortcuts(BOLD_COMMAND);
} else if(event.getCode() == KeyCode.I) {
keyboardShortcuts(ITALIC_COMMAND);
} else if (event.getCode() == KeyCode.U) {
keyboardShortcuts(UNDERLINE_COMMAND);
}
updateToolbarState(true);
} else {
resetToolbarState = event.getCode() == KeyCode.ENTER;
if (resetToolbarState) {
if (getCommandState(BOLD_COMMAND) != boldButton.selectedProperty().getValue()) {
executeCommand(BOLD_COMMAND, boldButton.selectedProperty().getValue().toString());
}
}
updateToolbarState(false);
}
resetToolbarState = false;
}
else if (event.isShiftDown() &&
(event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN ||
event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT)) {
updateToolbarState(true);
}
}
});
}
});
webView.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler() {
@Override public void handle(final KeyEvent event) {
if (event.getCode() == KeyCode.CONTROL || event.getCode() == KeyCode.META) {
return;
}
// Work around for bug that sends events from ColorPicker to this Scene
if ((fgColorButton != null && fgColorButton.isShowing()) ||
(bgColorButton != null && bgColorButton.isShowing())) {
return;
}
Platform.runLater(new Runnable() {
@Override public void run() {
if (webPage.getClientSelectedText().isEmpty()) {
if (event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN ||
event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT ||
event.getCode() == KeyCode.HOME || event.getCode() == KeyCode.END) {
updateToolbarState(true);
} else if (event.isControlDown() || event.isMetaDown()) {
if (event.getCode() == KeyCode.B) {
keyboardShortcuts(BOLD_COMMAND);
} else if(event.getCode() == KeyCode.I) {
keyboardShortcuts(ITALIC_COMMAND);
} else if (event.getCode() == KeyCode.U) {
keyboardShortcuts(UNDERLINE_COMMAND);
}
updateToolbarState(true);
} else {
resetToolbarState = event.getCode() == KeyCode.ENTER;
if (!resetToolbarState) {
updateToolbarState(false);
}
}
resetToolbarState = false;
}
}
});
}
});
getSkinnable().focusedProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Boolean oldValue, final Boolean newValue) {
Platform.runLater(new Runnable() {
@Override public void run() {
if (newValue) {
webView.requestFocus();
}
}
});
}
});
webView.focusedProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Boolean oldValue, final Boolean newValue) {
// disabling as a fix for RT-30081
// if (newValue) {
// webPage.dispatchFocusEvent(new WCFocusEvent(WCFocusEvent.FOCUS_GAINED, WCFocusEvent.FORWARD));
// enableToolbar(true);
// } else {
// webPage.dispatchFocusEvent(new WCFocusEvent(WCFocusEvent.FOCUS_LOST, WCFocusEvent.FORWARD));
// enableToolbar(false);
// }
Platform.runLater(new Runnable() {
@Override public void run() {
updateToolbarState(true);
if (PlatformImpl.isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) {
Scene scene = getSkinnable().getScene();
if (newValue) {
FXVK.attach(webView);
} else if (scene == null ||
scene.getWindow() == null ||
!scene.getWindow().isFocused() ||
!(scene.getFocusOwner() instanceof TextInputControl /*||
getScene().getFocusOwner() instanceof WebView*/)) {
FXVK.detach();
}
}
}
});
}
});
webView.getEngine().getLoadWorker().workDoneProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, final Number oldValue, final Number newValue) {
Platform.runLater(new Runnable() {
@Override public void run() {
webView.requestLayout();
}
});
double totalWork = webView.getEngine().getLoadWorker().getTotalWork();
if (newValue.doubleValue() == totalWork) {
cachedHTMLText = null;
Platform.runLater(new Runnable() {
@Override public void run() {
setContentEditable(true);
updateToolbarState(true);
updateNodeOrientation();
}
});
}
}
});
enableToolbar(true);
setHTMLText(cachedHTMLText);
engine = new TraversalEngine(getSkinnable(), false);
engine.addTraverseListener(this);
engine.reg(toolbar1);
getSkinnable().setImpl_traversalEngine(engine);
webView.setFocusTraversable(true);
gridPane.getChildren().addListener(itemsListener);
}
public final String getHTMLText() {
// RT17203 setHTMLText is asynchronous. We use the cached version of
// the html text until the page finishes loading.
return cachedHTMLText != null ? cachedHTMLText : webPage.getHtml(webPage.getMainFrame());
}
public final void setHTMLText(String htmlText) {
cachedHTMLText = htmlText;
webPage.load(webPage.getMainFrame(), htmlText, "text/html");
Platform.runLater(new Runnable() {
@Override public void run() {
updateToolbarState(true);
}
});
}
private ResourceBundle resources;
private void populateToolbars() {
resources = ResourceBundle.getBundle(HTMLEditorSkin.class.getName());
// Toolbar 1
cutButton = addButton(toolbar1, resources.getString("cutIcon"), resources.getString("cut"), CUT_COMMAND, "html-editor-cut");
copyButton = addButton(toolbar1, resources.getString("copyIcon"), resources.getString("copy"), COPY_COMMAND, "html-editor-copy");
pasteButton = addButton(toolbar1, resources.getString("pasteIcon"), resources.getString("paste"), PASTE_COMMAND, "html-editor-paste");
toolbar1.getItems().add(new Separator());
// undoButton = addButton(toolbar1, "undoIcon", resources.getString("undo"), UNDO_COMMAND);
// redoButton = addButton(toolbar1, "redoIcon", resources.getString("redo"), REDO_COMMAND);//
// toolbar1.getItems().add(new Separator());
alignmentToggleGroup = new ToggleGroup();
alignLeftButton = addToggleButton(toolbar1, alignmentToggleGroup,
resources.getString("alignLeftIcon"), resources.getString("alignLeft"), ALIGN_LEFT_COMMAND, "html-editor-align-left");
alignCenterButton = addToggleButton(toolbar1, alignmentToggleGroup,
resources.getString("alignCenterIcon"), resources.getString("alignCenter"), ALIGN_CENTER_COMMAND, "html-editor-align-center");
alignRightButton = addToggleButton(toolbar1, alignmentToggleGroup,
resources.getString("alignRightIcon"), resources.getString("alignRight"), ALIGN_RIGHT_COMMAND, "html-editor-align-right");
alignJustifyButton = addToggleButton(toolbar1, alignmentToggleGroup,
resources.getString("alignJustifyIcon"), resources.getString("alignJustify"), ALIGN_JUSTIFY_COMMAND, "html-editor-align-justify");
toolbar1.getItems().add(new Separator());
outdentButton = addButton(toolbar1, resources.getString("outdentIcon"), resources.getString("outdent"), OUTDENT_COMMAND, "html-editor-outdent");
if (outdentButton.getGraphic() != null) outdentButton.getGraphic().setNodeOrientation(NodeOrientation.INHERIT);
indentButton = addButton(toolbar1, resources.getString("indentIcon"), resources.getString("indent"), INDENT_COMMAND, "html-editor-indent");
if (indentButton.getGraphic() != null) indentButton.getGraphic().setNodeOrientation(NodeOrientation.INHERIT);
toolbar1.getItems().add(new Separator());
ToggleGroup listStyleToggleGroup = new ToggleGroup();
bulletsButton = addToggleButton(toolbar1, listStyleToggleGroup,
resources.getString("bulletsIcon"), resources.getString("bullets"), BULLETS_COMMAND, "html-editor-bullets");
if (bulletsButton.getGraphic() != null) bulletsButton.getGraphic().setNodeOrientation(NodeOrientation.INHERIT);
numbersButton = addToggleButton(toolbar1, listStyleToggleGroup,
resources.getString("numbersIcon"), resources.getString("numbers"), NUMBERS_COMMAND, "html-editor-numbers");
toolbar1.getItems().add(new Separator());
//toolbar1.getItems().add(new Separator());
// Toolbar 2
formatComboBox = new ComboBox();
formatComboBox.getStyleClass().add("font-menu-button");
formatComboBox.setFocusTraversable(false);
formatComboBox.setMinWidth(Region.USE_PREF_SIZE);
toolbar2.getItems().add(formatComboBox);
formatStyleMap = new HashMap();
styleFormatMap = new HashMap();
createFormatMenuItem(FORMAT_PARAGRAPH, resources.getString("paragraph"));
Platform.runLater(new Runnable() {
@Override
public void run() {
formatComboBox.setValue(resources.getString("paragraph"));
}
});
createFormatMenuItem(FORMAT_HEADING_1, resources.getString("heading1"));
createFormatMenuItem(FORMAT_HEADING_2, resources.getString("heading2"));
createFormatMenuItem(FORMAT_HEADING_3, resources.getString("heading3"));
createFormatMenuItem(FORMAT_HEADING_4, resources.getString("heading4"));
createFormatMenuItem(FORMAT_HEADING_5, resources.getString("heading5"));
createFormatMenuItem(FORMAT_HEADING_6, resources.getString("heading6"));
// formatComboBox.setCellFactory(new Callback, ListCell>() {
// @Override public ListCell call(ListView param) {
// final ListCell cell = new ListCell() {
// @Override public void updateItem(String item, boolean empty) {
// super.updateItem(item, empty);
// if (item != null) {
// setText(item);
// }
// }
// };
// return cell;
// }
// });
formatComboBox.setTooltip(new Tooltip(resources.getString("format")));
formatComboBox.valueProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, String oldValue, String newValue) {
if (newValue == null) {
formatComboBox.setValue(null);
} else {
String formatValue = formatStyleMap.get(newValue);
executeCommand(FORMAT_COMMAND, formatValue);
updateToolbarState(false);
// RT-16330 match the new font format with the required weight and size
for (int i = 0; i < DEFAULT_FORMAT_MAPPINGS.length; i++) {
String[] mapping = DEFAULT_FORMAT_MAPPINGS[i];
if (mapping[0].equalsIgnoreCase(formatValue)) {
executeCommand(FONT_SIZE_COMMAND, mapping[2]);
updateToolbarState(false);
break;
}
}
}
}
});
fontFamilyComboBox = new ComboBox();
fontFamilyComboBox.getStyleClass().add("font-menu-button");
fontFamilyComboBox.setMinWidth(FONT_FAMILY_MENUBUTTON_WIDTH);
fontFamilyComboBox.setPrefWidth(FONT_FAMILY_MENUBUTTON_WIDTH);
fontFamilyComboBox.setMaxWidth(FONT_FAMILY_MENUBUTTON_WIDTH);
fontFamilyComboBox.setFocusTraversable(false);
fontFamilyComboBox.setTooltip(new Tooltip(resources.getString("fontFamily")));
toolbar2.getItems().add(fontFamilyComboBox);
fontFamilyComboBox.setCellFactory(new Callback, ListCell>() {
@Override public ListCell call(ListView param) {
final ListCell cell = new ListCell() {
@Override public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
setFont(new Font(item, 12));
}
}
};
cell.setMinWidth(FONT_FAMILY_MENU_WIDTH);
cell.setPrefWidth(FONT_FAMILY_MENU_WIDTH);
cell.setMaxWidth(FONT_FAMILY_MENU_WIDTH);
return cell;
}
});
Platform.runLater(new Runnable() {
@Override public void run() {
final ObservableList fonts = FXCollections.observableArrayList(Font.getFamilies());
for (String fontFamily : fonts) {
if (DEFAULT_OS_FONT.equals(fontFamily)) {
fontFamilyComboBox.setValue(fontFamily);
}
fontFamilyComboBox.setItems(fonts);
}
}
});
fontFamilyComboBox.valueProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, String oldValue, String newValue) {
executeCommand(FONT_FAMILY_COMMAND, newValue);
}
});
fontSizeComboBox = new ComboBox();
fontSizeComboBox.getStyleClass().add("font-menu-button");
fontSizeComboBox.setFocusTraversable(false);
toolbar2.getItems().add(fontSizeComboBox);
fontSizeMap = new HashMap();
sizeFontMap = new HashMap();
createFontSizeMenuItem(SIZE_XX_SMALL, resources.getString("extraExtraSmall"));
createFontSizeMenuItem(SIZE_X_SMALL, resources.getString("extraSmall"));
createFontSizeMenuItem(SIZE_SMALL, resources.getString("small"));
Platform.runLater(new Runnable() {
@Override
public void run() {
fontSizeComboBox.setValue(resources.getString("small"));
}
});
createFontSizeMenuItem(SIZE_MEDIUM, resources.getString("medium"));
createFontSizeMenuItem(SIZE_LARGE, resources.getString("large"));
createFontSizeMenuItem(SIZE_X_LARGE, resources.getString("extraLarge"));
createFontSizeMenuItem(SIZE_XX_LARGE, resources.getString("extraExtraLarge"));
fontSizeComboBox.setTooltip(new Tooltip(resources.getString("fontSize")));
fontSizeComboBox.setCellFactory(new Callback, ListCell>() {
@Override public ListCell call(ListView param) {
final ListCell cell = new ListCell() {
@Override public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
setFont(new Font((String)fontFamilyComboBox.getValue(), Double.valueOf(item.substring(0, item.indexOf(" ")))));
}
}
};
return cell;
}
});
fontSizeComboBox.valueProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, String oldValue, String newValue) {
Object fontSizeValue = getCommandValue(FONT_SIZE_COMMAND);
if (!newValue.equals(fontSizeValue)) {
executeCommand(FONT_SIZE_COMMAND, fontSizeMap.get(newValue));
}
}
});
toolbar2.getItems().add(new Separator());
boldButton = addToggleButton(toolbar2, null,
resources.getString("boldIcon"), resources.getString("bold"), BOLD_COMMAND, "html-editor-bold");
boldButton.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent event) {
// Only use the bold button for paragraphs. We don't
// want to turn bold off for headings.
if ("".equals(formatStyleMap.get(formatComboBox.getValue()))) {
executeCommand(BOLD_COMMAND, boldButton.selectedProperty().getValue().toString());
}
}
});
italicButton = addToggleButton(toolbar2, null,
resources.getString("italicIcon"), resources.getString("italic"), ITALIC_COMMAND, "html-editor-italic");
underlineButton = addToggleButton(toolbar2, null,
resources.getString("underlineIcon"), resources.getString("underline"), UNDERLINE_COMMAND, "html-editor-underline");
strikethroughButton = addToggleButton(toolbar2, null,
resources.getString("strikethroughIcon"), resources.getString("strikethrough"), STRIKETHROUGH_COMMAND, "html-editor-strike");
toolbar2.getItems().add(new Separator());
insertHorizontalRuleButton = addButton(toolbar2, resources.getString("insertHorizontalRuleIcon"),
resources.getString("insertHorizontalRule"), INSERT_HORIZONTAL_RULE_COMMAND, "html-editor-hr");
// We override setOnAction to insert a new line. This fixes RT-16453
insertHorizontalRuleButton.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent event) {
executeCommand(INSERT_NEW_LINE_COMMAND, null);
executeCommand(INSERT_HORIZONTAL_RULE_COMMAND, null);
updateToolbarState(false);
}
});
fgColorButton = new ColorPicker();
fgColorButton.getStyleClass().add("html-editor-foreground");
fgColorButton.setFocusTraversable(false);
toolbar1.getItems().add(fgColorButton);
fgColorButton.impl_processCSS(true);
ColorPickerSkin fgColorPickerSkin = (ColorPickerSkin) fgColorButton.getSkin();
String fgIcon = AccessController.doPrivileged(new PrivilegedAction() {
@Override public String run() {
return HTMLEditorSkin.class.getResource(resources.getString("foregroundColorIcon")).toString();
}
});
((StyleableProperty)fgColorPickerSkin.imageUrlProperty()).applyStyle(null,fgIcon);
fgColorButton.setValue(DEFAULT_FG_COLOR);
fgColorButton.setTooltip(new Tooltip(resources.getString("foregroundColor")));
fgColorButton.setOnAction(new EventHandler() {
@Override public void handle(ActionEvent ev) {
Color newValue = fgColorButton.getValue();
if (newValue != null) {
executeCommand(FOREGROUND_COLOR_COMMAND, colorValueToHex(newValue));
fgColorButton.hide();
}
}
});
bgColorButton = new ColorPicker();
bgColorButton.getStyleClass().add("html-editor-background");
bgColorButton.setFocusTraversable(false);
toolbar1.getItems().add(bgColorButton);
bgColorButton.impl_processCSS(true);
ColorPickerSkin bgColorPickerSkin = (ColorPickerSkin) bgColorButton.getSkin();
String bgIcon = AccessController.doPrivileged(new PrivilegedAction() {
@Override public String run() {
return HTMLEditorSkin.class.getResource(resources.getString("backgroundColorIcon")).toString();
}
});
((StyleableProperty)bgColorPickerSkin.imageUrlProperty()).applyStyle(null,bgIcon);
bgColorButton.setValue(DEFAULT_BG_COLOR);
bgColorButton.setTooltip(new Tooltip(resources.getString("backgroundColor")));
bgColorButton.setOnAction(new EventHandler() {
@Override public void handle(ActionEvent ev) {
Color newValue = bgColorButton.getValue();
if (newValue != null) {
executeCommand(BACKGROUND_COLOR_COMMAND, colorValueToHex(newValue));
bgColorButton.hide();
}
}
});
}
private String colorValueToHex(Color c) {
return String.format((Locale)null, "#%02x%02x%02x",
Math.round(c.getRed() * 255),
Math.round(c.getGreen() * 255),
Math.round(c.getBlue() * 255));
}
private Button addButton(ToolBar toolbar, final String iconName, String tooltipText,
final String command, final String styleClass) {
Button button = new Button();
button.setFocusTraversable(false);
button.getStyleClass().add(styleClass);
toolbar.getItems().add(button);
Image icon = AccessController.doPrivileged(new PrivilegedAction() {
@Override public Image run() {
return new Image(HTMLEditorSkin.class.getResource(iconName).toString());
}
});
// button.setGraphic(new ImageView(icon));
((StyleableProperty)button.graphicProperty()).applyStyle(null,new ImageView(icon));
button.setTooltip(new Tooltip(tooltipText));
button.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent event) {
executeCommand(command, null);
updateToolbarState(false);
}
});
return button;
}
private ToggleButton addToggleButton(ToolBar toolbar, ToggleGroup toggleGroup,
final String iconName, String tooltipText, final String command, final String styleClass) {
ToggleButton toggleButton = new ToggleButton();
toggleButton.setUserData(command);
toggleButton.setFocusTraversable(false);
toggleButton.getStyleClass().add(styleClass);
toolbar.getItems().add(toggleButton);
if (toggleGroup != null) {
toggleButton.setToggleGroup(toggleGroup);
}
Image icon = AccessController.doPrivileged(new PrivilegedAction() {
@Override public Image run() {
return new Image(HTMLEditorSkin.class.getResource(iconName).toString());
}
});
((StyleableProperty)toggleButton.graphicProperty()).applyStyle(null,new ImageView(icon));
// toggleButton.setGraphic(new ImageView(icon));
toggleButton.setTooltip(new Tooltip(tooltipText));
if (!BOLD_COMMAND.equals(command)) {
toggleButton.selectedProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) {
if (getCommandState(command) != newValue.booleanValue()) {
executeCommand(command, null);
}
}
});
}
return toggleButton;
}
private void createFormatMenuItem(String formatValue, String label) {
formatComboBox.getItems().add(label);
formatStyleMap.put(label, formatValue);
styleFormatMap.put(formatValue, label);
}
private void createFontSizeMenuItem(String fontSizeValue, String label) {
fontSizeComboBox.getItems().add(label);
fontSizeMap.put(label, fontSizeValue);
sizeFontMap.put(fontSizeValue, label);
}
private void updateNodeOrientation() {
NodeOrientation orientation = getSkinnable().getEffectiveNodeOrientation();
HTMLDocument htmlDocument = (HTMLDocument)webPage.getDocument(webPage.getMainFrame());
HTMLElement htmlDocumentElement = (HTMLElement)htmlDocument.getDocumentElement();
if (htmlDocumentElement.getAttribute("dir") == null) {
htmlDocumentElement.setAttribute("dir", (orientation == RIGHT_TO_LEFT) ? "rtl" : "ltr");
}
if (orientation == RIGHT_TO_LEFT) {
try {
final String iconName = resources.getString("numbersIcon-rtl");
Image icon = AccessController.doPrivileged(new PrivilegedAction() {
@Override public Image run() {
return new Image(HTMLEditorSkin.class.getResource(iconName).toString());
}
});
numbersButton.setGraphic(new ImageView(icon));
} catch (java.util.MissingResourceException ex) {
// ignore
}
}
}
private void updateToolbarState(final boolean updateAlignment) {
if (!webView.isFocused()) {
return;
}
// These command aways return true.
copyButton.setDisable(!isCommandEnabled(CUT_COMMAND));
cutButton.setDisable(!isCommandEnabled(COPY_COMMAND));
pasteButton.setDisable(!isCommandEnabled(PASTE_COMMAND));
// undoButton.setDisable(!isCommandEnabled(UNDO_COMMAND));
// redoButton.setDisable(!isCommandEnabled(REDO_COMMAND));
// undoButton.setDisable(!isCommandEnabled(FORMAT_COMMAND));
// redoButton.setDisable(!isCommandEnabled(FORMAT_COMMAND));
insertHorizontalRuleButton.setDisable(!isCommandEnabled(INSERT_HORIZONTAL_RULE_COMMAND));
if (updateAlignment) {
alignLeftButton.setDisable(!isCommandEnabled(ALIGN_LEFT_COMMAND));
alignLeftButton.setSelected(getCommandState(ALIGN_LEFT_COMMAND));
alignCenterButton.setDisable(!isCommandEnabled(ALIGN_CENTER_COMMAND));
alignCenterButton.setSelected(getCommandState(ALIGN_CENTER_COMMAND));
alignRightButton.setDisable(!isCommandEnabled(ALIGN_RIGHT_COMMAND));
alignRightButton.setSelected(getCommandState(ALIGN_RIGHT_COMMAND));
alignJustifyButton.setDisable(!isCommandEnabled(ALIGN_JUSTIFY_COMMAND));
alignJustifyButton.setSelected(getCommandState(ALIGN_JUSTIFY_COMMAND));
} else {
if (alignmentToggleGroup.getSelectedToggle() != null) {
String command = alignmentToggleGroup.getSelectedToggle().getUserData().toString();
if (isCommandEnabled(command) && !getCommandState(command) ) {
executeCommand(command, null);
}
}
}
if (alignmentToggleGroup.getSelectedToggle() == null) {
alignmentToggleGroup.selectToggle(alignLeftButton);
}
bulletsButton.setDisable(!isCommandEnabled(BULLETS_COMMAND));
bulletsButton.setSelected(getCommandState(BULLETS_COMMAND));
numbersButton.setDisable(!isCommandEnabled(NUMBERS_COMMAND));
numbersButton.setSelected(getCommandState(NUMBERS_COMMAND));
indentButton.setDisable(!isCommandEnabled(INDENT_COMMAND));
outdentButton.setDisable(!isCommandEnabled(OUTDENT_COMMAND));
formatComboBox.setDisable(!isCommandEnabled(FORMAT_COMMAND));
String formatValue = getCommandValue(FORMAT_COMMAND);
if (formatValue != null) {
String htmlTag = "<" + formatValue + ">";
String comboFormatValue = styleFormatMap.get(htmlTag);
String format = formatComboBox.getValue();
// if the format value is then we assume that we're dealing with a paragraph,
// which seems to correspond with the HTML output we receive.
if ((resetToolbarState || htmlTag.equals("<>") || htmlTag.equalsIgnoreCase(""))) {
formatComboBox.setValue(resources.getString("paragraph"));
} else if (format != null && ! format.equalsIgnoreCase(comboFormatValue)) {
formatComboBox.setValue(comboFormatValue);
}
}
fontFamilyComboBox.setDisable(!isCommandEnabled(FONT_FAMILY_COMMAND));
final String fontFamilyValue = getCommandValue(FONT_FAMILY_COMMAND);
if (fontFamilyValue != null) {
String fontFamilyStr = fontFamilyValue;
// stripping out apostrophe characters, which are appended to either
// end of the font face name when the font face has one or more spaces.
if (fontFamilyStr.startsWith("'")) {
fontFamilyStr = fontFamilyStr.substring(1);
}
if (fontFamilyStr.endsWith("'")) {
fontFamilyStr = fontFamilyStr.substring(0,fontFamilyStr.length() - 1);
}
Object selectedFont = fontFamilyComboBox.getValue();
if (selectedFont instanceof String) {
if (!selectedFont.equals(fontFamilyStr)) {
ObservableList fontFamilyItems = fontFamilyComboBox.getItems();
String selectedComboFont = null;
for (String comboFontFamilyValue : fontFamilyItems) {
if (comboFontFamilyValue.equals(fontFamilyStr)) {
selectedComboFont = comboFontFamilyValue;
break;
}
// Note: By default, 'Dialog' is the font returned from webview.
// For presidio, we're just mapping to an OS-specific font.
if (comboFontFamilyValue.equals(DEFAULT_OS_FONT) && fontFamilyStr.equals("Dialog")) {
selectedComboFont = comboFontFamilyValue;
break;
}
}
if (selectedComboFont != null) {
fontFamilyComboBox.setValue(selectedComboFont);
}
}
}
}
fontSizeComboBox.setDisable(!isCommandEnabled(FONT_SIZE_COMMAND));
String fontSizeValue = getCommandValue(FONT_SIZE_COMMAND);
if (resetToolbarState) {
fontSizeComboBox.setValue(sizeFontMap.get(SIZE_SMALL));
}
else {
if (fontSizeValue != null) {
if (!fontSizeComboBox.getValue().equals(sizeFontMap.get(fontSizeValue))) {
fontSizeComboBox.setValue(sizeFontMap.get(fontSizeValue));
}
}
else {
/*
** these is no font size set in webview,
** let's just use the default....
*/
if (!fontSizeComboBox.getValue().equals(sizeFontMap.get(SIZE_SMALL))) {
fontSizeComboBox.setValue(sizeFontMap.get(SIZE_SMALL));
}
}
}
boldButton.setDisable(!isCommandEnabled(BOLD_COMMAND));
if (formatValue != null) {
if (!resetToolbarState && ("p".equals(formatValue) || "div".equals(formatValue))) {
boldButton.setSelected(getCommandState(BOLD_COMMAND));
}
}
italicButton.setDisable(!isCommandEnabled(ITALIC_COMMAND));
italicButton.setSelected(getCommandState(ITALIC_COMMAND));
underlineButton.setDisable(!isCommandEnabled(UNDERLINE_COMMAND));
underlineButton.setSelected(getCommandState(UNDERLINE_COMMAND));
strikethroughButton.setDisable(!isCommandEnabled(STRIKETHROUGH_COMMAND));
strikethroughButton.setSelected(getCommandState(STRIKETHROUGH_COMMAND));
fgColorButton.setDisable(!isCommandEnabled(FOREGROUND_COLOR_COMMAND));
String foregroundColorValue = getCommandValue(FOREGROUND_COLOR_COMMAND);
if (foregroundColorValue != null) {
Color c = Color.web(rgbToHex((String)foregroundColorValue));
fgColorButton.setValue(c);
}
bgColorButton.setDisable(!isCommandEnabled(BACKGROUND_COLOR_COMMAND));
String backgroundColorValue = getCommandValue(BACKGROUND_COLOR_COMMAND);
if (backgroundColorValue != null) {
Color c = Color.web(rgbToHex((String)backgroundColorValue));
bgColorButton.setValue(c);
}
}
private void enableToolbar(final boolean enable) {
Platform.runLater(new Runnable() {
@Override public void run() {
// Make sure buttons have been created to avoid NPE
if (copyButton == null) return;
/*
** if we're to enable, we still only enable
** the cut/copy/paste buttons that make sense
*/
if (enable) {
copyButton.setDisable(!isCommandEnabled(COPY_COMMAND));
cutButton.setDisable(!isCommandEnabled(CUT_COMMAND));
pasteButton.setDisable(!isCommandEnabled(PASTE_COMMAND));
}
else {
copyButton.setDisable(true);
cutButton.setDisable(true);
pasteButton.setDisable(true);
}
// undoButton.setDisable(!enable);
// redoButton.setDisable(!enable);
insertHorizontalRuleButton.setDisable(!enable);
alignLeftButton.setDisable(!enable);
alignCenterButton.setDisable(!enable);
alignRightButton.setDisable(!enable);
alignJustifyButton.setDisable(!enable);
bulletsButton.setDisable(!enable);
numbersButton.setDisable(!enable);
indentButton.setDisable(!enable);
outdentButton.setDisable(!enable);
formatComboBox.setDisable(!enable);
fontFamilyComboBox.setDisable(!enable);
fontSizeComboBox.setDisable(!enable);
boldButton.setDisable(!enable);
italicButton.setDisable(!enable);
underlineButton.setDisable(!enable);
strikethroughButton.setDisable(!enable);
fgColorButton.setDisable(!enable);
bgColorButton.setDisable(!enable);
}
});
}
private boolean executeCommand(String command, String value) {
return webPage.executeCommand(command, value);
}
private boolean isCommandEnabled(String command) {
return webPage.queryCommandEnabled(command);
}
private void setContentEditable(boolean b) {
HTMLDocument htmlDocument = (HTMLDocument)webPage.getDocument(webPage.getMainFrame());
HTMLElement htmlDocumentElement = (HTMLElement)htmlDocument.getDocumentElement();
HTMLElement htmlBodyElement = (HTMLElement)htmlDocumentElement.getElementsByTagName("body").item(0);
htmlBodyElement.setAttribute("contenteditable", Boolean.toString(b));
}
private boolean getCommandState(String command) {
return webPage.queryCommandState(command);
}
private String getCommandValue(String command) {
return webPage.queryCommandValue(command);
}
private static String rgbToHex(String value) {
if (value.startsWith("rgba")) {
String[] components = value.substring(value.indexOf('(') + 1, value.lastIndexOf(')')).split(",");
value = String.format("#%02X%02X%02X%02X",
Integer.parseInt(components[0].trim()),
Integer.parseInt(components[1].trim()),
Integer.parseInt(components[2].trim()),
Integer.parseInt(components[3].trim()));
// The default background color for WebView, according to the HTML
// standard is rgba=#00000000 (black). The canvas background is expected
// to be white.
if ("#00000000".equals(value)) {
return "#FFFFFFFF";
}
} else if (value.startsWith("rgb")) {
String[] components = value.substring(value.indexOf('(') + 1, value.lastIndexOf(')')).split(",");
value = String.format("#%02X%02X%02X",
Integer.parseInt(components[0].trim()),
Integer.parseInt(components[1].trim()),
Integer.parseInt(components[2].trim()));
}
return value;
}
private void applyTextFormatting() {
if (getCommandState(BULLETS_COMMAND) || getCommandState(NUMBERS_COMMAND)) {
return;
}
if (webPage.getClientCommittedTextLength() == 0) {
String format = formatStyleMap.get(formatComboBox.getValue());
String font = fontFamilyComboBox.getValue().toString();
executeCommand(FORMAT_COMMAND, format);
executeCommand(FONT_FAMILY_COMMAND, font);
}
}
public void keyboardShortcuts(final String name) {
if ("bold".equals(name)) {
boldButton.fire();
} else if ("italic".equals(name)) {
italicButton.setSelected(!italicButton.isSelected());
} else if ("underline".equals(name)) {
underlineButton.setSelected(!underlineButton.isSelected());
}
}
@Override
public void onTraverse(Node node, Bounds bounds) {
cutButton.requestFocus();
}
private boolean isFirstRun = true;
@Override
protected void layoutChildren(final double x, final double y,
final double w, final double h) {
if (isFirstRun) {
populateToolbars();
isFirstRun = false;
}
super.layoutChildren(x,y,w,h);
double toolbarWidth = Math.max(toolbar1.prefWidth(-1), toolbar2.prefWidth(-1));
toolbar1.setMinWidth(toolbarWidth);
toolbar1.setPrefWidth(toolbarWidth);
toolbar2.setMinWidth(toolbarWidth);
toolbar2.setPrefWidth(toolbarWidth);
}
private static final int FONT_FAMILY_MENUBUTTON_WIDTH = 150;
private static final int FONT_FAMILY_MENU_WIDTH = 100;
private static final int FONT_SIZE_MENUBUTTON_WIDTH = 80;
public void print(PrinterJob job) {
webView.getEngine().print(job);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy