| 
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 }
 |