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

org.daisy.pipeline.gui.DetailsPane Maven / Gradle / Ivy

package org.daisy.pipeline.gui;



import java.io.File;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Labeled;
import javafx.scene.layout.VBox;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.text.Text;

import org.daisy.pipeline.gui.databridge.BoundScript;
import org.daisy.pipeline.gui.databridge.ObservableJob;
import org.daisy.pipeline.gui.databridge.ScriptFieldAnswer;
import org.daisy.pipeline.job.Job.Status;
import org.daisy.pipeline.job.JobResult;

public class DetailsPane extends VBox {

	private MainWindow main;
	private ScriptInfoHeaderVBox scriptInfoBox;
	private GridPaneHelper jobInfoGrid;
	private GridPaneHelper resultsGrid; 
	ChangeListener jobStatusListener;
	private ChangeListener currentJobChangeListener;
	private final static String paneTitle = "Job details";
	private Labeled firstLabeled;
	private String firstLabeledText;
	
	public DetailsPane(MainWindow main) {
		//super(main);
		super();
		this.main = main;
		scriptInfoBox = new ScriptInfoHeaderVBox(main);
		resultsGrid = new GridPaneHelper(main);
		jobInfoGrid = new GridPaneHelper(main);
		resultsGrid.getStyleClass().add("results");
		addCurrentJobChangeListener();
	}
	
	@Override
	public void requestFocus() {
		Node n = findFirstFocusTraversable(this);
		if (n != null)
			n.requestFocus();
	}
	
	private static Node findFirstFocusTraversable(Parent parent) {
		for (Node n : parent.getChildrenUnmodifiable()) {
			if (n.isFocusTraversable()) {
				return n;
			} else if (n instanceof Parent && (n = findFirstFocusTraversable((Parent)n)) != null) {
				return n;
			}
		}
		return null;
	}
	
	private static Labeled findFirstLabeled(Parent parent) {
		for (Node n : parent.getChildrenUnmodifiable()) {
			if (n instanceof Labeled) {
				return (Labeled)n;
			} else if (n instanceof Parent) {
				Labeled l = findFirstLabeled((Parent)n);
				if (l != null)
					return l;
			}
		}
		return null;
	}
	
	private void displayJobInfo() {
		
		ObservableJob job = this.main.getCurrentJobProperty().get();
		this.getStyleClass().add("details");
		
		Text title = new Text(paneTitle);
		title.getStyleClass().add("title");
		this.getChildren().add(title);
		
		final BoundScript boundScript = job.getBoundScript();

		scriptInfoBox.populate(boundScript.getScript());
		this.getChildren().add(scriptInfoBox);
		
    	this.getChildren().add(jobInfoGrid);
		
		Text statusLabel = new Text("Status:");
		statusLabel.getStyleClass().add("subtitle");
		Text statusValue = new Text();
		statusValue.getStyleClass().add("subtitle");
		
		// binding this causes a thread error
		statusValue.textProperty().bind(job.statusProperty());
		jobInfoGrid.addRow(statusLabel, statusValue);
		
		jobInfoGrid.addNameValuePair("ID", job.getJob().getId().toString());
		
		Text settingsLabel = new Text("Settings:");
		settingsLabel.getStyleClass().add("subtitle");
		jobInfoGrid.addRow(settingsLabel);
		
		for (ScriptFieldAnswer answer : boundScript.getInputFields()) {
			addScriptFieldAnswer(answer);
		}
		
		for (ScriptFieldAnswer answer : boundScript.getRequiredOptionFields()) {
			addScriptFieldAnswer(answer);
		}
		for (ScriptFieldAnswer answer : boundScript.getOptionalOptionFields()) {
			addScriptFieldAnswer(answer);
		}
		
		this.getChildren().add(resultsGrid);
		
		firstLabeled = findFirstLabeled(this);
		if (firstLabeled != null) {
			firstLabeledText = firstLabeled.getAccessibleText();
			if (firstLabeledText == null) firstLabeledText = firstLabeled.getText();
		}
		
		refreshLinks();
	}
	
