summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/StartupConfigurator.java
blob: 7013e437bc9904215de221c5e1b00112fc4a68e7 (plain) (tree)
1
2
3
4
5
6
7
8
                                            
 
                      

                                    
                       

                                     


                              
                           
                         



                                                   
 
                       
                               
                                        


                             


                               

                                        
 




                                                      
                                                                
                                               
                                                                

                                                    
                                                 





                                                     
                             


                                                                       
                                 
   
                                                                    
        
                                                                           
        
                                                                            
 







                                              
                                      
                        























































































                                                                                                                                       

         


                                                 
           

                                                                                   
           
                                                                               
                          
           
                                           
                                                                                         


                                                                                                           

                                                                           
                                                                                                     
                                                          
                                                                                  
                 
                                                                  

                                                                                                                
                                                                                          
                                                                               
                                                                                
 
                             
                                       






                                                                                    
                         

                                                               

















                                                                                                                                               

                                                               
                               
                 
                                     
                                                                                             
                                                   


                                         
                                     

                                                                                                             
                                                                 
                                                                                







                                                                                          
                                                                                                 






                                                                                             
                                                                                       



                                                                         

                                 

                                                
                                                                                           
                                                                                



                                                                                        
                                                                       

         

                                                                           
                                                                       
                                                                          
                                                                 
                                         













                                         
                                                  
           




                                                                   
 
                                                     
                                
                                               



                                                                                              
                                                        



                                            
                                                       




                                          
                                                
                                                               
                                                                          









                                                        
                                                           

                                         
                                      



                                                              
                                                                      


                                 











                                                                                  
         
 





                                                                             
                                                


                                                                         

                                                                                                         
                                        
                                              
                                                                 
                                                                             
                                                           
                                                     
 
                                            
                                                                                          














                                                                                                  
                                                                       
                               
                










                                                                                      
                                                   
                                                                                   
                                                      
                                                            
                                                     
                               
                




                                                                                                                        
                                                        







                                                                                                                             






                                                                         

                                                                              
                



                                                                                      






                                                                            
                                   









                                                                  
 







                                                              

         























                                                                                              
package org.openslx.dozmod.gui.configurator;

import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;

import javax.swing.Box;
import javax.swing.ButtonModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;

import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.ImageDetailsRead;
import org.openslx.bwlp.thrift.iface.LectureRead;
import org.openslx.bwlp.thrift.iface.PresetRunScript;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor;
import org.openslx.dozmod.gui.control.ComboBox;
import org.openslx.dozmod.gui.control.ComboBox.ComboBoxRenderer;
import org.openslx.dozmod.gui.control.QLabel;
import org.openslx.dozmod.gui.control.WordWrapLabel;
import org.openslx.dozmod.gui.helper.GridManager;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.thrift.Session;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;
import org.openslx.util.Util;

/**
 * Widget for advanced configuration options for lectures. This handles
 * Runscript and sound (un)muting
 */
public class StartupConfigurator extends StartupConfiguratorLayout {
	
	private static final long serialVersionUID = -3497629601818983994L;
	
	private StartupSettings startupSettings = new StartupSettings(null);

	private ImageDetailsRead image;
	
	private List<PresetRunScript> scripts;

	private LectureRead lecture;
	
	private ChangeListener mightyListener;

