JavaFXUtils.java
001 /*
002  * Copyright 2008-2015 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package griffon.javafx.support;
017 
018 import griffon.core.editors.ValueConversionException;
019 import javafx.beans.value.ChangeListener;
020 import javafx.beans.value.ObservableValue;
021 import javafx.event.ActionEvent;
022 import javafx.event.EventHandler;
023 import javafx.scene.Node;
024 import javafx.scene.Parent;
025 import javafx.scene.control.Accordion;
026 import javafx.scene.control.ButtonBase;
027 import javafx.scene.control.Control;
028 import javafx.scene.control.Menu;
029 import javafx.scene.control.MenuBar;
030 import javafx.scene.control.MenuItem;
031 import javafx.scene.control.SplitPane;
032 import javafx.scene.control.Tab;
033 import javafx.scene.control.TabPane;
034 import javafx.scene.control.TitledPane;
035 import javafx.scene.control.Tooltip;
036 import javafx.scene.image.Image;
037 import javafx.scene.image.ImageView;
038 import javafx.scene.input.KeyCombination;
039 
040 import javax.annotation.Nonnull;
041 import javax.annotation.Nullable;
042 import java.lang.reflect.Constructor;
043 import java.lang.reflect.InvocationTargetException;
044 import java.net.URL;
045 
046 import static griffon.util.GriffonClassUtils.getPropertyValue;
047 import static griffon.util.GriffonNameUtils.isBlank;
048 import static griffon.util.GriffonNameUtils.requireNonBlank;
049 import static java.util.Objects.requireNonNull;
050 
051 /**
052  @author Andres Almiray
053  */
054 public final class JavaFXUtils {
055     private static final String ERROR_CONTROL_NULL = "Argument 'control' must not be null";
056     private static final String ERROR_ACTION_NULL = "Argument 'action' must not be null";
057     private static final String ERROR_ICON_BLANK = "Argument 'iconUrl' must not be blank";
058     private static final String ERROR_ID_BLANK = "Argument 'id' must not be blank";
059     private static final String ERROR_ROOT_NULL = "Argument 'root' must not be null";
060 
061     private JavaFXUtils() {
062 
063     }
064 
065     public static void configure(final @Nonnull ButtonBase control, final @Nonnull JavaFXAction action) {
066         requireNonNull(control, ERROR_CONTROL_NULL);
067         requireNonNull(action, ERROR_ACTION_NULL);
068 
069         action.onActionProperty().addListener(new ChangeListener<EventHandler<ActionEvent>>() {
070             @Override
071             public void changed(ObservableValue<? extends EventHandler<ActionEvent>> observableValue, EventHandler<ActionEvent> oldValue, EventHandler<ActionEvent> newValue) {
072                 control.onActionProperty().set(newValue);
073             }
074         });
075         control.onActionProperty().set(action.getOnAction());
076 
077         action.nameProperty().addListener(new ChangeListener<String>() {
078             @Override
079             public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
080                 control.textProperty().set(newValue);
081             }
082         });
083         control.textProperty().set(action.getName());
084 
085         action.descriptionProperty().addListener(new ChangeListener<String>() {
086             @Override
087             public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
088                 setTooltip(control, newValue);
089             }
090         });
091         setTooltip(control, action.getDescription());
092 
093         action.iconProperty().addListener(new ChangeListener<String>() {
094             @Override
095             public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
096                 setIcon(control, newValue);
097             }
098         });
099         if (!isBlank(action.getIcon())) {
100             setIcon(control, action.getIcon());
101         }
102 
103         action.enabledProperty().addListener(new ChangeListener<Boolean>() {
104             @Override
105             public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldValue, Boolean newValue) {
106                 control.setDisable(!newValue);
107             }
108         });
109         control.setDisable(!action.getEnabled());
110     }
111 
112     public static void configure(final @Nonnull MenuItem control, final @Nonnull JavaFXAction action) {
113         requireNonNull(control, ERROR_CONTROL_NULL);
114         requireNonNull(action, ERROR_ACTION_NULL);
115 
116         action.onActionProperty().addListener(new ChangeListener<EventHandler<ActionEvent>>() {
117             @Override
118             public void changed(ObservableValue<? extends EventHandler<ActionEvent>> observableValue, EventHandler<ActionEvent> oldValue, EventHandler<ActionEvent> newValue) {
119                 control.onActionProperty().set(newValue);
120             }
121         });
122         control.onActionProperty().set(action.getOnAction());
123 
124         action.nameProperty().addListener(new ChangeListener<String>() {
125             @Override
126             public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
127                 control.textProperty().set(newValue);
128             }
129         });
130         control.textProperty().set(action.getName());
131 
132         action.iconProperty().addListener(new ChangeListener<String>() {
133             @Override
134             public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
135                 setIcon(control, newValue);
136             }
137         });
138         if (!isBlank(action.getIcon())) {
139             setIcon(control, action.getIcon());
140         }
141 
142         action.enabledProperty().addListener(new ChangeListener<Boolean>() {
143             @Override
144             public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldValue, Boolean newValue) {
145                 control.setDisable(!newValue);
146             }
147         });
148         control.setDisable(!action.getEnabled());
149 
150         action.acceleratorProperty().addListener(new ChangeListener<KeyCombination>() {
151             @Override
152             public void changed(ObservableValue<? extends KeyCombination> observable, KeyCombination oldValue, KeyCombination newValue) {
153                 control.setAccelerator(newValue);
154             }
155         });
156         control.setAccelerator(action.getAccelerator());
157     }
158 
159     public static void setTooltip(@Nonnull Control control, @Nullable String text) {
160         if (isBlank(text)) {
161             return;
162         }
163         requireNonNull(control, ERROR_CONTROL_NULL);
164 
165         Tooltip tooltip = control.tooltipProperty().get();
166         if (tooltip == null) {
167             tooltip = new Tooltip();
168             control.tooltipProperty().set(tooltip);
169         }
170         tooltip.setText(text);
171     }
172 
173     public static void setIcon(@Nonnull ButtonBase control, @Nonnull String iconUrl) {
174         requireNonNull(control, ERROR_CONTROL_NULL);
175         requireNonBlank(iconUrl, ERROR_ICON_BLANK);
176 
177         Node graphicNode = resolveIcon(iconUrl);
178         if (graphicNode != null) {
179             control.graphicProperty().set(graphicNode);
180         }
181     }
182 
183     public static void setIcon(@Nonnull MenuItem control, @Nonnull String iconUrl) {
184         requireNonNull(control, ERROR_CONTROL_NULL);
185         requireNonBlank(iconUrl, ERROR_ICON_BLANK);
186 
187         Node graphicNode = resolveIcon(iconUrl);
188         if (graphicNode != null) {
189             control.graphicProperty().set(graphicNode);
190         }
191     }
192 
193     @Nullable
194     private static Node resolveIcon(String iconUrl) {
195         if (iconUrl.contains("|")) {
196             // assume classname|arg format
197             return handleAsClassWithArg(iconUrl);
198         else {
199             URL resource = Thread.currentThread().getContextClassLoader().getResource(iconUrl);
200             if (resource != null) {
201                 return new ImageView(new Image(resource.toString()));
202             }
203         }
204         return null;
205     }
206 
207     @SuppressWarnings("unchecked")
208     private static Node handleAsClassWithArg(String str) {
209         String[] args = str.split("\\|");
210         if (args.length == 2) {
211             Class<?> iconClass = null;
212             try {
213                 iconClass = (Class<?>JavaFXUtils.class.getClassLoader().loadClass(args[0]);
214             catch (ClassNotFoundException e) {
215                 throw illegalValue(str, Node.class, e);
216             }
217 
218             Constructor<?> constructor = null;
219             try {
220                 constructor = iconClass.getConstructor(String.class);
221             catch (NoSuchMethodException e) {
222                 throw illegalValue(str, Node.class, e);
223             }
224 
225             try {
226                 Object o = constructor.newInstance(args[1]);
227                 if (instanceof Node) {
228                     return (Nodeo;
229                 else if (instanceof Image) {
230                     return new ImageView((Imageo);
231                 else {
232                     throw illegalValue(str, Node.class);
233                 }
234             catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
235                 throw illegalValue(str, Node.class, e);
236             }
237         else {
238             throw illegalValue(str, Node.class);
239         }
240     }
241 
242     @Nullable
243     public static Node findNode(@Nonnull Node root, @Nonnull String id) {
244         requireNonNull(root, ERROR_ROOT_NULL);
245         requireNonBlank(id, ERROR_ID_BLANK);
246 
247         if (id.equals(root.getId())) return root;
248 
249         if (root instanceof TabPane) {
250             TabPane parent = (TabPaneroot;
251             for (Tab child : parent.getTabs()) {
252                 Node found = findNode(child.getContent(), id);
253                 if (found != nullreturn found;
254             }
255         else if (root instanceof TitledPane) {
256             TitledPane parent = (TitledPaneroot;
257             Node found = findNode(parent.getContent(), id);
258             if (found != nullreturn found;
259         else if (root instanceof Accordion) {
260             Accordion parent = (Accordionroot;
261             for (TitledPane child : parent.getPanes()) {
262                 Node found = findNode(child, id);
263                 if (found != nullreturn found;
264             }
265         else if (root instanceof SplitPane) {
266             SplitPane parent = (SplitPaneroot;
267             for (Node child : parent.getItems()) {
268                 Node found = findNode(child, id);
269                 if (found != nullreturn found;
270             }
271         }
272         if (root instanceof Parent) {
273             Parent parent = (Parentroot;
274             for (Node child : parent.getChildrenUnmodifiable()) {
275                 Node found = findNode(child, id);
276                 if (found != nullreturn found;
277             }
278         }
279 
280         return null;
281     }
282 
283     @Nullable
284     public static Object findElement(@Nonnull Object root, @Nonnull String id) {
285         requireNonNull(root, ERROR_ROOT_NULL);
286         requireNonBlank(id, ERROR_ID_BLANK);
287 
288         if (id.equals(getPropertyValue(root, "id"))) return root;
289 
290         if (root instanceof MenuBar) {
291             MenuBar menuBar = (MenuBarroot;
292             for (Menu child : menuBar.getMenus()) {
293                 Object found = findElement(child, id);
294                 if (found != nullreturn found;
295             }
296         else if (root instanceof Menu) {
297             Menu menu = (Menuroot;
298             for (MenuItem child : menu.getItems()) {
299                 Object found = findElement(child, id);
300                 if (found != nullreturn found;
301             }
302         else if (root instanceof TabPane) {
303             TabPane tabPane = (TabPaneroot;
304             for (Tab child : tabPane.getTabs()) {
305                 Object found = findElement(child, id);
306                 if (found != nullreturn found;
307             }
308         else if (root instanceof TitledPane) {
309             TitledPane parent = (TitledPaneroot;
310             Object found = findElement(parent.getContent(), id);
311             if (found != nullreturn found;
312         else if (root instanceof Accordion) {
313             Accordion parent = (Accordionroot;
314             for (TitledPane child : parent.getPanes()) {
315                 Object found = findElement(child, id);
316                 if (found != nullreturn found;
317             }
318         else if (root instanceof SplitPane) {
319             SplitPane parent = (SplitPaneroot;
320             for (Node child : parent.getItems()) {
321                 Object found = findElement(child, id);
322                 if (found != nullreturn found;
323             }
324         else if (root instanceof Parent) {
325             Parent parent = (Parentroot;
326             for (Node child : parent.getChildrenUnmodifiable()) {
327                 Object found = findElement(child, id);
328                 if (found != nullreturn found;
329             }
330         }
331 
332         return null;
333     }
334 
335     private static ValueConversionException illegalValue(Object value, Class<?> klass) {
336         throw new ValueConversionException(value, klass);
337     }
338 
339     private static ValueConversionException illegalValue(Object value, Class<?> klass, Exception e) {
340         throw new ValueConversionException(value, klass, e);
341     }
342 }