/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.cismet.tools.gui;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.html.HTMLAnchorElement;
import java.awt.EventQueue;
import de.cismet.tools.BrowserLauncher;
/**
* DOCUMENT ME!
*
* @author daniel
* @version $Revision$, $Date$
*/
public class FXWebViewPanel extends JFXPanel {
//~ Static fields/initializers ---------------------------------------------
private static final Logger LOG = Logger.getLogger(FXWebViewPanel.class);
//~ Instance fields --------------------------------------------------------
protected WebEngine webEng = null;
protected WebView webView;
protected Scene scene;
//~ Constructors -----------------------------------------------------------
/**
* Creates a new FXWebViewPanel object.
*/
public FXWebViewPanel() {
Platform.setImplicitExit(false);
Platform.runLater(new Runnable() {
@Override
public void run() {
scene = createBrowserScene();
FXWebViewPanel.this.setScene(scene);
}
});
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private Scene createBrowserScene() {
webView = new WebView();
// disabling the context menue
webView.setContextMenuEnabled(false);
webEng = webView.getEngine();
webEng.setJavaScriptEnabled(true);
// Log errors that happen in the web engine
webEng.getLoadWorker().exceptionProperty().addListener(new ChangeListener<Throwable>() {
@Override
public void changed(final ObservableValue<? extends Throwable> ov,
final Throwable t,
final Throwable t1) {
LOG.error("Error in WebEngine Load Worker", t);
}
});
// every time a new document was loaded, we need to add listeners to the a elements in that document, that check
// if that link represents a non hml document we want to open in the system browser
webEng.getLoadWorker().stateProperty().addListener(
new ChangeListener<Worker.State>() {
@Override
public void changed(final ObservableValue ov,
final Worker.State oldState,
final Worker.State newState) {
if (newState == Worker.State.SUCCEEDED) {
addClickListenerToLinks();
}
}
});
final BorderPane pane = new BorderPane();
pane.setPadding(new Insets(5));
pane.setCenter(webView);
final Scene s = new Scene(pane);
return s;
}
/**
* this method adds a EventListener to each link element in the loaded document. The added EventListner checks if
* the href of the link points to a non html document or anchor and if so opens he url in the external browser
*/
private void addClickListenerToLinks() {
final NodeList nodeList = webEng.getDocument().getElementsByTagName("a");
for (int i = 0; i < nodeList.getLength(); i++) {
final Node node = nodeList.item(i);
final EventTarget eventTarget = (EventTarget)node;
eventTarget.addEventListener("click", new EventListener() {
@Override
public void handleEvent(final Event evt) {
final EventTarget target = evt.getCurrentTarget();
final HTMLAnchorElement anchorElement = (HTMLAnchorElement)target;
final String href = anchorElement.getHref();
final String targetWindow = anchorElement.getTarget();
if (((targetWindow != null) && targetWindow.equalsIgnoreCase("_blank"))
|| ((href != null) && !href.endsWith("html") && !href.contains("#"))) {
openInSystemBrowser(href);
evt.preventDefault();
}
}
}, false);
}
}
/**
* DOCUMENT ME!
*
* @param url DOCUMENT ME!
*/
private void openInSystemBrowser(final String url) {
if (LOG.isDebugEnabled()) {
LOG.debug("Will Launch 2:" + url);
}
try {
if (url != null) {
final Thread t = new Thread() {
@Override
public void run() {
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Will Launch 1:" + url);
}
BrowserLauncher.openURL(url);
} catch (Exception ex) {
LOG.error(ex.getMessage(), ex);
}
}
};
t.start();
}
} catch (Exception ex) {
LOG.error(ex.getMessage(), ex);
}
}
/**
* Returns the {@link WebEngine} used by the {@link WebView}. Hence the WebView is a JavaFX component, it should
* only be accessed from JavaFX Application Thread.
*
* @return DOCUMENT ME!
*/
public WebEngine getWebEngine() {
return webEng;
}
/**
* Returns the {@link WebView} used to render html documents. Hence the{@link WebView} is a JavaFX component, it
* should only be accessed from JavaFX Application Thread.
*
* @return DOCUMENT ME!
*/
public WebView getWebView() {
return webView;
}
/**
* DOCUMENT ME!
*
* @param url DOCUMENT ME!
*/
public void loadUrl(final String url) {
if (webEng == null) {
LOG.warn("JavaFX WebEnginge is not initialized. can not load url: " + url);
return;
}
Platform.runLater(new Runnable() {
@Override
public void run() {
webEng.load(url);
}
});
}
/**
* DOCUMENT ME!
*
* @param htmlContent DOCUMENT ME!
*/
public void loadContent(final String htmlContent) {
final Runnable loader = new Runnable() {
@Override
public void run() {
try {
webEng.loadContent(htmlContent);
webEng.setJavaScriptEnabled(true);
} catch (Exception e) {
LOG.error("Problem during the rendering of the htmlContent", e);
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
if (webEng == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("JavaFX WebEnginge is not initialized. Will wait for 1 sec and try again");
}
try {
Thread.sleep(1000);
} catch (Exception interuppted) {
}
if (webEng == null) {
LOG.warn("JavaFX WebEnginge is not initialized. can not load html content: " + htmlContent);
} else {
Platform.runLater(loader);
}
} else {
Platform.runLater(loader);
}
}
}).start();
}
}