/* * 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 3 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.chooser; import java.awt.image.BufferedImage; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; import java.util.logging.Level; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.embed.swing.SwingFXUtils; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.Accordion; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.ScrollPane; import javafx.scene.control.SelectionMode; import javafx.scene.control.SplitPane; import javafx.scene.control.TitledPane; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.client.ClientFactory; import org.geotoolkit.coverage.amended.AmendedCoverageStore; import org.geotoolkit.data.AbstractFolderFeatureStoreFactory; import org.geotoolkit.data.FeatureStoreFactory; import org.geotoolkit.data.FileFeatureStoreFactory; import org.geotoolkit.db.AbstractJDBCFeatureStoreFactory; import org.geotoolkit.font.FontAwesomeIcons; import org.geotoolkit.font.IconBuilder; import org.geotoolkit.gui.javafx.parameter.FXParameterEditor; import org.geotoolkit.gui.javafx.util.FXOptionDialog; import org.geotoolkit.gui.javafx.util.FXUtilities; import org.geotoolkit.internal.GeotkFX; import org.geotoolkit.internal.Loggers; import org.geotoolkit.map.MapLayer; import org.geotoolkit.storage.DataStoreFactory; import org.geotoolkit.storage.DataStores; import org.geotoolkit.storage.coverage.CoverageStore; import org.geotoolkit.storage.coverage.CoverageStoreFactory; import org.opengis.parameter.ParameterValueGroup; /** * * @author Johann Sorel (Geomatys) */ public class FXStoreChooser extends SplitPane { public static final Predicate FEATUREFACTORY_ONLY = (Object t) -> t instanceof FeatureStoreFactory; public static final Predicate COVERAGEFACTORY_ONLY = (Object t) -> t instanceof CoverageStoreFactory; public static final Predicate CLIENTFACTORY_ONLY = (Object t) -> t instanceof ClientFactory; static final Comparator<Object> SORTER = new Comparator<Object>() { @Override public int compare(Object o1, Object o2) { //sort by type first final int o1p = getPriority(o1); final int o2p = getPriority(o2); if(o1p == o2p){ final String o1Name = getText(o1); final String o2Name = getText(o2); return o1Name.compareTo(o2Name); }else{ return Integer.compare(o1p, o2p); } } private String getText(Object candidate){ if(candidate instanceof DataStoreFactory){ return ((DataStoreFactory)candidate).getDisplayName().toString(); }else if(candidate instanceof ClientFactory){ return ((ClientFactory)candidate).getDisplayName().toString(); }else{ return ""; } } private int getPriority(Object o){ if(o instanceof FileFeatureStoreFactory){ return 1; }else if(o instanceof CoverageStoreFactory && !(o instanceof ClientFactory)){ return 2; }else if(o instanceof AbstractFolderFeatureStoreFactory){ return 3; }else if(o instanceof AbstractJDBCFeatureStoreFactory){ return 4; }else if(o instanceof ClientFactory){ return 6; }else{ return 5; } } }; private final Accordion accordion = new Accordion(); private final ListView<Object> factoryView = new ListView<>(); private final FXLayerChooser layerChooser = new FXLayerChooser(); private final FXParameterEditor paramEditor = new FXParameterEditor(); private final ScrollPane listScroll = new ScrollPane(factoryView); private final Button connectButton = new Button(GeotkFX.getString(FXStoreChooser.class,"apply")); private final Label infoLabel = new Label(); private final BooleanProperty decorateProperty = new SimpleBooleanProperty(false); public FXStoreChooser() { this(null); } public FXStoreChooser(Predicate factoryFilter) { final Set factoriesLst = new HashSet(); factoriesLst.addAll(DataStores.getAvailableFactories(null)); ObservableList factories = FXCollections.observableArrayList(factoriesLst); Collections.sort(factories, SORTER); if(factoryFilter!=null){ factories = factories.filtered(factoryFilter); } factoryView.setItems(factories); factoryView.setCellFactory((ListView<Object> param) -> new FactoryCell()); listScroll.setFitToHeight(true); listScroll.setFitToWidth(true); //hide the tree table header FXUtilities.hideTableHeader(paramEditor.getTreetable()); final BorderPane hpane = new BorderPane(infoLabel, null, connectButton, null, null); hpane.setPadding(new Insets(6, 6, 6, 6)); final BorderPane vpane = new BorderPane(paramEditor, null, null, hpane, null); vpane.setPadding(Insets.EMPTY); final TitledPane paneFactory = new TitledPane(GeotkFX.getString(FXStoreChooser.class,"factory"), listScroll); paneFactory.setFont(Font.font(paneFactory.getFont().getFamily(), FontWeight.BOLD, paneFactory.getFont().getSize())); final TitledPane paneConfig = new TitledPane(GeotkFX.getString(FXStoreChooser.class,"config"), vpane); accordion.getPanes().add(paneFactory); accordion.getPanes().add(paneConfig); accordion.setPrefSize(500, 500); accordion.setExpandedPane(paneFactory); getItems().add(accordion); getItems().add(layerChooser); factoryView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); factoryView.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<Object>() { @Override public void onChanged(ListChangeListener.Change<? extends Object> c) { final Object factory = factoryView.getSelectionModel().getSelectedItem(); final ParameterValueGroup param; if(factory instanceof FeatureStoreFactory){ param = ((FeatureStoreFactory)factory).getParametersDescriptor().createValue(); }else if(factory instanceof CoverageStoreFactory){ param = ((CoverageStoreFactory)factory).getParametersDescriptor().createValue(); }else if(factory instanceof ClientFactory){ param = ((ClientFactory)factory).getParametersDescriptor().createValue(); }else{ return; } paramEditor.setParameter(param); accordion.setExpandedPane(paneConfig); } }); connectButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { try { layerChooser.setSource(null); Object store = getStore(); if(decorateProperty.get() && store instanceof CoverageStore){ //decorate store store = new AmendedCoverageStore((CoverageStore) store); } layerChooser.setSource(store); } catch (DataStoreException ex) { infoLabel.setText("Error "+ex.getMessage()); Loggers.JAVAFX.log(Level.WARNING, ex.getMessage(),ex); } } }); } public BooleanProperty decorateProperty(){ return decorateProperty; } private void setLayerSelectionVisible(boolean layerVisible) { layerChooser.setVisible(layerVisible); } /** * * @return FeatureStore, CoverageStore or Client * @throws DataStoreException if store creation failed */ private Object getStore() throws DataStoreException { final Object factory = factoryView.getSelectionModel().getSelectedItem(); final ParameterValueGroup param = (ParameterValueGroup) paramEditor.getParameter(); if(factory instanceof FeatureStoreFactory){ return ((FeatureStoreFactory)factory).open(param); }else if(factory instanceof CoverageStoreFactory){ return ((CoverageStoreFactory)factory).open(param); }else if(factory instanceof ClientFactory){ return ((ClientFactory)factory).open(param); }else{ return null; } } private List<MapLayer> getSelectedLayers() throws DataStoreException { return layerChooser.getLayers(); } /** * Display a modal dialog. * * @param parent parent widget, can be null * @return FeatureStore, CoverageStore or Client * @throws DataStoreException if store creation failed */ public static Object showDialog(Node parent) throws DataStoreException{ return showDialog(parent, null); } /** * Display a modal dialog. * * @param parent Parent region over which dialog will be displayed. * @param predicate factory filter * @return FeatureStore, CoverageStore or Client * @throws DataStoreException if store creation failed */ public static Object showDialog(Node parent, Predicate predicate) throws DataStoreException{ final List lst = showDialog(parent, predicate, false); if(lst.isEmpty()){ return null; }else{ return lst.get(0); } } /** * Display a modal dialog choosing layers. * * @param parent Parent region over which dialog will be displayed. * @param predicate factory filter * @return created map layers. * @throws DataStoreException if store creation failed */ public static List<MapLayer> showLayerDialog(Node parent, Predicate predicate) throws DataStoreException{ return showDialog(parent, predicate, true); } private static List showDialog(Node parent, Predicate predicate, boolean layerVisible) throws DataStoreException{ final FXStoreChooser chooser = new FXStoreChooser(predicate); chooser.decorateProperty().set(true); chooser.setLayerSelectionVisible(layerVisible); final boolean res = FXOptionDialog.showOkCancel(parent, chooser, "", true); if (res) { if(layerVisible){ return chooser.getSelectedLayers(); }else{ final Object store = chooser.getStore(); if(store == null){ return Collections.EMPTY_LIST; }else{ return Collections.singletonList(store); } } } else { return Collections.EMPTY_LIST; } } public static class FactoryCell extends ListCell{ @Override protected void updateItem(Object item, boolean empty) { super.updateItem(item, empty); setText(null); setGraphic(null); if(!empty && item!=null){ if(item instanceof DataStoreFactory){ setText(((DataStoreFactory)item).getDisplayName().toString()); }else if(item instanceof ClientFactory){ setText(((ClientFactory)item).getDisplayName().toString()); } setGraphic(new ImageView(findIcon(item))); } } } private static final Image EMPTY_24 = SwingFXUtils.toFXImage(new BufferedImage(24, 24, BufferedImage.TYPE_INT_ARGB),null); private static final Image ICON_SERVER = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_GLOBE, 24, FontAwesomeIcons.DISABLE_COLOR),null); private static final Image ICON_DATABASE = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_DATABASE, 24, FontAwesomeIcons.DISABLE_COLOR),null); private static final Image ICON_VECTOR = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_PENCIL_SQUARE, 24, FontAwesomeIcons.DISABLE_COLOR),null); private static final Image ICON_COVERAGE = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_PICTURE_O, 24, FontAwesomeIcons.DISABLE_COLOR),null); private static final Image ICON_FOLDER = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_FOLDER, 24, FontAwesomeIcons.DISABLE_COLOR),null); private static final Image ICON_FILE = SwingFXUtils.toFXImage(IconBuilder.createImage(FontAwesomeIcons.ICON_FILE, 24, FontAwesomeIcons.DISABLE_COLOR),null); private static Image findIcon(Object candidate){ Image icon = EMPTY_24; if(candidate instanceof AbstractFolderFeatureStoreFactory){ icon = ICON_FOLDER; }else if(candidate instanceof FileFeatureStoreFactory){ icon = ICON_FILE; }else if(candidate instanceof ClientFactory){ icon = ICON_SERVER; }else if(candidate instanceof AbstractJDBCFeatureStoreFactory){ icon = ICON_DATABASE; }else if(candidate instanceof CoverageStoreFactory){ icon = ICON_COVERAGE; }else if(candidate instanceof FeatureStoreFactory){ icon = ICON_VECTOR; } return icon; } }