AbstractJavaFXGriffonView.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 org.codehaus.griffon.runtime.javafx.artifact;
017 
018 import griffon.core.GriffonApplication;
019 import griffon.core.artifact.GriffonClass;
020 import griffon.core.artifact.GriffonController;
021 import griffon.core.controller.Action;
022 import griffon.core.controller.ActionManager;
023 import griffon.exceptions.GriffonException;
024 import griffon.exceptions.InstanceMethodInvocationException;
025 import griffon.javafx.support.JavaFXAction;
026 import griffon.javafx.support.JavaFXUtils;
027 import javafx.event.ActionEvent;
028 import javafx.fxml.FXMLLoader;
029 import javafx.fxml.JavaFXBuilderFactory;
030 import javafx.scene.Node;
031 import javafx.scene.Parent;
032 import javafx.scene.control.ButtonBase;
033 import javafx.scene.control.MenuItem;
034 import javafx.util.Callback;
035 import org.codehaus.griffon.runtime.core.artifact.AbstractGriffonView;
036 
037 import javax.annotation.Nonnull;
038 import javax.annotation.Nullable;
039 import javax.inject.Inject;
040 import java.io.IOException;
041 import java.net.URL;
042 import java.util.Map;
043 
044 import static griffon.javafx.support.JavaFXUtils.findElement;
045 import static griffon.util.ConfigUtils.stripFilenameExtension;
046 import static griffon.util.GriffonClassUtils.invokeInstanceMethod;
047 import static griffon.util.GriffonNameUtils.isBlank;
048 import static griffon.util.GriffonNameUtils.requireNonBlank;
049 import static java.util.Objects.requireNonNull;
050 
051 /**
052  * JavaFX-friendly implementation of the GriffonView interface.
053  *
054  @author Andres Almiray
055  @since 2.0.0
056  */
057 public abstract class AbstractJavaFXGriffonView extends AbstractGriffonView {
058     private static final String ACTION_TARGET_SUFFIX = "ActionTarget";
059     private static final String FXML_SUFFIX = ".fxml";
060 
061     public AbstractJavaFXGriffonView() {
062 
063     }
064 
065     /**
066      * Creates a new instance of this class.
067      *
068      @param application the GriffonApplication that holds this artifact.
069      @deprecated Griffon prefers field injection over constructor injector for artifacts as of 2.1.0
070      */
071     @Inject
072     @Deprecated
073     public AbstractJavaFXGriffonView(@Nonnull GriffonApplication application) {
074         super(application);
075     }
076 
077     @Nullable
078     protected Node loadFromFXML() {
079         return loadFromFXML(resolveBasename());
080     }
081 
082     @Nullable
083     protected Node loadFromFXML(@Nonnull String baseName) {
084         requireNonBlank(baseName, "Argument 'baseName' must not be blank");
085         if (baseName.endsWith(FXML_SUFFIX)) {
086             baseName = stripFilenameExtension(baseName);
087         }
088         baseName = baseName.replace('.''/');
089         String viewName = baseName + FXML_SUFFIX;
090         String styleName = baseName + ".css";
091 
092         URL viewResource = getResourceAsURL(viewName);
093         if (viewResource == null) {
094             return null;
095         }
096 
097         FXMLLoader fxmlLoader = new FXMLLoader(viewResource);
098         fxmlLoader.setResources(getApplication().getMessageSource().asResourceBundle());
099         fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory(getApplication().getApplicationClassLoader().get()));
100         fxmlLoader.setClassLoader(getApplication().getApplicationClassLoader().get());
101         fxmlLoader.setControllerFactory(new Callback<Class<?>, Object>() {
102             @Override
103             public Object call(Class<?> aClass) {
104                 return getMvcGroup().getView();
105             }
106         });
107 
108         try {
109             fxmlLoader.load();
110         catch (IOException e) {
111             throw new GriffonException(e);
112         }
113 
114         Parent node = fxmlLoader.getRoot();
115 
116         URL cssResource = getResourceAsURL(styleName);
117         if (cssResource != null) {
118             String uriToCss = cssResource.toExternalForm();
119             node.getStylesheets().add(uriToCss);
120         }
121 
122         return node;
123     }
124 
125     protected String resolveBasename() {
126         GriffonClass griffonClass = getGriffonClass();
127         String packageName = griffonClass.getPackageName();
128         String baseName = griffonClass.getLogicalPropertyName();
129         if (!isBlank(packageName)) {
130             baseName = packageName + "." + baseName;
131         }
132         return baseName;
133     }
134 
135     protected void connectActions(@Nonnull Node node, @Nonnull GriffonController controller) {
136         requireNonNull(node, "Argument 'node' must not be null");
137         requireNonNull(controller, "Argument 'controller' must not be null");
138         ActionManager actionManager = getApplication().getActionManager();
139         for (Map.Entry<String, Action> e : actionManager.actionsFor(controller).entrySet()) {
140             String actionTargetName = actionManager.normalizeName(e.getKey()) + ACTION_TARGET_SUFFIX;
141             Object control = findElement(node, actionTargetName);
142             if (control == nullcontinue;
143             JavaFXAction action = (JavaFXActione.getValue().getToolkitAction();
144 
145             if (control instanceof ButtonBase) {
146                 JavaFXUtils.configure(((ButtonBasecontrol), action);
147             else if (control instanceof MenuItem) {
148                 JavaFXUtils.configure(((MenuItemcontrol), action);
149             else if (control instanceof Node) {
150                 ((Nodecontrol).addEventHandler(ActionEvent.ACTION, action.getOnAction());
151             else {
152                 // does it support the onAction property?
153                 try {
154                     invokeInstanceMethod(control, "setOnAction", action.getOnAction());
155                 catch (InstanceMethodInvocationException imie) {
156                     // ignore
157                 }
158             }
159         }
160     }
161 
162     @Nullable
163     protected JavaFXAction toolkitActionFor(@Nonnull GriffonController controller, @Nonnull String actionName) {
164         Action action = actionFor(controller, actionName);
165         return action != null (JavaFXActionaction.getToolkitAction() null;
166     }
167 }