1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.editor.i18n.I18N;
  35 import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
  36 import com.oracle.javafx.scenebuilder.kit.util.JavaLanguage;
  37 
  38 import java.util.ArrayList;
  39 import java.util.List;
  40 import java.util.Set;
  41 
  42 import javafx.event.ActionEvent;
  43 import javafx.event.EventHandler;
  44 import javafx.geometry.Pos;
  45 import javafx.scene.Node;
  46 import javafx.scene.control.Label;
  47 import javafx.scene.control.MenuItem;
  48 import javafx.scene.layout.HBox;
  49 import javafx.scene.layout.Priority;
  50 import javafx.scene.layout.StackPane;
  51 
  52 /**
  53  * Event handler editor. An event handler in FXML can be of 2 types: - script
  54  * event handler - controller method event handler.
  55  *
  56  *
  57  */
  58 public class EventHandlerEditor extends AutoSuggestEditor {
  59 
  60     private static final String HASH_STR = "#"; //NOI18N
  61     private final MenuItem controllerMethodMenuItem = new MenuItem(I18N.getString("inspector.event.menu.methodmode"));
  62     private final MenuItem scriptMenuItem = new MenuItem(I18N.getString("inspector.event.menu.scriptmode"));
  63     private boolean methodNameMode;
  64     private StackPane root = new StackPane();
  65     private HBox hbox = null;
  66     private List<String> suggestedMethods;
  67 
  68     public EventHandlerEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, List<String> suggestedMethods) {
  69         super(propMeta, selectedClasses, suggestedMethods); //NOI18N
  70         initialize(suggestedMethods);
  71     }
  72 
  73     private void initialize(List<String> suggestedMethods) {
  74         this.suggestedMethods = suggestedMethods;
  75 
  76         // text field events handling
  77         EventHandler<ActionEvent> onActionListener = event -> {
  78             String tfValue = getTextField().getText();
  79             if (tfValue == null || tfValue.isEmpty()) {
  80                 userUpdateValueProperty(null);
  81                 return;
  82             }
  83             if (methodNameMode) {
  84                 // method name should be a java identifier
  85                 if (!JavaLanguage.isIdentifier(tfValue)) {
  86                     System.err.println(I18N.getString("inspector.event.invalid.method", tfValue)); // Will go in message panel
  87                     handleInvalidValue(tfValue);
  88                     return;
  89                 }
  90             }
  91             Object value = getValue();
  92             assert value instanceof String;
  93             userUpdateValueProperty((String) value);
  94             getTextField().selectAll();
  95         };
  96         setTextEditorBehavior(this, getTextField(), onActionListener);
  97 
  98         scriptMenuItem.setOnAction(e -> {
  99             getTextField().setText(null);
 100             userUpdateValueProperty(null);
 101             switchToScriptMode();
 102         });
 103 
 104         controllerMethodMenuItem.setOnAction(e -> {
 105             getTextField().setText(null);
 106             userUpdateValueProperty(null);
 107             switchToMethodNameMode();
 108         });
 109         getMenu().getItems().add(controllerMethodMenuItem);
 110 
 111         // methodeName mode by default
 112         switchToMethodNameMode();
 113     }
 114 
 115     @Override
 116     public Object getValue() {
 117         String valueTf = getTextField().getText();
 118         if (valueTf == null || valueTf.isEmpty()) {
 119             return null; // default value
 120         }
 121         String value;
 122         if (methodNameMode) {
 123             value = HASH_STR + getTextField().getText();
 124         } else {
 125             value = getTextField().getText();
 126         }
 127 //        System.out.println("EventHandlerEditor : getValue() returns: '" + value + "'");
 128         return value;
 129     }
 130 
 131     @Override
 132     public void setValue(Object value) {
 133 //        System.out.println("EventHandlerEditor : setValue to '" + value + "'");
 134         setValueGeneric(value);
 135         if (isSetValueDone()) {
 136             return;
 137         }
 138 
 139         String valueStr;
 140         if (value == null) {
 141             if (!methodNameMode) {
 142                 switchToMethodNameMode();
 143             }
 144             valueStr = null;
 145         } else {
 146             assert value instanceof String;
 147             valueStr = (String) value;
 148             if (valueStr.startsWith(HASH_STR)) {
 149                 if (!methodNameMode) {
 150                     switchToMethodNameMode();
 151                 }
 152                 valueStr = valueStr.substring(1);
 153             } else if (!valueStr.startsWith(HASH_STR) && methodNameMode) {
 154                 switchToScriptMode();
 155             }
 156         }
 157         getTextField().setText(valueStr);
 158     }
 159 
 160     @Override
 161     public Node getValueEditor() {
 162         return super.handleGenericModes(root);
 163     }
 164 
 165     @Override
 166     public void reset(String name, String defaultValue, List<String> suggestedList) {
 167         super.reset(name, defaultValue, suggestedList);
 168         switchToMethodNameMode();
 169     }
 170 
 171     private void wrapInHBox() {
 172         hbox = new HBox();
 173         hbox.setAlignment(Pos.CENTER);
 174         Label hashLabel = new Label(HASH_STR);
 175         hashLabel.getStyleClass().add("symbol-prefix");
 176         hbox.getChildren().addAll(hashLabel, getRoot());
 177         HBox.setHgrow(hashLabel, Priority.NEVER);
 178         root.getChildren().clear();
 179         root.getChildren().add(hbox);
 180     }
 181 
 182     private void unwrapHBox() {
 183         root.getChildren().clear();
 184         root.getChildren().add(getRoot());
 185     }
 186 
 187     private void switchToMethodNameMode() {
 188         methodNameMode = true;
 189         resetSuggestedList(suggestedMethods);
 190         replaceMenuItem(controllerMethodMenuItem, scriptMenuItem);
 191         wrapInHBox();
 192     }
 193 
 194     private void switchToScriptMode() {
 195         methodNameMode = false;
 196         resetSuggestedList(new ArrayList<>());
 197         replaceMenuItem(scriptMenuItem, controllerMethodMenuItem);
 198         unwrapHBox();
 199     }
 200 }