All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.rpgframework.jfx.wizard.WizardPageGenerator Maven / Gradle / Ivy

package de.rpgframework.jfx.wizard;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.prelle.javafx.ResponsiveControlManager;
import org.prelle.javafx.SymbolIcon;
import org.prelle.javafx.TitledComponent;
import org.prelle.javafx.WindowMode;
import org.prelle.javafx.Wizard;
import org.prelle.javafx.WizardPage;

import de.rpgframework.ResourceI18N;
import de.rpgframework.character.CharacterHandle;
import de.rpgframework.character.RuleSpecificCharacterObject;
import de.rpgframework.genericrpg.chargen.CharacterGenerator;
import de.rpgframework.genericrpg.chargen.GeneratorId;
import de.rpgframework.genericrpg.chargen.IGeneratorWrapper;
import de.rpgframework.genericrpg.chargen.Rule;
import de.rpgframework.genericrpg.chargen.RuleInterpretation;
import de.rpgframework.genericrpg.data.CommonCharacter;
import de.rpgframework.genericrpg.data.IAttribute;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import javafx.util.StringConverter;

/**
 * @author Stefan
 *
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class WizardPageGenerator, G extends CharacterGenerator> extends WizardPage {

	private final static Logger logger = System.getLogger(WizardPageGenerator.class.getPackageName());

	private static PropertyResourceBundle RES = (PropertyResourceBundle) ResourceBundle.getBundle(WizardPageGenerator.class.getPackageName()+".WizardPages");

	private List> allKnown;
	private Rule[] allRules;

	private IGeneratorWrapper wrapper;
	private ChoiceBox cbStrictness;
	private ListView> options;
	private RuleChoiceBox bxRules;
	private Label descHeading;
	private Label description;
	private VBox bxExtraAndRules;
	private VBox descBox;
	private VBox layout;
	private Function, String[]> nameGetter;
	private ObjectProperty extraNode = new SimpleObjectProperty<>();

	private Class selected;

	//--------------------------------------------------------------------
	public WizardPageGenerator(Wizard wizard,  IGeneratorWrapper model, List> values,
			List interpretations,
			Rule[] allRules,
			Function, String[]> nameGetter) {
		super(wizard);
		allKnown = values;
		setTitle(ResourceI18N.get(RES, "wizard.generator.title"));
		this.wrapper = model;
		this.allRules= allRules;
		this.nameGetter = nameGetter;

		initComponents();
		initLayout();
		initStyle();
		initInteractivity();
//		options.getItems().setAll(values);
		String expect = Locale.getDefault().getLanguage();
		List filtered = interpretations.stream()
				.filter( i -> i.getLanguage()==null || expect.equals(i.getLanguage()))
				.collect(Collectors.toList());
		cbStrictness.getItems().setAll(filtered);
		/*
		 * Try to select current interpretation - otherwise use first
		 */
		cbStrictness.getSelectionModel().select(0);
		if (wrapper!=null && (wrapper.getModel() instanceof de.rpgframework.genericrpg.data.CommonCharacter)) {
			String interID = ((CommonCharacter)wrapper.getModel()).getStrictness();
			for (RuleInterpretation inter : interpretations) {
				if (inter.getId().equals(interID)) {
					cbStrictness.getSelectionModel().select(inter);
					break;
				}
			}
		}
		if (cbStrictness.getValue()!=null)
			logger.log(Level.INFO, "Start with strictness '"+cbStrictness.getValue().getId()+"'");
		interpretationChanged();


		logger.log(Level.INFO, "CURRENT: "+model.getWrapped());
		if (!options.getItems().isEmpty()) {
			options.getSelectionModel().select((Class) model.getWrapped().getClass());
		}

	}

	//-------------------------------------------------------------------
	private void initComponents() {
		cbStrictness = new ChoiceBox<>();
		cbStrictness.setConverter(new StringConverter() {
			public String toString(RuleInterpretation value) { return (value!=null)?value.getName():ResourceI18N.get(RES, "interpretation.label");}
			public RuleInterpretation fromString(String arg0) {return null;	}
		});

		options= new ListView>();
		options.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		options.setCellFactory(new Callback>, ListCell>>() {
			public ListCell> call(ListView> arg0) {
				return new CharacterGeneratorListCell(nameGetter);
			}
		});
//		options.getItems().setAll(CharacterGeneratorRegistry.getGenerators());
		int lines = Math.max(4, options.getItems().size());
		options.setStyle("-fx-pref-width: 15em; -fx-pref-height: "+(lines*3.5)+"em");

		bxRules = new RuleChoiceBox(Rule.EffectOn.CHARGEN, Rule.EffectOn.COMMON);

		descHeading = new Label();
		description = new Label();
		description.setWrapText(true);
	}

	//-------------------------------------------------------------------
	private void initLayout() {
		descBox = new VBox(20);
		descBox.getChildren().addAll(descHeading, description);
		description.setStyle("-fx-pref-width: 18em");

		layout = new VBox();

//		FlowPane content = new FlowPane();
//		content.setHgap(20);
//		content.setVgap(20);
//		content.getChildren().addAll(options, descBox);
		super.setContent(layout);

		bxExtraAndRules = new VBox(bxRules);
		if (extraNode.get()!=null)
			bxExtraAndRules.getChildren().add(0,extraNode.get());

//		super.setBackContent(new Label("Backcontent"));
		super.setBackContent(bxExtraAndRules);
		updateLayout();
	}

	//-------------------------------------------------------------------
	private void updateLayout() {
		if (logger.isLoggable(Level.TRACE))
			logger.log(Level.TRACE, "ENTER updateLayout("+ResponsiveControlManager.getCurrentMode()+")");
		logger.log(Level.WARNING, "ENTER updateLayout("+ResponsiveControlManager.getCurrentMode()+")");
		layout.getChildren().clear();
		bxExtraAndRules.getChildren().retainAll(bxRules);
		if (extraNode.get()!=null)
			bxExtraAndRules.getChildren().add(0,extraNode.get());

		layout.getChildren().addAll(options, descBox);
		TitledComponent tcStrict = new TitledComponent(ResourceI18N.get(RES, "wizard.generator.interpretation.label"),  cbStrictness);
		VBox bxList = new VBox(5, tcStrict, options);
		options.setMaxHeight(Double.MAX_VALUE);
		bxList.setMaxHeight(Double.MAX_VALUE);
		VBox.setVgrow(options, Priority.ALWAYS);

		Label lbFineTune = new Label(ResourceI18N.get(RES, "wizard.generator.backheader.finetune"));
		Region buf = new Region();
		buf.setMaxWidth(Double.MAX_VALUE);
		HBox backHeader = new HBox(10, lbFineTune, buf, new SymbolIcon("setting"));
		HBox.setHgrow(buf, Priority.ALWAYS);
		backHeader.setMaxWidth(Double.MAX_VALUE);
		HBox.setMargin(lbFineTune, new Insets(0,0,0,10));
		HBox.setMargin(backHeader.getChildren().get(2), new Insets(0,10,0,0));

		switch (ResponsiveControlManager.getCurrentMode()) {
		case MINIMAL:
			super.setBackHeader(backHeader);
			VBox helpV = new VBox(20, bxList, descBox);
			helpV.setAlignment(Pos.TOP_LEFT);
			VBox.setVgrow(bxList, Priority.ALWAYS);
			VBox.setVgrow(descBox, Priority.ALWAYS);
			bxRules.setStyle("-fx-pref-width: 23em; -fx-min-height: 15em");
			bxList.setStyle("-fx-pref-height: 14em");
			layout.getChildren().add(helpV);
			break;
		default:
			HBox helpH = new HBox(20, bxList, descBox);
			helpH.setMaxHeight(Double.MAX_VALUE);
			VBox.setVgrow(helpH, Priority.ALWAYS);
			if (ResponsiveControlManager.getCurrentMode()==WindowMode.EXPANDED) {
				super.setBackContent(null);
				super.setBackHeader(null);
				bxRules.setMaxHeight(Double.MAX_VALUE);
				bxExtraAndRules.setMaxHeight(Double.MAX_VALUE);
				helpH.setFillHeight(true);
				helpH.getChildren().add(1, bxExtraAndRules);
				HBox.setHgrow(bxRules, Priority.SOMETIMES);
			} else {
				super.setBackContent(bxExtraAndRules);
				super.setBackHeader(backHeader);
			}
			bxRules.setStyle("-fx-pref-width: 30em; -fx-max-width: 40em");
			bxList.setStyle("-fx-pref-height: 20em");
			layout.getChildren().add(helpH);
			break;
		}

		if (logger.isLoggable(Level.TRACE))
			logger.log(Level.TRACE, "LEAVE updateLayout("+ResponsiveControlManager.getCurrentMode()+")");
	}

	//-------------------------------------------------------------------
	private void initStyle() {
		descHeading.getStyleClass().add("text-small-subheader");
		description.getStyleClass().add("text-body");
//		setImageInsets(new Insets(-40,0,0,0));
	}

	//-------------------------------------------------------------------
	private void initInteractivity() {
		options.getSelectionModel().selectedItemProperty().addListener(new ChangeListener>() {
			public void changed(ObservableValue> observable,
					Class oldValue, Class newValue) {
				if (newValue!=null) {
					logger.log(Level.DEBUG, "selected "+newValue);
					descHeading.setText(nameGetter.apply(newValue)[0]);
					description.setText(nameGetter.apply(newValue)[1]);
				} else {
					descHeading.setText(null);
					description.setText(null);
				}
				selectGenerator(newValue);
			}
		});

		cbStrictness.getSelectionModel().selectedItemProperty().addListener( (ov,o,n) -> interpretationChanged());
		extraNode.addListener( (ov,o,n) -> {
			updateLayout();
		});
	}

	// -------------------------------------------------------------------
	private void interpretationChanged() {
		RuleInterpretation n = cbStrictness.getValue();
		try {
//			if (logger.isLoggable(Level.TRACE))
				logger.log(Level.ERROR, "ENTER: interpretationChanged  (" + n + ")");
			if (n != null && wrapper != null
					&& (wrapper.getModel() instanceof de.rpgframework.genericrpg.data.CommonCharacter)) {
				((CommonCharacter) wrapper.getModel()).setStrictness(n.getId());
				wrapper.getRuleController().updateEffectiveRules();
			}
			Class current = options.getSelectionModel().getSelectedItem();
			List> toSet = new ArrayList<>(allKnown);
			if (wrapper != null) {
				// Update list of rule settings
				bxRules.setData(wrapper);
				// Check is list shall be restricted
				if (n!=null && n.getRestrictGenTo() != null) {
					logger.log(Level.DEBUG, "CharGens are restricted");
					toSet = toSet.stream().filter(r -> {
						logger.log(Level.ERROR, "Check {0}",r);
						try {
							GeneratorId anno = r.getAnnotation(GeneratorId.class);
							String id=null;
							if (anno==null) {
								logger.log(Level.WARNING, r+" is missing @GeneratorId - now I need to instantiate it to obtain the ID");
								System.err.println(r+" is missing @GeneratorId - now I need to instantiate it to obtain the ID");
								id = r.getDeclaredConstructor().newInstance().getId();
							} else {
								id = anno.value();
							}
							logger.log(Level.ERROR, "Is {0} in {1} = {2}",id, n.getRestrictGenTo(),n.getRestrictGenTo().contains(id));
							return n.getRestrictGenTo().contains(id);
						} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
								| NoSuchMethodException | SecurityException | InstantiationException e) {
							e.printStackTrace();
							return true;
						}
					}).collect(Collectors.toList());
				}
				logger.log(Level.ERROR, "Allowed chargens: "+toSet);
				options.getItems().setAll(toSet);
			}
			if (current != null && toSet.contains(current)) {
				options.getSelectionModel().select(current);
			} else if (!toSet.isEmpty()) {
				options.getSelectionModel().select(toSet.get(0));
			}
		} finally {
			if (logger.isLoggable(Level.TRACE))
				logger.log(Level.TRACE, "LEAVE: interpretationChanged");
		}
	}

	//-------------------------------------------------------------------
	/**
	 * @see org.prelle.javafx.ResponsiveControl#setResponsiveMode(org.prelle.javafx.WindowMode)
	 */
	@Override
	public void setResponsiveMode(WindowMode value) {
		logger.log(Level.WARNING, "ENTER setResponsiveMode(" + value + ")");
		updateLayout();
	}

	//-------------------------------------------------------------------
	private void selectGenerator(Class clazz) {
		if (logger.isLoggable(Level.TRACE))
			logger.log(Level.TRACE, "ENTER selectGenerator(" + clazz + ")");
		try {
			// Only change if new class is not null
			if (clazz == null)
				return;
			// Find out what generator is currently used - if any
			Class currentlyUsed = null;
			if (wrapper != null && wrapper.getWrapped() != null) {
				currentlyUsed = (Class) wrapper.getWrapped().getClass();
			}
			logger.log(Level.INFO, "currentlyUsed " + currentlyUsed);
//			// Only change something, if it is not already used
//			if (currentlyUsed != null && currentlyUsed == clazz) {
//				// Is already used - do nothing
//				return;
//			}

			// Create a new instance of the selected generator
			logger.log(Level.INFO, "Change generator to " + clazz);
			try {
//				G newGen = clazz.getConstructor().newInstance();
//				wrapper.setWrapped(newGen);
//				descHeading.setText(newGen.getName());
//				description.setText(newGen.getDescription());
				selected = clazz;
			} catch (Exception e) {
				logger.log(Level.ERROR,"Failed instantiating new generator " + clazz, e);
				descHeading.setText(null);
				description.setText(null);
			}
		} finally {
			if (logger.isLoggable(Level.TRACE))
				logger.log(Level.TRACE, "LEAVE selectGenerator(" + clazz + ")");
		}
	}

	//-------------------------------------------------------------------
	/**
	 * @see org.prelle.javafx.WizardPage#pageVisited()
	 */
	@Override
	public void pageVisited() {
		logger.log(Level.DEBUG, "pageVisited: "+wrapper.getWrapped());
		logger.log(Level.DEBUG, "selection is "+options.getSelectionModel().getSelectedItem());
		// Does the selection require updating?
		if (wrapper.getWrapped()!=null && options.getSelectionModel().getSelectedItem()!=wrapper.getWrapped().getClass()) {
			options.getSelectionModel().select((Class) wrapper.getWrapped().getClass());
		}
	}

	//-------------------------------------------------------------------
	/**
	 * @see org.prelle.javafx.WizardPage#pageLeft()
	 */
	@Override
	public void pageLeft() {
		if (wrapper.getModel() instanceof CommonCharacter) {
			logger.log(Level.INFO, "Set chargen used to {0}", wrapper.getId());
			((CommonCharacter)wrapper.getModel()).setCharGenUsed(wrapper.getId());
		}
	}

	//-------------------------------------------------------------------
	/**
	 * @see org.prelle.javafx.WizardPage#beforeLeaving()
	 */
	@Override
	public void beforeLeaving() {
		logger.log(Level.DEBUG, "beforeLeaving");
		logger.log(Level.DEBUG, "..instance={0}", wrapper.getWrapped());
		logger.log(Level.DEBUG, "..selected={0}", selected);
		logger.log(Level.DEBUG, "..condition1 {0}", selected!=null);
		logger.log(Level.DEBUG, "..condition2 {0}", wrapper.getWrapped().getClass()!=selected);
		if (selected!=null && (wrapper.getWrapped()==null || wrapper.getWrapped().getClass()!=selected)) {
			logger.log(Level.INFO, "Instantiate {0}  (previous {1})",selected, wrapper.getWrapped().getClass());
			G newGen = null;
			try {
				Constructor cons = selected.getConstructor(RuleSpecificCharacterObject.class, CharacterHandle.class);
				newGen = cons.newInstance(wrapper.getModel(), null);
			} catch (Exception e1) {
				logger.log(Level.ERROR, "Expected constructor "+selected.getSimpleName()+"(RulespecificCHaracterObject, CharacterHandle)");
				try {
					Constructor cons = selected.getConstructor();
					newGen = cons.newInstance();
					newGen.setModel(wrapper.getModel(), null);
				} catch (Exception e) {
					logger.log(Level.ERROR, "Problem finding constructor");
					e.printStackTrace();
					System.exit(1);
				}
			}

			newCharGenCreated(newGen, wrapper.getModel());
			try {
//				G newGen = selected.getConstructor().newInstance();
				wrapper.setWrapped(newGen);
				logger.log(Level.DEBUG, "call runProcessors on "+newGen);
				newGen.runProcessors();
			} catch (Exception e) {
				logger.log(Level.ERROR, "Failed instantiating "+selected,e);
			} finally {
				logger.log(Level.INFO, "Instantiate {0} finished",selected);
			}
		} else {
			wrapper.runProcessors();
		}

	}

	//-------------------------------------------------------------------
	/**
	 * Called when a new character has been created - allows to apply power level
	 */
	protected void newCharGenCreated(G newGen, C model) {
	}

	public ObjectProperty extraNode() { return extraNode(); }
	public Node getExtraNode() { return extraNode.get(); }
	public WizardPageGenerator setExtraNode(Node value) { extraNode.set(value); return this; }

}

