/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2014, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.gui.javafx.util;
import java.awt.Color;
import java.awt.Desktop;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Level;
import javafx.beans.DefaultProperty;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import static javafx.scene.layout.Region.USE_PREF_SIZE;
import org.geotoolkit.font.FontAwesomeIcons;
import org.geotoolkit.font.IconBuilder;
import org.geotoolkit.internal.GeotkFX;
import org.geotoolkit.internal.Loggers;
/**
* A custom component which contains a text field designed to contain a file path.
*
* Note : Override {@link #chooseInputContent() } method, to allow user to choose a path
* when he clicks on {@link #choosePathButton}.
*
* Note 2 : It's not its purpose, but you can also use distant URL as text field
* content. No completion will be proposed, but you will be able to use system
* browser to visit specified address.
*
* @author Alexis Manin (Geomatys)
*/
@DefaultProperty("text")
public abstract class AbstractPathTextField extends HBox {
public static final Image ICON_FIND = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_FOLDER_OPEN, 16, Color.DARK_GRAY), null);
public static final Image ICON_FORWARD = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_EXTERNAL_LINK, 16, Color.DARK_GRAY), null);
protected final TextField inputText = new TextField();
private final StringProperty textProperty = inputText.textProperty();
protected final PathCompletor completor = new PathCompletor(inputText);
protected final Button choosePathButton = new Button("", new ImageView(ICON_FIND));
protected final Button openPathButton = new Button("", new ImageView(ICON_FORWARD));
public AbstractPathTextField() {
choosePathButton.setOnAction((ActionEvent e)-> {
final String content = chooseInputContent();
if (content != null) {
setText(content);
}
});
inputText.setMinSize(0, USE_PREF_SIZE);
inputText.setMaxSize(Double.MAX_VALUE, USE_PREF_SIZE);
// TODO : put style rules in CSS
choosePathButton.setBackground(new Background(new BackgroundFill(null, CornerRadii.EMPTY, Insets.EMPTY)));
choosePathButton.setBorder(Border.EMPTY);
choosePathButton.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
openPathButton.setBackground(new Background(new BackgroundFill(null, CornerRadii.EMPTY, Insets.EMPTY)));
openPathButton.setBorder(Border.EMPTY);
openPathButton.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
choosePathButton.setTooltip(new Tooltip(
GeotkFX.getString("org.geotoolkit.gui.javafx.util.AbstractPathTextField.choosePath.tooltip")
));
openPathButton.setTooltip(new Tooltip(
GeotkFX.getString("org.geotoolkit.gui.javafx.util.AbstractPathTextField.openPath.tooltip")
));
final SimpleBooleanProperty notValidPath = new SimpleBooleanProperty(true);
textProperty.addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
notValidPath.set((textProperty.get() == null || textProperty.get().isEmpty()));
});
openPathButton.disableProperty().bind(notValidPath);
setAlignment(Pos.CENTER);
setHgrow(inputText, Priority.ALWAYS);
setSpacing(5);
getChildren().addAll(inputText, choosePathButton);
openPathButton.managedProperty().bind(openPathButton.visibleProperty());
if (Desktop.isDesktopSupported()) {
getChildren().add(openPathButton);
openPathButton.setOnAction((ActionEvent e) -> {
TaskManager.INSTANCE.submit(new OpenOnSystem(textProperty.get()));
});
}
}
public BooleanProperty showOpenProperty(){
return openPathButton.visibleProperty();
}
public String getText() {
return textProperty.get();
}
public void setText(final String input) {
textProperty.set(input);
}
public StringProperty textProperty() {
return textProperty;
}
/**
* Operation to perform for {@link #choosePathButton} action. It should be
* used to display an easy-to-use wizard to help user to choose its wanted
* value.
*
* @return the value chosen by the user, or null.
*/
protected abstract String chooseInputContent();
/**
* Build a valid URI from text written in input text control. Designed to
* allow implementations of the current class to modify specified paths as
* they need.
*
* @param inputText The current text value of {@link #inputText} control.
* @return A valid URI which points on the location defined by input text.
* @throws Exception malformatted URI
*/
protected abstract URI getURIForText(final String inputText) throws Exception;
/**
* Try to transform input text into a valid URI using {@link #getURIForText(java.lang.String) },
* then ask the system to open it.
*/
private class OpenOnSystem extends Task {
private final String inputText;
OpenOnSystem(final String inputText) {
super();
this.inputText = inputText;
updateTitle(GeotkFX.getString("org.geotoolkit.gui.javafx.util.AbstractPathTextField.taskTitle"));
}
@Override
protected Object call() throws Exception {
final URI toOpen = getURIForText(inputText);
// First, we try to open input file as a local file, to allow system to find best application to open it.
try {
Path local = Paths.get(toOpen.toString());
Desktop.getDesktop().open(local.toFile());
return null;
} catch (Exception ex) {
Loggers.JAVAFX.log(Level.FINE, "Input URI cannot be opened as a local file : " + toOpen, ex);
}
Desktop.getDesktop().browse(toOpen);
return null;
}
}
}