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