class CharacterGeneratorListCell, G extends CharacterGenerator> extends ListCell> {

	private final static Logger logger = System.getLogger(CharacterGeneratorListCell.class.getPackageName());

	private static PropertyResourceBundle RES = (PropertyResourceBundle) ResourceBundle.getBundle(WizardPageGenerator.class.getPackageName()+".WizardPages");

	private Map, G> cache = new HashMap<>();
	private VBox box;
	private Label lblHeading;
	private Label lblHardcopy;
	private Function, String[]> nameGetter;

	//--------------------------------------------------------------------
	public CharacterGeneratorListCell(Function, String[]> nameGetter) {
		this.nameGetter = nameGetter;
		lblHeading = new Label();
		lblHeading.getStyleClass().add("base");
		lblHardcopy = new Label();
		box = new VBox(5);
		box.getChildren().addAll(lblHeading, lblHardcopy);
	}


	//--------------------------------------------------------------------
	/**
	 * @see javafx.scene.control.Cell#updateItem(java.lang.Object, boolean)
	 */
	@Override
	public void updateItem(Class item, boolean empty) {
		super.updateItem(item, empty);
		if (empty) {
			setGraphic(null);
		} else {
			switch (ResponsiveControlManager.getCurrentMode()) {
			case MINIMAL:
				setGraphic(lblHeading);
				break;
			default:
				setGraphic(box);
			}

			try {
				if (cache.containsKey(item)) {
					G gen = cache.get(item);
					lblHeading.setText(gen.getName());
					lblHardcopy.setText(gen.getDescription());

				} else {
					if (logger.isLoggable(Level.TRACE))
						logger.log(Level.TRACE, "Need to create instance of Generator to obtain names: "+item);
//					G gen = item.getDeclaredConstructor().newInstance();
					lblHeading.setText(nameGetter.apply(item)[0]);
//					lblHardcopy.setText(gen.getDescription());
//					cache.put(item, gen);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				logger.log(Level.ERROR, "Failed creating instance of Generator "+item,e);
				lblHeading.setText(item.getSimpleName());
			}
			lblHardcopy.setUserData(item);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy