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 (o instanceof Node) {
228 return (Node) o;
229 } else if (o instanceof Image) {
230 return new ImageView((Image) o);
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 = (TabPane) root;
251 for (Tab child : parent.getTabs()) {
252 Node found = findNode(child.getContent(), id);
253 if (found != null) return found;
254 }
255 } else if (root instanceof TitledPane) {
256 TitledPane parent = (TitledPane) root;
257 Node found = findNode(parent.getContent(), id);
258 if (found != null) return found;
259 } else if (root instanceof Accordion) {
260 Accordion parent = (Accordion) root;
261 for (TitledPane child : parent.getPanes()) {
262 Node found = findNode(child, id);
263 if (found != null) return found;
264 }
265 } else if (root instanceof SplitPane) {
266 SplitPane parent = (SplitPane) root;
267 for (Node child : parent.getItems()) {
268 Node found = findNode(child, id);
269 if (found != null) return found;
270 }
271 }
272 if (root instanceof Parent) {
273 Parent parent = (Parent) root;
274 for (Node child : parent.getChildrenUnmodifiable()) {
275 Node found = findNode(child, id);
276 if (found != null) return 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 = (MenuBar) root;
292 for (Menu child : menuBar.getMenus()) {
293 Object found = findElement(child, id);
294 if (found != null) return found;
295 }
296 } else if (root instanceof Menu) {
297 Menu menu = (Menu) root;
298 for (MenuItem child : menu.getItems()) {
299 Object found = findElement(child, id);
300 if (found != null) return found;
301 }
302 } else if (root instanceof TabPane) {
303 TabPane tabPane = (TabPane) root;
304 for (Tab child : tabPane.getTabs()) {
305 Object found = findElement(child, id);
306 if (found != null) return found;
307 }
308 } else if (root instanceof TitledPane) {
309 TitledPane parent = (TitledPane) root;
310 Object found = findElement(parent.getContent(), id);
311 if (found != null) return found;
312 } else if (root instanceof Accordion) {
313 Accordion parent = (Accordion) root;
314 for (TitledPane child : parent.getPanes()) {
315 Object found = findElement(child, id);
316 if (found != null) return found;
317 }
318 } else if (root instanceof SplitPane) {
319 SplitPane parent = (SplitPane) root;
320 for (Node child : parent.getItems()) {
321 Object found = findElement(child, id);
322 if (found != null) return found;
323 }
324 } else if (root instanceof Parent) {
325 Parent parent = (Parent) root;
326 for (Node child : parent.getChildrenUnmodifiable()) {
327 Object found = findElement(child, id);
328 if (found != null) return 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 }
|