	private void refreshLinks() {
		ObservableJob job = this.main.getCurrentJobProperty().get();
		resultsGrid.clearControls();
		
		Status status = job.getJob().getStatus();
		if (status == Status.SUCCESS || status == Status.ERROR || status == Status.FAIL) {
			Text resultsLabel = new Text("Results");
			resultsLabel.getStyleClass().add("subtitle");
	    	resultsGrid.addRow(resultsLabel);
	    	
	    	resultsGrid.addFinderLinkRow("Log file", job.getJob().getContext().getLogFile().toString());
	    	
	    	Iterable results = job.getJob().getContext().getResults().getResults();
	    	for (JobResult result : results) {
	    		File f = new File(result.getPath());
	    		resultsGrid.addFinderLinkRow(f.getName(), result.getPath().toString());
	    	}   	
	    }
		
		// Hack: Prepend the pane title and the job description to the accessible text of the first
		// focusable node so that it is spoken when switching between panes with F6. This node
		// should normally be the "read online documentation" link.
		if (firstLabeled != null)
			firstLabeled.setAccessibleText(paneTitle + " - Job " + job.toString() + " - " + firstLabeledText);
	}
	
	// listen for when the currently selected job changes
	private void addCurrentJobChangeListener() {
    	currentJobChangeListener = new ChangeListener() {

			public void changed(
					ObservableValue observable,
					ObservableJob oldValue, ObservableJob newValue) {
				setJobStatusListeners();
				clearControls();
				if (newValue != null) {
					displayJobInfo();
				}
			}
    	};
    	main.getCurrentJobProperty().addListener(currentJobChangeListener);
    }
	
	private void setJobStatusListeners() {
		ObservableJob job = this.main.getCurrentJobProperty().get();
		// unhook the old listener
		if (job != null && jobStatusListener != null) {
			job.statusProperty().removeListener(jobStatusListener);
		}
		
		if (job != null) {
			// hook up a new one
			// other job changes (status, messages) are kept up-to-date by having the widget
			// directly observe the relevant property, but in this case we need to observe from the outside
			// and add widgets accordingly
			final DetailsPane thiz = this;
			jobStatusListener = new ChangeListener() {
				public void changed(ObservableValue observable,
						String oldValue, String newValue) {
					
					// need this to avoid "you're on the wrong thread" errors
					Platform.runLater(new Runnable(){
						public void run() {
							thiz.refreshLinks();
						}
					});
				}
			};
			
			job.statusProperty().addListener(jobStatusListener);
		}
	}
	
	private void addScriptFieldAnswer(ScriptFieldAnswer answer) {
		if (answer instanceof ScriptFieldAnswer.ScriptFieldAnswerBoolean) {
			ScriptFieldAnswer.ScriptFieldAnswerBoolean answer_ = (ScriptFieldAnswer.ScriptFieldAnswerBoolean)answer;
			jobInfoGrid.addNameValuePair(answer.getField().getNiceName(), answer_.answerAsString());
		}
		else if (answer instanceof ScriptFieldAnswer.ScriptFieldAnswerString) {
			ScriptFieldAnswer.ScriptFieldAnswerString answer_ = (ScriptFieldAnswer.ScriptFieldAnswerString)answer;
			String value = answer_.answerProperty().get();
			if (value.isEmpty()) {
				value = "Not specified";
			}
			jobInfoGrid.addNameValuePair(answer.getField().getNiceName(), value);
		}
		else if (answer instanceof ScriptFieldAnswer.ScriptFieldAnswerList) {
			ScriptFieldAnswer.ScriptFieldAnswerList answer_ = (ScriptFieldAnswer.ScriptFieldAnswerList)answer;
			int sz = answer_.answerProperty().size();
			if (sz > 0) {
				// add the first one along with the field name
				String value = answer_.answerProperty().get(0);
				if (value.isEmpty()) {
					value = "Not specified";
				}
				jobInfoGrid.addNameValuePair(answer.getField().getNiceName(), value);
				if (sz > 1) {
					// add the rest with blanks in the field column
					for (int i = 1; i 0) {
			getChildren().remove(0, sz); // removes all controls from 0 to sz
		}
		
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy