1 /* 2 * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.web; 27 28 import java.util.ResourceBundle; 29 30 import com.sun.javafx.application.PlatformImpl; 31 import com.sun.javafx.scene.ParentHelper; 32 import com.sun.javafx.scene.traversal.Algorithm; 33 import com.sun.javafx.scene.traversal.Direction; 34 import com.sun.javafx.scene.traversal.ParentTraversalEngine; 35 import com.sun.javafx.scene.traversal.TraversalContext; 36 import javafx.css.PseudoClass; 37 import javafx.geometry.Orientation; 38 import org.w3c.dom.html.HTMLDocument; 39 import org.w3c.dom.html.HTMLElement; 40 41 import javafx.application.ConditionalFeature; 42 import javafx.application.Platform; 43 import javafx.collections.FXCollections; 44 import javafx.collections.ObservableList; 45 import javafx.css.StyleableProperty; 46 import javafx.geometry.NodeOrientation; 47 import javafx.scene.Node; 48 import javafx.scene.control.Button; 49 import javafx.scene.control.ComboBox; 50 import javafx.scene.control.ListCell; 51 import javafx.scene.control.ListView; 52 import javafx.scene.control.Separator; 53 import javafx.scene.control.TextInputControl; 54 import javafx.scene.control.ToggleButton; 55 import javafx.scene.control.ToggleGroup; 56 import javafx.scene.control.ToolBar; 57 import javafx.scene.control.Tooltip; 58 import javafx.scene.image.Image; 59 import javafx.scene.image.ImageView; 60 import javafx.scene.input.KeyCode; 61 import javafx.scene.input.KeyEvent; 62 import javafx.scene.input.MouseEvent; 63 import javafx.scene.layout.ColumnConstraints; 64 import javafx.scene.layout.GridPane; 65 import javafx.scene.layout.Priority; 66 import javafx.scene.paint.Color; 67 import javafx.scene.text.Font; 68 import javafx.util.Callback; 69 70 import com.sun.javafx.scene.control.skin.FXVK; 71 import com.sun.javafx.scene.web.behavior.HTMLEditorBehavior; 72 import com.sun.webkit.dom.HTMLDocumentImpl; 73 import com.sun.webkit.WebPage; 74 import com.sun.javafx.webkit.Accessor; 75 76 import java.security.AccessController; 77 import java.security.PrivilegedAction; 78 79 import java.util.HashMap; 80 import java.util.Locale; 81 import java.util.Map; 82 import javafx.scene.Scene; 83 import javafx.scene.control.*; 84 import javafx.scene.layout.*; 85 import javafx.collections.ListChangeListener; 86 87 import static javafx.geometry.NodeOrientation.*; 88 import javafx.print.PrinterJob; 89 90 import static javafx.scene.web.HTMLEditorSkin.Command.*; 91 92 /** 93 * HTML editor skin. 94 * 95 * @see HTMLEditor 96 * @since 9 97 */ 98 public class HTMLEditorSkin extends SkinBase<HTMLEditor> { 99 100 /*************************************************************************** 101 * * 102 * Private fields * 103 * * 104 **************************************************************************/ 105 106 private GridPane gridPane; 107 108 private ToolBar toolbar1; 109 private ToolBar toolbar2; 110 111 private Button cutButton; 112 private Button copyButton; 113 private Button pasteButton; 114 115 // private Button undoButton; 116 // private Button redoButton; 117 118 private Button insertHorizontalRuleButton; 119 120 private ToggleGroup alignmentToggleGroup; 121 private ToggleButton alignLeftButton; 122 private ToggleButton alignCenterButton; 123 private ToggleButton alignRightButton; 124 private ToggleButton alignJustifyButton; 125 126 private ToggleButton bulletsButton; 127 private ToggleButton numbersButton; 128 129 private Button indentButton; 130 private Button outdentButton; 131 132 private ComboBox<String> formatComboBox; 133 private Map<String, String> formatStyleMap; 134 private Map<String, String> styleFormatMap; 135 136 private ComboBox<String> fontFamilyComboBox; 137 138 private ComboBox<String> fontSizeComboBox; 139 private Map<String, String> fontSizeMap; 140 private Map<String, String> sizeFontMap; 141 142 private ToggleButton boldButton; 143 private ToggleButton italicButton; 144 private ToggleButton underlineButton; 145 private ToggleButton strikethroughButton; 146 147 private ColorPicker fgColorButton; 148 private ColorPicker bgColorButton; 149 150 private WebView webView; 151 private WebPage webPage; 152 153 private ParentTraversalEngine engine; 154 155 private boolean resetToolbarState = false; 156 private String cachedHTMLText = "<html><head></head><body contenteditable=\"true\"></body></html>"; 157 private ResourceBundle resources; 158 159 private boolean enableAtomicityCheck = false; 160 private int atomicityCount = 0; 161 private boolean isFirstRun = true; 162 163 private static final int FONT_FAMILY_MENUBUTTON_WIDTH = 150; 164 private static final int FONT_FAMILY_MENU_WIDTH = 100; 165 private static final int FONT_SIZE_MENUBUTTON_WIDTH = 80; 166 167 168 169 /*************************************************************************** 170 * * 171 * Static fields * 172 * * 173 **************************************************************************/ 174 175 private static final Color DEFAULT_BG_COLOR = Color.WHITE; 176 private static final Color DEFAULT_FG_COLOR = Color.BLACK; 177 178 private static final String FORMAT_PARAGRAPH = "<p>"; 179 private static final String FORMAT_HEADING_1 = "<h1>"; 180 private static final String FORMAT_HEADING_2 = "<h2>"; 181 private static final String FORMAT_HEADING_3 = "<h3>"; 182 private static final String FORMAT_HEADING_4 = "<h4>"; 183 private static final String FORMAT_HEADING_5 = "<h5>"; 184 private static final String FORMAT_HEADING_6 = "<h6>"; 185 186 private static final String SIZE_XX_SMALL = "1"; 187 private static final String SIZE_X_SMALL = "2"; 188 private static final String SIZE_SMALL = "3"; 189 private static final String SIZE_MEDIUM = "4"; 190 private static final String SIZE_LARGE = "5"; 191 private static final String SIZE_X_LARGE = "6"; 192 private static final String SIZE_XX_LARGE = "7"; 193 194 // As per RT-16330: default format -> bold/size mappings are as follows: 195 private static final String[][] DEFAULT_FORMAT_MAPPINGS = { 196 { FORMAT_PARAGRAPH, "", SIZE_SMALL }, 197 { FORMAT_HEADING_1, BOLD.getCommand(), SIZE_X_LARGE }, 198 { FORMAT_HEADING_2, BOLD.getCommand(), SIZE_LARGE }, 199 { FORMAT_HEADING_3, BOLD.getCommand(), SIZE_MEDIUM }, 200 { FORMAT_HEADING_4, BOLD.getCommand(), SIZE_SMALL }, 201 { FORMAT_HEADING_5, BOLD.getCommand(), SIZE_X_SMALL }, 202 { FORMAT_HEADING_6, BOLD.getCommand(), SIZE_XX_SMALL }, 203 }; 204 205 private static PseudoClass CONTAINS_FOCUS_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("contains-focus"); 206 207 208 209 /*************************************************************************** 210 * * 211 * Static Methods * 212 * * 213 **************************************************************************/ 214 215 216 217 /*************************************************************************** 218 * * 219 * Listeners * 220 * * 221 **************************************************************************/ 222 223 private ListChangeListener<Node> itemsListener = c -> { 224 while (c.next()) { 225 if (c.getRemovedSize() > 0) { 226 for (Node n : c.getList()) { 227 if (n instanceof WebView) { 228 // RT-28611 webView removed - set associated webPage to null 229 webPage.dispose(); 230 } 231 } 232 } 233 } 234 }; 235 236 237 238 /*************************************************************************** 239 * * 240 * Constructors * 241 * * 242 **************************************************************************/ 243 244 /** 245 * Creates a new HTMLEditorSkin instance, installing the necessary child 246 * nodes into the Control {@link Control#getChildren() children} list, as 247 * well as the necessary input mappings for handling key, mouse, etc events. 248 * 249 * @param control The control that this skin should be installed onto. 250 */ 251 public HTMLEditorSkin(HTMLEditor control) { 252 super(control); 253 254 // install default input map for the HTMLEditor control 255 HTMLEditorBehavior behavior = new HTMLEditorBehavior(control); 256 // htmlEditor.setInputMap(behavior.getInputMap()); 257 258 getChildren().clear(); 259 260 gridPane = new GridPane(); 261 gridPane.getStyleClass().add("grid"); 262 getChildren().addAll(gridPane); 263 264 toolbar1 = new ToolBar(); 265 toolbar1.getStyleClass().add("top-toolbar"); 266 gridPane.add(toolbar1, 0, 0); 267 268 toolbar2 = new ToolBar(); 269 toolbar2.getStyleClass().add("bottom-toolbar"); 270 gridPane.add(toolbar2, 0, 1); 271 272 // populateToolbars(); 273 274 webView = new WebView(); 275 gridPane.add(webView, 0, 2); 276 277 ColumnConstraints column = new ColumnConstraints(); 278 column.setHgrow(Priority.ALWAYS); 279 gridPane.getColumnConstraints().add(column); 280 281 webPage = Accessor.getPageFor(webView.getEngine()); 282 283 webView.addEventHandler(MouseEvent.MOUSE_RELEASED, event2 -> { 284 Platform.runLater(new Runnable() { 285 @Override public void run() { 286 enableAtomicityCheck = true; 287 updateToolbarState(true); 288 enableAtomicityCheck = false; 289 } 290 }); 291 }); 292 293 294 webView.addEventHandler(KeyEvent.KEY_PRESSED, event -> { 295 applyTextFormatting(); 296 if (event.getCode() == KeyCode.CONTROL || event.getCode() == KeyCode.META) { 297 return; 298 } 299 if (event.getCode() == KeyCode.TAB && !event.isControlDown()) { 300 if (!event.isShiftDown()) { 301 /* 302 ** if we are in either Bullet or Numbers mode then the 303 ** TAB key tells us to indent again. 304 */ 305 if (getCommandState(BULLETS.getCommand()) || getCommandState(NUMBERS.getCommand())) { 306 executeCommand(INDENT.getCommand(), null); 307 } 308 else { 309 executeCommand(INSERT_TAB.getCommand(), null); 310 } 311 } 312 else { 313 /* 314 ** if we are in either Bullet or Numbers mode then the 315 ** Shift-TAB key tells us to outdent. 316 */ 317 if (getCommandState(BULLETS.getCommand()) || getCommandState(NUMBERS.getCommand())) { 318 executeCommand(OUTDENT.getCommand(), null); 319 } 320 } 321 return; 322 } 323 // Work around for bug that sends events from ColorPicker to this Scene 324 if ((fgColorButton != null && fgColorButton.isShowing()) || 325 (bgColorButton != null && bgColorButton.isShowing())) { 326 return; 327 } 328 Platform.runLater(() -> { 329 if (webPage.getClientSelectedText().isEmpty()) { 330 if (event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN || 331 event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT || 332 event.getCode() == KeyCode.HOME || event.getCode() == KeyCode.END) { 333 updateToolbarState(true); 334 } else if (event.isControlDown() || event.isMetaDown()) { 335 if (event.getCode() == KeyCode.B) { 336 performCommand(BOLD); 337 } else if (event.getCode() == KeyCode.I) { 338 performCommand(ITALIC); 339 } else if (event.getCode() == KeyCode.U) { 340 performCommand(UNDERLINE); 341 } 342 updateToolbarState(true); 343 } else { 344 resetToolbarState = event.getCode() == KeyCode.ENTER; 345 if (resetToolbarState) { 346 if (getCommandState(BOLD.getCommand()) != boldButton.selectedProperty().getValue()) { 347 executeCommand(BOLD.getCommand(), boldButton.selectedProperty().getValue().toString()); 348 } 349 } 350 updateToolbarState(false); 351 } 352 resetToolbarState = false; 353 } else if (event.isShiftDown() && 354 (event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN || 355 event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT)) { 356 updateToolbarState(true); 357 } 358 }); 359 }); 360 361 webView.addEventHandler(KeyEvent.KEY_RELEASED, event -> { 362 if (event.getCode() == KeyCode.CONTROL || event.getCode() == KeyCode.META) { 363 return; 364 } 365 // Work around for bug that sends events from ColorPicker to this Scene 366 if ((fgColorButton != null && fgColorButton.isShowing()) || 367 (bgColorButton != null && bgColorButton.isShowing())) { 368 return; 369 } 370 Platform.runLater(() -> { 371 if (webPage.getClientSelectedText().isEmpty()) { 372 if (event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN || 373 event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT || 374 event.getCode() == KeyCode.HOME || event.getCode() == KeyCode.END) { 375 updateToolbarState(true); 376 } else if (event.isControlDown() || event.isMetaDown()) { 377 if (event.getCode() == KeyCode.B) { 378 performCommand(BOLD); 379 } else if (event.getCode() == KeyCode.I) { 380 performCommand(ITALIC); 381 } else if (event.getCode() == KeyCode.U) { 382 performCommand(UNDERLINE); 383 } 384 updateToolbarState(true); 385 } else { 386 resetToolbarState = event.getCode() == KeyCode.ENTER; 387 if (!resetToolbarState) { 388 updateToolbarState(false); 389 } 390 } 391 resetToolbarState = false; 392 } 393 }); 394 }); 395 396 getSkinnable().focusedProperty().addListener((observable, oldValue, newValue) -> { 397 Platform.runLater(new Runnable() { 398 @Override public void run() { 399 if (newValue) { 400 webView.requestFocus(); 401 } 402 } 403 }); 404 }); 405 406 webView.focusedProperty().addListener((observable, oldValue, newValue) -> { 407 // disabling as a fix for RT-30081 408 // if (newValue) { 409 // webPage.dispatchFocusEvent(new WCFocusEvent(WCFocusEvent.FOCUS_GAINED, WCFocusEvent.FORWARD)); 410 // enableToolbar(true); 411 // } else { 412 // webPage.dispatchFocusEvent(new WCFocusEvent(WCFocusEvent.FOCUS_LOST, WCFocusEvent.FORWARD)); 413 // enableToolbar(false); 414 // } 415 416 pseudoClassStateChanged(CONTAINS_FOCUS_PSEUDOCLASS_STATE, newValue); 417 418 Platform.runLater(new Runnable() { 419 @Override public void run() { 420 updateToolbarState(true); 421 422 if (PlatformImpl.isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) { 423 Scene scene = getSkinnable().getScene(); 424 if (newValue) { 425 FXVK.attach(webView); 426 } else if (scene == null || 427 scene.getWindow() == null || 428 !scene.getWindow().isFocused() || 429 !(scene.getFocusOwner() instanceof TextInputControl /*|| 430 getScene().getFocusOwner() instanceof WebView*/)) { 431 FXVK.detach(); 432 } 433 } 434 } 435 }); 436 }); 437 438 webView.getEngine().getLoadWorker().workDoneProperty().addListener((observable, oldValue, newValue) -> { 439 Platform.runLater(() -> { 440 webView.requestLayout(); 441 }); 442 443 double totalWork = webView.getEngine().getLoadWorker().getTotalWork(); 444 if (newValue.doubleValue() == totalWork) { 445 cachedHTMLText = null; 446 Platform.runLater(() -> { 447 setDesignMode("on"); 448 setContentEditable(true); 449 updateToolbarState(true); 450 updateNodeOrientation(); 451 }); 452 } 453 }); 454 455 enableToolbar(true); 456 setHTMLText(cachedHTMLText); 457 458 engine = new ParentTraversalEngine(getSkinnable(), new Algorithm() { 459 @Override 460 public Node select(Node owner, Direction dir, TraversalContext context) { 461 return cutButton; 462 } 463 464 @Override 465 public Node selectFirst(TraversalContext context) { 466 return cutButton; 467 } 468 469 @Override 470 public Node selectLast(TraversalContext context) { 471 return cutButton; 472 } 473 }); 474 ParentHelper.setTraversalEngine(getSkinnable(), engine); 475 webView.setFocusTraversable(true); 476 gridPane.getChildren().addListener(itemsListener); 477 } 478 479 480 481 /*************************************************************************** 482 * * 483 * Public API * 484 * * 485 **************************************************************************/ 486 487 /** 488 * Special-case handling for certain commands. Over time this may be extended 489 * to handle additional commands. The current list of supported commands is: 490 * 491 * <ul> 492 * <li>BOLD</li> 493 * <li>ITALIC</li> 494 * <li>UNDERLINE</li> 495 * </ul> 496 * @param command the command 497 */ 498 public void performCommand(final Command command) { 499 switch (command) { 500 case BOLD: boldButton.fire(); break; 501 case ITALIC: italicButton.setSelected(!italicButton.isSelected()); break; 502 case UNDERLINE: underlineButton.setSelected(!underlineButton.isSelected()); break; 503 } 504 } 505 506 /** {@inheritDoc} */ 507 @Override protected void layoutChildren(final double x, final double y, 508 final double w, final double h) { 509 510 if (isFirstRun) { 511 populateToolbars(); 512 isFirstRun = false; 513 } 514 super.layoutChildren(x,y,w,h); 515 double toolbarWidth = Math.max(toolbar1.prefWidth(-1), toolbar2.prefWidth(-1)); 516 toolbar1.setMinWidth(toolbarWidth); 517 toolbar1.setPrefWidth(toolbarWidth); 518 toolbar2.setMinWidth(toolbarWidth); 519 toolbar2.setPrefWidth(toolbarWidth); 520 } 521 522 523 524 /*************************************************************************** 525 * * 526 * Private Implementation * 527 * * 528 **************************************************************************/ 529 530 final String getHTMLText() { 531 // RT17203 setHTMLText is asynchronous. We use the cached version of 532 // the html text until the page finishes loading. 533 return cachedHTMLText != null ? cachedHTMLText : webPage.getHtml(webPage.getMainFrame()); 534 } 535 536 final void setHTMLText(String htmlText) { 537 cachedHTMLText = htmlText; 538 webPage.load(webPage.getMainFrame(), htmlText, "text/html"); 539 540 Platform.runLater(() -> { 541 updateToolbarState(true); 542 }); 543 } 544 545 private void populateToolbars() { 546 resources = ResourceBundle.getBundle(HTMLEditorSkin.class.getName()); 547 548 // Toolbar 1 549 cutButton = addButton(toolbar1, resources.getString("cutIcon"), resources.getString("cut"), CUT.getCommand(), "html-editor-cut"); 550 copyButton = addButton(toolbar1, resources.getString("copyIcon"), resources.getString("copy"), COPY.getCommand(), "html-editor-copy"); 551 pasteButton = addButton(toolbar1, resources.getString("pasteIcon"), resources.getString("paste"), PASTE.getCommand(), "html-editor-paste"); 552 553 toolbar1.getItems().add(new Separator(Orientation.VERTICAL)); 554 555 // undoButton = addButton(toolbar1, "undoIcon", resources.getString("undo"), UNDO.getCommand()); 556 // redoButton = addButton(toolbar1, "redoIcon", resources.getString("redo"), REDO.getCommand());// 557 // toolbar1.getItems().add(new Separator()); 558 559 alignmentToggleGroup = new ToggleGroup(); 560 alignLeftButton = addToggleButton(toolbar1, alignmentToggleGroup, 561 resources.getString("alignLeftIcon"), resources.getString("alignLeft"), ALIGN_LEFT.getCommand(), "html-editor-align-left"); 562 alignCenterButton = addToggleButton(toolbar1, alignmentToggleGroup, 563 resources.getString("alignCenterIcon"), resources.getString("alignCenter"), ALIGN_CENTER.getCommand(), "html-editor-align-center"); 564 alignRightButton = addToggleButton(toolbar1, alignmentToggleGroup, 565 resources.getString("alignRightIcon"), resources.getString("alignRight"), ALIGN_RIGHT.getCommand(), "html-editor-align-right"); 566 alignJustifyButton = addToggleButton(toolbar1, alignmentToggleGroup, 567 resources.getString("alignJustifyIcon"), resources.getString("alignJustify"), ALIGN_JUSTIFY.getCommand(), "html-editor-align-justify"); 568 569 toolbar1.getItems().add(new Separator(Orientation.VERTICAL)); 570 571 outdentButton = addButton(toolbar1, resources.getString("outdentIcon"), resources.getString("outdent"), OUTDENT.getCommand(), "html-editor-outdent"); 572 if (outdentButton.getGraphic() != null) outdentButton.getGraphic().setNodeOrientation(NodeOrientation.INHERIT); 573 indentButton = addButton(toolbar1, resources.getString("indentIcon"), resources.getString("indent"), INDENT.getCommand(), "html-editor-indent"); 574 if (indentButton.getGraphic() != null) indentButton.getGraphic().setNodeOrientation(NodeOrientation.INHERIT); 575 576 toolbar1.getItems().add(new Separator(Orientation.VERTICAL)); 577 578 ToggleGroup listStyleToggleGroup = new ToggleGroup(); 579 bulletsButton = addToggleButton(toolbar1, listStyleToggleGroup, 580 resources.getString("bulletsIcon"), resources.getString("bullets"), BULLETS.getCommand(), "html-editor-bullets"); 581 if (bulletsButton.getGraphic() != null) bulletsButton.getGraphic().setNodeOrientation(NodeOrientation.INHERIT); 582 numbersButton = addToggleButton(toolbar1, listStyleToggleGroup, 583 resources.getString("numbersIcon"), resources.getString("numbers"), NUMBERS.getCommand(), "html-editor-numbers"); 584 585 toolbar1.getItems().add(new Separator(Orientation.VERTICAL)); 586 587 //toolbar1.getItems().add(new Separator()); 588 589 // Toolbar 2 590 formatComboBox = new ComboBox<String>(); 591 formatComboBox.getStyleClass().add("font-menu-button"); 592 formatComboBox.setFocusTraversable(false); 593 formatComboBox.setMinWidth(Region.USE_PREF_SIZE); 594 toolbar2.getItems().add(formatComboBox); 595 596 formatStyleMap = new HashMap<String, String>(); 597 styleFormatMap = new HashMap<String, String>(); 598 599 createFormatMenuItem(FORMAT_PARAGRAPH, resources.getString("paragraph")); 600 Platform.runLater(() -> { 601 formatComboBox.setValue(resources.getString("paragraph")); 602 }); 603 createFormatMenuItem(FORMAT_HEADING_1, resources.getString("heading1")); 604 createFormatMenuItem(FORMAT_HEADING_2, resources.getString("heading2")); 605 createFormatMenuItem(FORMAT_HEADING_3, resources.getString("heading3")); 606 createFormatMenuItem(FORMAT_HEADING_4, resources.getString("heading4")); 607 createFormatMenuItem(FORMAT_HEADING_5, resources.getString("heading5")); 608 createFormatMenuItem(FORMAT_HEADING_6, resources.getString("heading6")); 609 610 // formatComboBox.setCellFactory(new Callback<ListView<String>, ListCell<String>>() { 611 // @Override public ListCell<String> call(ListView<String> param) { 612 // final ListCell<String> cell = new ListCell<String>() { 613 // @Override public void updateItem(String item, boolean empty) { 614 // super.updateItem(item, empty); 615 // if (item != null) { 616 // setText(item); 617 // } 618 // } 619 // }; 620 // return cell; 621 // } 622 // }); 623 624 formatComboBox.setTooltip(new Tooltip(resources.getString("format"))); 625 626 formatComboBox.valueProperty().addListener((observable, oldValue, newValue) -> { 627 if (newValue == null) { 628 formatComboBox.setValue(null); 629 } else { 630 String formatValue = formatStyleMap.get(newValue); 631 executeCommand(FORMAT.getCommand(), formatValue); 632 updateToolbarState(false); 633 634 // RT-16330 match the new font format with the required weight and size 635 for (int i = 0; i < DEFAULT_FORMAT_MAPPINGS.length; i++) { 636 String[] mapping = DEFAULT_FORMAT_MAPPINGS[i]; 637 if (mapping[0].equalsIgnoreCase(formatValue)) { 638 executeCommand(FONT_SIZE.getCommand(), mapping[2]); 639 updateToolbarState(false); 640 break; 641 } 642 } 643 } 644 }); 645 646 fontFamilyComboBox = new ComboBox<String>(); 647 fontFamilyComboBox.getStyleClass().add("font-menu-button"); 648 fontFamilyComboBox.setMinWidth(FONT_FAMILY_MENUBUTTON_WIDTH); 649 fontFamilyComboBox.setPrefWidth(FONT_FAMILY_MENUBUTTON_WIDTH); 650 fontFamilyComboBox.setMaxWidth(FONT_FAMILY_MENUBUTTON_WIDTH); 651 fontFamilyComboBox.setFocusTraversable(false); 652 fontFamilyComboBox.setTooltip(new Tooltip(resources.getString("fontFamily"))); 653 toolbar2.getItems().add(fontFamilyComboBox); 654 655 // Fix for RT-32906, where all rows were being put through the cell factory 656 // so that they could be measured. Because we have a fixed width for the 657 // button this is unnecessary and so we tell the ComboBox to not measure 658 // any rows. 659 fontFamilyComboBox.getProperties().put("comboBoxRowsToMeasureWidth", 0); 660 661 fontFamilyComboBox.setCellFactory(new Callback<ListView<String>, ListCell<String>>() { 662 @Override public ListCell<String> call(ListView<String> param) { 663 final ListCell<String> cell = new ListCell<String>() { 664 @Override public void updateItem(String item, boolean empty) { 665 super.updateItem(item, empty); 666 if (item != null) { 667 setText(item); 668 setFont(new Font(item, 12)); 669 } 670 } 671 }; 672 cell.setMinWidth(FONT_FAMILY_MENU_WIDTH); 673 cell.setPrefWidth(FONT_FAMILY_MENU_WIDTH); 674 cell.setMaxWidth(FONT_FAMILY_MENU_WIDTH); 675 return cell; 676 } 677 }); 678 679 Platform.runLater(() -> { 680 final ObservableList<String> fonts = FXCollections.observableArrayList(Font.getFamilies()); 681 fonts.add(0, ""); 682 for (String fontFamily : fonts) { 683 fontFamilyComboBox.setValue(""); 684 fontFamilyComboBox.setItems(fonts); 685 } 686 }); 687 688 fontFamilyComboBox.valueProperty().addListener((observable, oldValue, newValue) -> { 689 executeCommand(FONT_FAMILY.getCommand(), ("".equals(newValue)) ? "''" : newValue); 690 }); 691 692 fontSizeComboBox = new ComboBox<String>(); 693 fontSizeComboBox.getStyleClass().add("font-menu-button"); 694 fontSizeComboBox.setFocusTraversable(false); 695 toolbar2.getItems().add(fontSizeComboBox); 696 697 fontSizeMap = new HashMap<String, String>(); 698 sizeFontMap = new HashMap<String, String>(); 699 700 createFontSizeMenuItem(SIZE_XX_SMALL, resources.getString("extraExtraSmall")); 701 createFontSizeMenuItem(SIZE_X_SMALL, resources.getString("extraSmall")); 702 createFontSizeMenuItem(SIZE_SMALL, resources.getString("small")); 703 Platform.runLater(() -> { 704 fontSizeComboBox.setValue(resources.getString("small")); 705 }); 706 createFontSizeMenuItem(SIZE_MEDIUM, resources.getString("medium")); 707 createFontSizeMenuItem(SIZE_LARGE, resources.getString("large")); 708 createFontSizeMenuItem(SIZE_X_LARGE, resources.getString("extraLarge")); 709 createFontSizeMenuItem(SIZE_XX_LARGE, resources.getString("extraExtraLarge")); 710 fontSizeComboBox.setTooltip(new Tooltip(resources.getString("fontSize"))); 711 712 fontSizeComboBox.setCellFactory(new Callback<ListView<String>, ListCell<String>>() { 713 @Override public ListCell<String> call(ListView<String> param) { 714 final ListCell<String> cell = new ListCell<String>() { 715 @Override public void updateItem(String item, boolean empty) { 716 super.updateItem(item, empty); 717 if (item != null) { 718 setText(item); 719 // Remove trailing non-digits to get the size (don't assume there's a space). 720 String size = item.replaceFirst("[^0-9.].*$", ""); 721 setFont(new Font((String)fontFamilyComboBox.getValue(), Double.valueOf(size))); 722 } 723 } 724 }; 725 return cell; 726 } 727 }); 728 729 730 fontSizeComboBox.valueProperty().addListener((observable, oldValue, newValue) -> { 731 Object fontSizeValue = getCommandValue(FONT_SIZE.getCommand()); 732 if (!newValue.equals(fontSizeValue)) { 733 executeCommand(FONT_SIZE.getCommand(), fontSizeMap.get(newValue)); 734 } 735 }); 736 737 toolbar2.getItems().add(new Separator(Orientation.VERTICAL)); 738 739 boldButton = addToggleButton(toolbar2, null, 740 resources.getString("boldIcon"), resources.getString("bold"), BOLD.getCommand(), "html-editor-bold"); 741 boldButton.setOnAction(event1 -> { 742 // Only use the bold button for paragraphs. We don't 743 // want to turn bold off for headings. 744 745 if ("<p>".equals(formatStyleMap.get(formatComboBox.getValue()))) { 746 executeCommand(BOLD.getCommand(), boldButton.selectedProperty().getValue().toString()); 747 } 748 }); 749 italicButton = addToggleButton(toolbar2, null, 750 resources.getString("italicIcon"), resources.getString("italic"), ITALIC.getCommand(), "html-editor-italic"); 751 underlineButton = addToggleButton(toolbar2, null, 752 resources.getString("underlineIcon"), resources.getString("underline"), UNDERLINE.getCommand(), "html-editor-underline"); 753 strikethroughButton = addToggleButton(toolbar2, null, 754 resources.getString("strikethroughIcon"), resources.getString("strikethrough"), STRIKETHROUGH.getCommand(), "html-editor-strike"); 755 756 toolbar2.getItems().add(new Separator(Orientation.VERTICAL)); 757 758 insertHorizontalRuleButton = addButton(toolbar2, resources.getString("insertHorizontalRuleIcon"), 759 resources.getString("insertHorizontalRule"), INSERT_HORIZONTAL_RULE.getCommand(), "html-editor-hr"); 760 // We override setOnAction to insert a new line. This fixes RT-16453 761 insertHorizontalRuleButton.setOnAction(event -> { 762 executeCommand(INSERT_NEW_LINE.getCommand(), null); 763 executeCommand(INSERT_HORIZONTAL_RULE.getCommand(), null); 764 updateToolbarState(false); 765 }); 766 767 fgColorButton = new ColorPicker(); 768 fgColorButton.getStyleClass().add("html-editor-foreground"); 769 fgColorButton.setFocusTraversable(false); 770 toolbar1.getItems().add(fgColorButton); 771 772 // JDK-8115747: Icon URLs are now specified in CSS. 773 // fgColorButton.applyCss(); 774 // ColorPickerSkin fgColorPickerSkin = (ColorPickerSkin) fgColorButton.getSkin(); 775 // String fgIcon = AccessController.doPrivileged((PrivilegedAction<String>) () -> HTMLEditorSkin.class.getResource(resources.getString("foregroundColorIcon")).toString()); 776 // ((StyleableProperty)fgColorPickerSkin.imageUrlProperty()).applyStyle(null,fgIcon); 777 778 fgColorButton.setValue(DEFAULT_FG_COLOR); 779 fgColorButton.setTooltip(new Tooltip(resources.getString("foregroundColor"))); 780 fgColorButton.setOnAction(ev1 -> { 781 Color newValue = fgColorButton.getValue(); 782 if (newValue != null) { 783 executeCommand(FOREGROUND_COLOR.getCommand(), colorValueToHex(newValue)); 784 fgColorButton.hide(); 785 } 786 }); 787 788 bgColorButton = new ColorPicker(); 789 bgColorButton.getStyleClass().add("html-editor-background"); 790 bgColorButton.setFocusTraversable(false); 791 toolbar1.getItems().add(bgColorButton); 792 793 // JDK-8115747: Icon URLs are now specified in CSS. 794 // bgColorButton.applyCss(); 795 // ColorPickerSkin bgColorPickerSkin = (ColorPickerSkin) bgColorButton.getSkin(); 796 // String bgIcon = AccessController.doPrivileged((PrivilegedAction<String>) () -> HTMLEditorSkin.class.getResource(resources.getString("backgroundColorIcon")).toString()); 797 // ((StyleableProperty)bgColorPickerSkin.imageUrlProperty()).applyStyle(null,bgIcon); 798 799 bgColorButton.setValue(DEFAULT_BG_COLOR); 800 bgColorButton.setTooltip(new Tooltip(resources.getString("backgroundColor"))); 801 802 bgColorButton.setOnAction(ev -> { 803 Color newValue = bgColorButton.getValue(); 804 if (newValue != null) { 805 executeCommand(BACKGROUND_COLOR.getCommand(), colorValueToHex(newValue)); 806 bgColorButton.hide(); 807 } 808 }); 809 } 810 811 private String colorValueToHex(Color c) { 812 return String.format((Locale)null, "#%02x%02x%02x", 813 Math.round(c.getRed() * 255), 814 Math.round(c.getGreen() * 255), 815 Math.round(c.getBlue() * 255)); 816 } 817 818 private Button addButton(ToolBar toolbar, final String iconName, String tooltipText, 819 final String command, final String styleClass) { 820 Button button = new Button(); 821 button.setFocusTraversable(false); 822 button.getStyleClass().add(styleClass); 823 toolbar.getItems().add(button); 824 825 Image icon = AccessController.doPrivileged((PrivilegedAction<Image>) () -> new Image(HTMLEditorSkin.class.getResource(iconName).toString())); 826 // button.setGraphic(new ImageView(icon)); 827 ((StyleableProperty)button.graphicProperty()).applyStyle(null, new ImageView(icon)); 828 button.setTooltip(new Tooltip(tooltipText)); 829 830 button.setOnAction(event -> { 831 executeCommand(command, null); 832 updateToolbarState(false); 833 }); 834 835 return button; 836 } 837 838 private ToggleButton addToggleButton(ToolBar toolbar, ToggleGroup toggleGroup, 839 final String iconName, String tooltipText, final String command, final String styleClass) { 840 ToggleButton toggleButton = new ToggleButton(); 841 toggleButton.setUserData(command); 842 toggleButton.setFocusTraversable(false); 843 toggleButton.getStyleClass().add(styleClass); 844 toolbar.getItems().add(toggleButton); 845 if (toggleGroup != null) { 846 toggleButton.setToggleGroup(toggleGroup); 847 } 848 849 Image icon = AccessController.doPrivileged((PrivilegedAction<Image>) () -> new Image(HTMLEditorSkin.class.getResource(iconName).toString())); 850 ((StyleableProperty)toggleButton.graphicProperty()).applyStyle(null, new ImageView(icon)); 851 // toggleButton.setGraphic(new ImageView(icon)); 852 853 toggleButton.setTooltip(new Tooltip(tooltipText)); 854 855 if (!BOLD.getCommand().equals(command)) { 856 toggleButton.selectedProperty().addListener((observable, oldValue, newValue) -> { 857 if (getCommandState(command) != newValue.booleanValue()) { 858 executeCommand(command, null); 859 } 860 }); 861 } 862 return toggleButton; 863 } 864 865 private void createFormatMenuItem(String formatValue, String label) { 866 formatComboBox.getItems().add(label); 867 formatStyleMap.put(label, formatValue); 868 styleFormatMap.put(formatValue, label); 869 } 870 871 private void createFontSizeMenuItem(String fontSizeValue, String label) { 872 fontSizeComboBox.getItems().add(label); 873 fontSizeMap.put(label, fontSizeValue); 874 sizeFontMap.put(fontSizeValue, label); 875 } 876 877 private void updateNodeOrientation() { 878 NodeOrientation orientation = getSkinnable().getEffectiveNodeOrientation(); 879 880 HTMLDocument htmlDocument = (HTMLDocument)webPage.getDocument(webPage.getMainFrame()); 881 HTMLElement htmlDocumentElement = (HTMLElement)htmlDocument.getDocumentElement(); 882 if (htmlDocumentElement.getAttribute("dir") == null) { 883 htmlDocumentElement.setAttribute("dir", (orientation == RIGHT_TO_LEFT) ? "rtl" : "ltr"); 884 } 885 886 } 887 888 private void updateToolbarState(final boolean updateAlignment) { 889 if (!webView.isFocused()) { 890 return; 891 } 892 893 atomicityCount++; 894 895 // These command aways return true. 896 copyButton.setDisable(!isCommandEnabled(CUT.getCommand())); 897 cutButton.setDisable(!isCommandEnabled(COPY.getCommand())); 898 pasteButton.setDisable(!isCommandEnabled(PASTE.getCommand())); 899 900 // undoButton.setDisable(!isCommandEnabled(UNDO.getCommand())); 901 // redoButton.setDisable(!isCommandEnabled(REDO.getCommand())); 902 903 // undoButton.setDisable(!isCommandEnabled(FORMAT.getCommand())); 904 // redoButton.setDisable(!isCommandEnabled(FORMAT.getCommand())); 905 906 insertHorizontalRuleButton.setDisable(!isCommandEnabled(INSERT_HORIZONTAL_RULE.getCommand())); 907 908 if (updateAlignment) { 909 alignLeftButton.setDisable(!isCommandEnabled(ALIGN_LEFT.getCommand())); 910 alignLeftButton.setSelected(getCommandState(ALIGN_LEFT.getCommand())); 911 alignCenterButton.setDisable(!isCommandEnabled(ALIGN_CENTER.getCommand())); 912 alignCenterButton.setSelected(getCommandState(ALIGN_CENTER.getCommand())); 913 alignRightButton.setDisable(!isCommandEnabled(ALIGN_RIGHT.getCommand())); 914 alignRightButton.setSelected(getCommandState(ALIGN_RIGHT.getCommand())); 915 alignJustifyButton.setDisable(!isCommandEnabled(ALIGN_JUSTIFY.getCommand())); 916 alignJustifyButton.setSelected(getCommandState(ALIGN_JUSTIFY.getCommand())); 917 } else { 918 if (alignmentToggleGroup.getSelectedToggle() != null) { 919 String command = alignmentToggleGroup.getSelectedToggle().getUserData().toString(); 920 if (isCommandEnabled(command) && !getCommandState(command) ) { 921 executeCommand(command, null); 922 } 923 } 924 } 925 926 if (alignmentToggleGroup.getSelectedToggle() == null) { 927 alignmentToggleGroup.selectToggle(alignLeftButton); 928 } 929 930 bulletsButton.setDisable(!isCommandEnabled(BULLETS.getCommand())); 931 bulletsButton.setSelected(getCommandState(BULLETS.getCommand())); 932 numbersButton.setDisable(!isCommandEnabled(NUMBERS.getCommand())); 933 numbersButton.setSelected(getCommandState(NUMBERS.getCommand())); 934 935 indentButton.setDisable(!isCommandEnabled(INDENT.getCommand())); 936 outdentButton.setDisable(!isCommandEnabled(OUTDENT.getCommand())); 937 938 formatComboBox.setDisable(!isCommandEnabled(FORMAT.getCommand())); 939 940 941 String formatValue = getCommandValue(FORMAT.getCommand()); 942 if (formatValue != null) { 943 String htmlTag = "<" + formatValue + ">"; 944 String comboFormatValue = styleFormatMap.get(htmlTag); 945 String format = formatComboBox.getValue(); 946 947 // if the format value is then we assume that we're dealing with a paragraph, 948 // which seems to correspond with the HTML output we receive. 949 if ((resetToolbarState || htmlTag.equals("<>") || htmlTag.equalsIgnoreCase("<div>") || htmlTag.equalsIgnoreCase("<blockquote>"))) { 950 formatComboBox.setValue(resources.getString("paragraph")); 951 } else if (format != null && ! format.equalsIgnoreCase(comboFormatValue)) { 952 formatComboBox.setValue(comboFormatValue); 953 } 954 } 955 956 fontFamilyComboBox.setDisable(!isCommandEnabled(FONT_FAMILY.getCommand())); 957 final String fontFamilyValue = getCommandValue(FONT_FAMILY.getCommand()); 958 if (fontFamilyValue != null) { 959 String fontFamilyStr = fontFamilyValue; 960 961 // stripping out apostrophe characters, which are appended to either 962 // end of the font face name when the font face has one or more spaces. 963 if (fontFamilyStr.startsWith("'")) { 964 fontFamilyStr = fontFamilyStr.substring(1); 965 } 966 if (fontFamilyStr.endsWith("'")) { 967 fontFamilyStr = fontFamilyStr.substring(0,fontFamilyStr.length() - 1); 968 } 969 970 Object selectedFont = fontFamilyComboBox.getValue(); 971 if (selectedFont instanceof String) { 972 if (!selectedFont.equals(fontFamilyStr)) { 973 974 ObservableList<String> fontFamilyItems = fontFamilyComboBox.getItems(); 975 String selectedComboFont = null; 976 for (String comboFontFamilyValue : fontFamilyItems) { 977 978 if (comboFontFamilyValue.equals(fontFamilyStr)) { 979 selectedComboFont = comboFontFamilyValue; 980 break; 981 } 982 // Note: By default, 'Dialog' is the font returned from webview. 983 // For presidio, we're just mapping to the default font. 984 if (comboFontFamilyValue.equals("") && fontFamilyStr.equals("Dialog")) { 985 selectedComboFont = comboFontFamilyValue; 986 break; 987 } 988 } 989 990 if (selectedComboFont != null) { 991 fontFamilyComboBox.setValue(selectedComboFont); 992 } 993 } 994 } 995 } 996 997 fontSizeComboBox.setDisable(!isCommandEnabled(FONT_SIZE.getCommand())); 998 String fontSizeValue = getCommandValue(FONT_SIZE.getCommand()); 999 1000 // added test for fontSizeValue == null to combat RT-28847 1001 if (resetToolbarState && fontSizeValue == null) { 1002 fontSizeComboBox.setValue(sizeFontMap.get(SIZE_SMALL)); 1003 } else { 1004 if (fontSizeValue != null) { 1005 if (!fontSizeComboBox.getValue().equals(sizeFontMap.get(fontSizeValue))) { 1006 fontSizeComboBox.setValue(sizeFontMap.get(fontSizeValue)); 1007 } 1008 } 1009 else { 1010 /* 1011 ** these is no font size set in webview, 1012 ** let's just use the default.... 1013 */ 1014 if ((fontSizeComboBox.getValue() == null) || !fontSizeComboBox.getValue().equals(sizeFontMap.get(SIZE_SMALL))) { 1015 fontSizeComboBox.setValue(sizeFontMap.get(SIZE_SMALL)); 1016 } 1017 } 1018 } 1019 1020 boldButton.setDisable(!isCommandEnabled(BOLD.getCommand())); 1021 boldButton.setSelected(getCommandState(BOLD.getCommand())); 1022 italicButton.setDisable(!isCommandEnabled(ITALIC.getCommand())); 1023 italicButton.setSelected(getCommandState(ITALIC.getCommand())); 1024 underlineButton.setDisable(!isCommandEnabled(UNDERLINE.getCommand())); 1025 underlineButton.setSelected(getCommandState(UNDERLINE.getCommand())); 1026 strikethroughButton.setDisable(!isCommandEnabled(STRIKETHROUGH.getCommand())); 1027 strikethroughButton.setSelected(getCommandState(STRIKETHROUGH.getCommand())); 1028 1029 fgColorButton.setDisable(!isCommandEnabled(FOREGROUND_COLOR.getCommand())); 1030 String foregroundColorValue = getCommandValue(FOREGROUND_COLOR.getCommand()); 1031 if (foregroundColorValue != null) { 1032 Color c = getColor(foregroundColorValue); 1033 fgColorButton.setValue(c); 1034 } 1035 1036 bgColorButton.setDisable(!isCommandEnabled(BACKGROUND_COLOR.getCommand())); 1037 String backgroundColorValue = getCommandValue(BACKGROUND_COLOR.getCommand()); 1038 if (backgroundColorValue != null) { 1039 Color c = getColor(backgroundColorValue); 1040 bgColorButton.setValue(c); 1041 } 1042 1043 atomicityCount = atomicityCount == 0 ? 0 : --atomicityCount; 1044 } 1045 1046 private void enableToolbar(final boolean enable) { 1047 Platform.runLater(() -> { 1048 1049 // Make sure buttons have been created to avoid NPE 1050 if (copyButton == null) return; 1051 1052 /* 1053 ** if we're to enable, we still only enable 1054 ** the cut/copy/paste buttons that make sense 1055 */ 1056 if (enable) { 1057 copyButton.setDisable(!isCommandEnabled(COPY.getCommand())); 1058 cutButton.setDisable(!isCommandEnabled(CUT.getCommand())); 1059 pasteButton.setDisable(!isCommandEnabled(PASTE.getCommand())); 1060 } else { 1061 copyButton.setDisable(true); 1062 cutButton.setDisable(true); 1063 pasteButton.setDisable(true); 1064 } 1065 1066 // undoButton.setDisable(!enable); 1067 // redoButton.setDisable(!enable); 1068 insertHorizontalRuleButton.setDisable(!enable); 1069 alignLeftButton.setDisable(!enable); 1070 alignCenterButton.setDisable(!enable); 1071 alignRightButton.setDisable(!enable); 1072 alignJustifyButton.setDisable(!enable); 1073 bulletsButton.setDisable(!enable); 1074 numbersButton.setDisable(!enable); 1075 indentButton.setDisable(!enable); 1076 outdentButton.setDisable(!enable); 1077 formatComboBox.setDisable(!enable); 1078 fontFamilyComboBox.setDisable(!enable); 1079 fontSizeComboBox.setDisable(!enable); 1080 boldButton.setDisable(!enable); 1081 italicButton.setDisable(!enable); 1082 underlineButton.setDisable(!enable); 1083 strikethroughButton.setDisable(!enable); 1084 fgColorButton.setDisable(!enable); 1085 bgColorButton.setDisable(!enable); 1086 }); 1087 } 1088 1089 private boolean executeCommand(String command, String value) { 1090 // The mentions of atomicity throughout this class relate back to RT-39941, 1091 // refer to that jira issue for more context. 1092 if (!enableAtomicityCheck || (enableAtomicityCheck && atomicityCount == 0)) { 1093 return webPage.executeCommand(command, value); 1094 } 1095 return false; 1096 } 1097 1098 private boolean isCommandEnabled(String command) { 1099 return webPage.queryCommandEnabled(command); 1100 } 1101 1102 private void setContentEditable(boolean b) { 1103 HTMLDocument htmlDocument = (HTMLDocument)webPage.getDocument(webPage.getMainFrame()); 1104 HTMLElement htmlDocumentElement = (HTMLElement)htmlDocument.getDocumentElement(); 1105 HTMLElement htmlBodyElement = (HTMLElement)htmlDocumentElement.getElementsByTagName("body").item(0); 1106 htmlBodyElement.setAttribute("contenteditable", Boolean.toString(b)); 1107 } 1108 1109 private void setDesignMode(String mode) { 1110 HTMLDocumentImpl htmlDocumentImpl = (HTMLDocumentImpl)webPage.getDocument(webPage.getMainFrame()); 1111 htmlDocumentImpl.setDesignMode(mode); 1112 } 1113 1114 private boolean getCommandState(String command) { 1115 return webPage.queryCommandState(command); 1116 } 1117 1118 private String getCommandValue(String command) { 1119 return webPage.queryCommandValue(command); 1120 } 1121 1122 private Color getColor(String value) { 1123 Color color = Color.web(value); 1124 /* The default background color for WebView, according to the HTML 1125 * standard is rgba=#00000000 (black). The canvas background is expected 1126 * to be white. 1127 */ 1128 if (color.toString().equals("0x00000000")) { 1129 color = Color.web("#FFFFFFFF"); 1130 } 1131 return color; 1132 } 1133 1134 private void applyTextFormatting() { 1135 if (getCommandState(BULLETS.getCommand()) || getCommandState(NUMBERS.getCommand())) { 1136 return; 1137 } 1138 1139 if (webPage.getClientCommittedTextLength() == 0) { 1140 String format = formatStyleMap.get(formatComboBox.getValue()); 1141 String font = fontFamilyComboBox.getValue().toString(); 1142 1143 executeCommand(FORMAT.getCommand(), format); 1144 executeCommand(FONT_FAMILY.getCommand(), font); 1145 } 1146 } 1147 1148 void print(PrinterJob job) { 1149 webView.getEngine().print(job); 1150 } 1151 1152 1153 1154 /*************************************************************************** 1155 * * 1156 * Support Classes * 1157 * * 1158 **************************************************************************/ 1159 1160 /** 1161 * Represents commands that can be passed into the HTMLEditor web engine. 1162 */ 1163 public enum Command { 1164 CUT("cut"), 1165 COPY("copy"), 1166 PASTE("paste"), 1167 1168 UNDO("undo"), 1169 REDO("redo"), 1170 1171 INSERT_HORIZONTAL_RULE("inserthorizontalrule"), 1172 1173 ALIGN_LEFT("justifyleft"), 1174 ALIGN_CENTER("justifycenter"), 1175 ALIGN_RIGHT("justifyright"), 1176 ALIGN_JUSTIFY("justifyfull"), 1177 1178 BULLETS("insertUnorderedList"), 1179 NUMBERS("insertOrderedList"), 1180 1181 INDENT("indent"), 1182 OUTDENT("outdent"), 1183 1184 FORMAT("formatblock"), 1185 FONT_FAMILY("fontname"), 1186 FONT_SIZE("fontsize"), 1187 1188 BOLD("bold"), 1189 ITALIC("italic"), 1190 UNDERLINE("underline"), 1191 STRIKETHROUGH("strikethrough"), 1192 1193 FOREGROUND_COLOR("forecolor"), 1194 BACKGROUND_COLOR("backcolor"), 1195 1196 INSERT_NEW_LINE("insertnewline"), 1197 INSERT_TAB("inserttab"); 1198 1199 private final String command; 1200 1201 Command(String command) { 1202 this.command = command; 1203 } 1204 1205 public String getCommand() { 1206 return command; 1207 } 1208 } 1209 }