/** * BetonQuest Editor - advanced quest creating tool for BetonQuest * Copyright (C) 2016 Jakub "Co0sh" Sapalski * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package pl.betoncraft.betonquest.editor.controller; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseButton; import javafx.scene.layout.Pane; import javafx.stage.Stage; import pl.betoncraft.betonquest.editor.BetonQuestEditor; import pl.betoncraft.betonquest.editor.custom.AutoCompleteTextField; import pl.betoncraft.betonquest.editor.custom.ConditionListCell; import pl.betoncraft.betonquest.editor.data.ConditionWrapper; import pl.betoncraft.betonquest.editor.data.ID; import pl.betoncraft.betonquest.editor.data.IdWrapper; import pl.betoncraft.betonquest.editor.model.exception.PackageNotFoundException; /** * Controls a pop-up window in which the user can edit a list. * * @author Jakub Sapalski */ public class SortedChoiceController<O extends ID, W extends IdWrapper<O>, F extends ListCell<W>> { @FXML private Pane root; private Stage stage; private String labelText; private ObservableList<W> chosen; private ObservableList<O> available; private Creator<O> creator; private CellFactory<F> cellFactory; private Wrapper<O, W> wrapper; private Refresher refresher; private final KeyCombination moveUp = new KeyCodeCombination(KeyCode.UP, KeyCombination.CONTROL_DOWN); private final KeyCombination moveDown = new KeyCodeCombination(KeyCode.DOWN, KeyCombination.CONTROL_DOWN); private final KeyCombination invert = new KeyCodeCombination(KeyCode.I, KeyCombination.CONTROL_DOWN); @FXML private Label label; @FXML private ListView<W> list; @FXML private AutoCompleteTextField field; private void refresh() { chosen.sort((ID o1, ID o2) -> o1.getIndex() - o2.getIndex()); label.setText(BetonQuestEditor.getInstance().getLanguage().getString(labelText)); list.setCellFactory(param -> cellFactory.getListCell()); list.getItems().setAll(chosen); ObservableList<ID> toChoose = FXCollections.observableArrayList(available); for (W wrapped : chosen) { toChoose.remove(wrapped.get()); } field.getEntries().clear(); for (ID id : toChoose) { field.getEntries().add(id.toString()); } } @FXML private void add() { try { String name = field.getText(); field.clear(); // check if name is not null if (name == null || name.isEmpty()) { BetonQuestEditor.showError("name-not-null"); return; } // check if it's not already there for (W wrapped : chosen) { if (wrapped.get().toString().equals(name)) { BetonQuestEditor.showError("already-exists"); return; } } // add it O object = null; // check if it already exists for (O id : available) { if (id.toString().equals(name)) { object = id; break; } } // create one if not if (object == null) { object = creator.create(name); if (!object.needsEditing() || object.edit()) { ObservableList<O> list = object.getList(); object.setIndex(list.size()); list.add(object); } else { return; } } W wrapped = wrapper.wrap(object); wrapped.setIndex(chosen.size()); chosen.add(wrapped); if (!available.contains(object)) { available.add(object); } refresh(); } catch (Exception e) { ExceptionController.display(e); } } @FXML private void edit() { try { IdWrapper<O> object = list.getSelectionModel().getSelectedItem(); if (object != null) { object.edit(); refresh(); } } catch (Exception e) { ExceptionController.display(e); } } @FXML private void delete() { try { IdWrapper<O> object = list.getSelectionModel().getSelectedItem(); if (object != null) { chosen.remove(object); refresh(); } } catch (Exception e) { ExceptionController.display(e); } } @FXML private void key(KeyEvent event) { try { if (event.getCode() == KeyCode.ESCAPE) { stage.close(); refresher.refresh(); return; } if (event.getCode() == KeyCode.DELETE && list.isFocused()) { delete(); return; } W item = list.getSelectionModel().getSelectedItem(); if (item != null) { int index = list.getItems().indexOf(item); if (moveUp.match(event)) { if (index > 0) { list.getItems().set(index, list.getItems().get(index - 1)); list.getItems().set(index - 1, item); } return; } else if (moveDown.match(event)) { if (index + 1 < list.getItems().size()) { list.getItems().set(index, list.getItems().get(index + 1)); list.getItems().set(index + 1, item); } return; } list.getSelectionModel().select(item); } if (item instanceof ConditionWrapper && invert.match(event)) { ConditionListCell.invert((ConditionWrapper) item); } } catch (Exception e) { ExceptionController.display(e); } } @FXML private void close() { } public static <O extends ID, W extends IdWrapper<O>, F extends ListCell<W>> void display(String labelText, ObservableList<W> chosen, ObservableList<O> available, Creator<O> creator, CellFactory<F> cellFactory, Wrapper<O, W> wrapper) { display(labelText, chosen, available, creator, cellFactory, wrapper, () -> { BetonQuestEditor.getInstance().refresh(); }); } public static <O extends ID, W extends IdWrapper<O>, F extends ListCell<W>> void display( String labelText, ObservableList<W> chosen, ObservableList<O> available, Creator<O> creator, CellFactory<F> cellFactory, Wrapper<O, W> wrapper, Refresher refresher ) { try { @SuppressWarnings("unchecked") SortedChoiceController<O, W, F> controller = (SortedChoiceController<O, W, F>) BetonQuestEditor .createWindow("view/window/SortedChoiceWindow.fxml", "choose-objects", 400, 500); if (controller == null) { return; } controller.stage = (Stage) controller.root.getScene().getWindow(); controller.labelText = labelText; controller.chosen = chosen; controller.available = available; controller.creator = creator; controller.cellFactory = cellFactory; controller.wrapper = wrapper; controller.refresher = refresher; controller.stage.setOnCloseRequest(event -> { refresher.refresh(); }); controller.list.setOnMouseClicked(event -> { if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) { controller.edit(); } }); controller.root.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> controller.key(event)); controller.refresh(); // fill the view controller.stage.showAndWait(); } catch (Exception e) { ExceptionController.display(e); } } public static interface Creator<O> { // this shouldn't throw any exceptions, packages exist public O create(String name) throws PackageNotFoundException; } public static interface CellFactory<F> { public F getListCell(); } public static interface Wrapper<O, W> { public W wrap(O item); } public static interface Refresher { public void refresh(); } }