	public StartupConfigurator() {
		super();
		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				scripts = MetaDataCache.getPredefinedRunScripts();
				if (scripts.isEmpty()) {
					Gui.asyncExec(new Runnable() {
						@Override
						public void run() {
							btnPredefinedScripts.setVisible(false);
						}
					});
				}
			}
		});
		btnPredefinedScripts.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				showRunscriptSelector();
			}
		});
	}
	
	private void showRunscriptSelector() {
		if (scripts == null) {
			Gui.showMessageBox("Wah wah wah! Null scripts", MessageType.ERROR, null, null);
			return;
		}
		final JDialog dialog = new JDialog(SwingUtilities.getWindowAncestor(this),
				"Startscripte auswählen", ModalityType.APPLICATION_MODAL);
		JPanel pane = new JPanel();
		dialog.setContentPane(pane);
		dialog.setMinimumSize(Gui.getScaledDimension(200, 300));
		GridManager grid = new GridManager(pane, 2, true, new Insets(2, 2, 2, 2));
		final Map<ButtonModel, Integer> mapper = new HashMap<>();
		boolean haveDisabled = false;
		for (PresetRunScript ruleSet : scripts) {
			JCheckBox button = new JCheckBox(ruleSet.displayname);
			grid.add(button, 2);
			grid.nextRow();
			mapper.put(button.getModel(), ruleSet.scriptId);
			if (image != null && ruleSet.osIds != null
					&& !ruleSet.osIds.contains(image.osId)) {
				button.setEnabled(false);
				haveDisabled = true;
			}
			if (lecture != null && lecture.presetScriptIds != null && lecture.presetScriptIds.contains(ruleSet.scriptId)) {
				button.setSelected(true);
			}
		}
		if (haveDisabled) {
			grid.add(new WordWrapLabel("Ausgegraute Elemente sind mit dem zur"
					+ " Veranstaltung gehörendem Betriebssystem nicht kompatibel"), 2);
			grid.nextRow();
		}
		JButton btnCancel = new JButton("Abbrechen");
		JButton btnOk = new JButton("SPASCHAN");
		grid.add(btnCancel).anchor(GridBagConstraints.LINE_START);
		grid.add(btnOk).anchor(GridBagConstraints.LINE_END);
		grid.finish(true);
		btnCancel.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				dialog.dispose();
			}
		});
		final AtomicReference<List<Integer>> selectedScripts = new AtomicReference<>();
		btnOk.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				List<Integer> selected = new ArrayList<>();
				for (Entry<ButtonModel, Integer> button : mapper.entrySet()) {
					if (button.getKey().isSelected()) {
						selected.add(button.getValue());
					}
				}
				selectedScripts.set(selected);
				dialog.dispose();
			}
		});
		dialog.pack();
		Gui.centerShellOverShell(SwingUtilities.getWindowAncestor(this), dialog);
		dialog.setVisible(true);
		// Call blocks as it's a modal window
		if (selectedScripts.get() != null) {
			// User clicked OK
			startupSettings.selectedScripts = selectedScripts.get();
			mightyListener.stateChanged(null);
		}
	}

	private void setError(final String msg) {
		lblError.setText(msg);
	}
	/**
	 * Gets the runscript as String. The chosen interpreter and visibility flag
	 * will get encoded in the first line of the script.
	 * 
	 * @return runscript as String. If no text was entered, returns a empty
	 *         string.
	 */
	public StartupSettings getState() {
		setError(""); // fill remove any prior errors, we'll reset them if needed
		// handle user input, this is tricky since
		// * either an item has been selected -> editorContent will be of our enum type
		// * user typed its own interpreter into the box -> editorContent will be a castable String
		Object cboContent = cboRunscriptType.getEditor().getItem();
		if (cboContent instanceof RunscriptType) {
			startupSettings.put(Field.EXTENSION, ((RunscriptType) cboContent).extension);
		} else if (cboContent instanceof String) {
			startupSettings.put(Field.EXTENSION, (String) cboContent);
		}
		startupSettings.runScript = taRunScript.getText();

		RunscriptVisibility visibility = (RunscriptVisibility) cboRunscriptVisibility.getSelectedItem();
		startupSettings.put(Field.VISIBILITY, Integer.toString(visibility.value));
		SoundState sound = (SoundState)cboSoundState.getSelectedItem();
		startupSettings.put(Field.MUTED, Integer.toString(sound.value));

		setError("");
		return startupSettings;
	}

	/**
	 * Sets the state of this widget to the given AdvancedConfiguration. Basicly
	 * this sets the content of the text areas to the corresponding network
	 * rules/runscript as given by the AdvancedConfiguration object
	 * 
	 * @param lecture
	 *            AdvancedConfiguration to set the state to
	 */
	public void setState(final LectureRead lecture) {
		if (lecture == null)
			return;
		this.lecture = lecture;
		setCustomScript(lecture.runscript);
		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				try {
					image = ThriftManager.getSatClient().getImageDetails(Session.getSatelliteToken(), lecture.imageBaseId);
				} catch (Exception e) {
				}
			}
		});
	}
	
	private void setCustomScript(final String lecture) {
		if (lecture == null || lecture.isEmpty()) {
			cboRunscriptType.setSelectedItem(null);
			taRunScript.setText("");
			return;
		}
		String header = null;
		try (BufferedReader reader = new BufferedReader(new StringReader(lecture))) {
			header = reader.readLine();
		} catch (IOException e) {
			// swallow ...
		}
		if (header != null) {
			// we should have following format: ext=<interpreter/extension>;visibility=<flag>;...
			// e.g. ext=sh;visibility=0
			startupSettings.deserializeItems(header);
			String extension = startupSettings.get(Field.EXTENSION);
			for (RunscriptType type : RunscriptType.values()) {
				if (type.extension.equals(extension)) {
					cboRunscriptType.setSelectedItem(type);
					// mark that we found it by nulling the shebang...
					extension = null;
					break;
				}
			}
			int visibility = Util.parseInt(startupSettings.get(Field.VISIBILITY), 1);
			for (RunscriptVisibility windowFlag : RunscriptVisibility.values()) {
				if (windowFlag.value == visibility) {
					cboRunscriptVisibility.setSelectedItem(windowFlag);
					break;
				}
			}
			cboSoundState.setSelectedItem(SoundState.DEFAULT);
			int mute = Util.parseInt(startupSettings.get(Field.MUTED), -1);
			for (SoundState s : SoundState.values()) {
				if (s.value == mute) {
					cboSoundState.setSelectedItem(s);
					break;
				}
			}

			if (extension != null) {
				// user specific shebang, so just write the text to the cbo
				cboRunscriptType.getEditor().setItem(extension);
			}
		}
		// finished with the interpreter, remove that line from the given config
		// before setting that text
		taRunScript.setText(lecture.replaceFirst(".*?\n", ""));
	}

	public void addToChangeMonitor(DialogChangeMonitor changeMonitor) {
		changeMonitor.add(taRunScript);
		changeMonitor.addEditableCombo(cboRunscriptType, null);
		changeMonitor.addFixedCombo(cboRunscriptVisibility, null);
		changeMonitor.addFixedCombo(cboSoundState, null);
		// TODO: Preset runscript
	}

	private static enum Field {
		EXTENSION("ext"),
		VISIBILITY("visibility"),
		MUTED("soundMuted");
		public final String id;

		Field(String id) {
			this.id = id;
		}
	}

	/**
	 * Object holding all the startup settings
	 */
	public static class StartupSettings {
		
		public Map<String, String> items = new HashMap<>();
		public String runScript;
		public List<Integer> selectedScripts;

		public StartupSettings(String data) {
			super();
			deserializeItems(data);
		}

		public String put(Field key, String value) {
			value = value.replace(';', '_').replace('\r', '_').replace('\n', '_');
			return items.put(key.id, value);
		}
		
		public String get(Field key)
		{
			String ret = items.get(key.id);
			if (ret == null)
				return "";
			return ret;
		}

		public String serializeItems() {
			StringBuilder sb = new StringBuilder();
			for (Entry<String, String> e : items.entrySet()) {
				if (sb.length() != 0) {
					sb.append(';');
				}
				sb.append(e.getKey());
				sb.append('=');
				sb.append(e.getValue());
			}
			return sb.toString();
		}

		public void deserializeItems(String data) {
			if (data == null)
				return;
			items.clear();
			String[] parts = data.split(";");
			for (String s : parts) {
				String[] entry = s.split("=");
				if (entry.length == 2) {
					items.put(entry[0], entry[1]);
				}
			}
		}
		
		@Override
		public boolean equals(Object obj) {
			if (!(obj instanceof StartupSettings))
				return false;
			StartupSettings other = (StartupSettings) obj;
			if (this.selectedScripts == other.selectedScripts)
				return true;
			if (this.selectedScripts == null)
				return false;
			return this.selectedScripts.equals(other.selectedScripts);
		}
	}

}

/**
 * Internal layout class for the advanced configurator (to keep it clean even
 * for widgets)
 */
class StartupConfiguratorLayout extends JPanel {

	private static final long serialVersionUID = 648729071828404053L;

	private final static String RUN_SCRIPT_HELP = "Ein hier eingetragenes Skript wird nach dem Start"
			+ " der VM automatisch ausgeführt.";
	protected final QLabel lblError;
	protected final JTextArea taRunScript;
	protected final ComboBox<RunscriptType> cboRunscriptType;
	protected final ComboBox<RunscriptVisibility> cboRunscriptVisibility;
	protected final ComboBox<SoundState> cboSoundState;
	protected final JButton btnPredefinedScripts;

	public StartupConfiguratorLayout() {
		GridManager grid = new GridManager(this, 2, true, new Insets(5, 5, 5, 5));
		grid.add(new QLabel("Audio"));
		cboSoundState = new ComboBox<>(new ComboBoxRenderer<SoundState>() {
			@Override
			public String renderItem(SoundState item) {
				return item.displayName;
			}
		});
		cboSoundState.setModel(new DefaultComboBoxModel<SoundState>(SoundState.values()));
		grid.add(cboSoundState).fill(true, false).expand(true, false);
		grid.nextRow();
		
		grid.add(Box.createVerticalStrut(4), 2);
		grid.nextRow();
		
		grid.add(new WordWrapLabel(RUN_SCRIPT_HELP, false, true), 2)
				.fill(true, false).expand(true, false);
		grid.nextRow();
		
		cboRunscriptType = new ComboBox<RunscriptType>(
				new ComboBoxRenderer<RunscriptType>() {
					@Override
					public String renderItem(RunscriptType item) {
						if (item == null)
							return null;
						return item.toString();
					}
				});
		cboRunscriptType.setModel(new DefaultComboBoxModel<RunscriptType>(
				RunscriptType.values()));
		cboRunscriptType.setEditable(true);
		grid.add(new QLabel("Dateinamenserweiterung: ")).fill(false, false)
				.expand(false, false);
		grid.add(cboRunscriptType).fill(true, false)
				.expand(true, false);
		grid.nextRow();
		
		cboRunscriptVisibility = new ComboBox<RunscriptVisibility>(new ComboBoxRenderer<RunscriptVisibility>() {
			@Override
			public String renderItem(RunscriptVisibility item) {
				if (item == null)
					return null;
				return item.displayName;
			}
		});
		cboRunscriptVisibility.setModel(new DefaultComboBoxModel<RunscriptVisibility>(RunscriptVisibility.values()));
		grid.add(new QLabel("Sichtbarkeit: ")).fill(false, false)
				.expand(false, false);
		grid.add(cboRunscriptVisibility).fill(true, false)
				.expand(true, false);
		grid.nextRow();
		
		taRunScript = new JTextArea("", 5, 20);
		JScrollPane scpRunScript = new JScrollPane(taRunScript,
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		taRunScript.setLineWrap(true);
		taRunScript.setWrapStyleWord(true);
		grid.add(scpRunScript, 2).fill(true, true).expand(true, true);
		grid.nextRow();
		
		btnPredefinedScripts = new JButton("Vordefinierte Scripte...");
		grid.add(btnPredefinedScripts, 2).anchor(GridBagConstraints.LINE_END);
		grid.nextRow();
		
		lblError = new QLabel("");
		lblError.setForeground(Color.RED);
		JPanel pnlError = new JPanel();
		pnlError.add(Box.createGlue());
		pnlError.add(lblError);
		pnlError.add(Box.createGlue());
		grid.add(pnlError, 2).fill(true, false).expand(true, false);
		grid.finish(false);
	}
	
}


enum RunscriptType {
	SHELL("Shellskript", "sh"), BATCH("Windows-Batch", "bat");

	public final String displayName;
	public final String extension;

	private RunscriptType(String name, String extension) {
		this.displayName = name;
		this.extension = extension;
	}

	@Override
	public String toString() {
		return extension + " (" + displayName + ")";
	}
}

enum RunscriptVisibility {
	NORMAL("Normal", 1), MINIMIZED("Minimiert", 2), HIDDEN("Versteckt", 0);

	public final String displayName;
	public final int value;

	private RunscriptVisibility(String name, int flag) {
		this.displayName = name;
		this.value = flag;
	}
}

enum SoundState {
	DEFAULT("Vorgabe des Pools", -1), MUTED("Stummschalten", 1), UNMUTED("Aktivieren", 0);
	
	public final String displayName;
	public final int value;
	
	private SoundState(String name, int value) {
		this.displayName = name;
		this.value = value;
	}
}