summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LocationSelector.java
blob: 0d522e44f1efd8bba1e815e8ae2826562dea39b0 (plain) (tree)
1
2
3
4
5
6
7
8
9


                                       


                             
                         

                      
                       

                               
                                           
                          

                          
                                



                                               


                                              
                                 














                                                                               
                           



                                                           






                                                                                                       
 


                                            
                                         

           

                                                                             
           
                                           

                                                                                                                           

           



                                                                           
 


                                                                              
                               
 

                                                                                       








                                                                                     
                               



                                                      




                                      


                                        

           
                                                                      























                                                                                                    




                                                                                   
           


                                                                   
           


                                                                          
 































                                                                                               




                                                           

                                                                                


                                                         
                                                        
         
 





























                                                                                 

                                                                             

                                                          

                                                                  
                                    










                                                                                   
                                         








                                                                                          




                                         







                                                                                   

                         














































                                                                                                                                     
                 
                                       


           


                                                           
           








                                                                
                 
                                                                              




                                                                       


                                                              





                                                                        







                                                                                        
                 
                                                                
         















                                                                                                
package org.openslx.dozmod.gui.control;

import java.awt.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.Location;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.helper.GridManager;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;

/**
 * 
 * Widget to select the locations of a lecture. The UI is simply two JLists and
 * a button panel to move the elements from one list to the other. Use the
 * getters to read the values of the lists/checkbox: get
 * 
 * @author Jonathan Bauer
 * 
 */
@SuppressWarnings("serial")
public class LocationSelector extends JPanel {
	private final static Logger LOGGER = Logger
			.getLogger(LocationSelector.class);

	private JLabel lblError = new JLabel();
	private JRadioButton exclusivelyButton = new JRadioButton(
			"Veranstaltung ausschließlich in den ausgewählten Räumen anzeigen");
	private JRadioButton prioritizedButton = new JRadioButton(
			"Veranstaltung mit höherer Priorität in den ausgewählten Räumen anzeigen");
	// private JCheckBox limitToAllowedUsers = new
	// JCheckBox("Nur für die ausgewählten Benutzern anzeigen", false);

	/**
	 * Flag for the initialization state
	 */
	private boolean initDone = false;

	/**
	 * List of ID's of locations to set the selection to when we finished
	 * initializing
	 */
	private List<Integer> preselection;
	private JCheckBoxTree locationTree = new JCheckBoxTree();;
	private HashMap<Integer, DefaultMutableTreeNode> locationNodesMap = new HashMap<Integer, DefaultMutableTreeNode>();

	/**
	 * Constructor. Initializes the grid layout, the location lists and
	 * initializes the data
	 */
	public LocationSelector() {

		// build the grid
		GridManager grid = new GridManager(this, 3);
		grid.add(locationTree, 3).fill(true, true).expand(true, true);
		grid.nextRow();

		// the radio buttons, default is to show the lecture exclusively in the
		// selected locations
		exclusivelyButton.setSelected(true);
		ButtonGroup group = new ButtonGroup();
		group.add(exclusivelyButton);
		group.add(prioritizedButton);
		JPanel radioPanel = new JPanel();
		radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.PAGE_AXIS));
		radioPanel.add(exclusivelyButton);
		radioPanel.add(prioritizedButton);
		grid.add(radioPanel, 3);
		grid.nextRow();
		grid.add(Box.createVerticalGlue(), 3);
		grid.nextRow();
		grid.add(lblError, 3);
		grid.nextRow();
		grid.finish(true);

		// initialise the data
		init();
	}
	public JCheckBoxTree getTree() {
		return locationTree;
	}

	/**
	 * Async fetching of the list of the locations from the server
	 */
	public void init() {
		QuickTimer.scheduleOnce(new Task() {
			List<Location> locsList = null;

			@Override
			public void fire() {
				locsList = MetaDataCache.getLocations();
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						initDone = fillLocationsList(locsList);
						// check if preselection was set before we were done
						// initialising
						if (initDone && preselection != null) {
							setSelectionInternal(preselection);
						}
					}
				});
			}
		});
	}

	/**
	 * Sets the list of available locations. Should be called with the list of
	 * locations given by the server. This function will build the tree
	 * represented by the 'locationid'/'parentlocationid' fields of locations
	 * and set the generated tree as model for the JCheckboxTree used in the UI
	 * of this widget.
	 * 
	 * @param list
	 *            of locations to set the available list to
	 * @return true if setting the list worked, false otherwise
	 */
	public boolean fillLocationsList(final List<Location> locations) {
		if (locations == null)
			return false;

		DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(
				Config.getLastSatellite());
		DefaultTreeModel treeModel = new DefaultTreeModel(rootNode, true);
		// build map containing the node object for each location
		for (Location loc : locations) {
			if (loc == null)
				continue;
			locationNodesMap.put(loc.getLocationId(),
					new DefaultMutableTreeNode(loc, true));
		}
		// now go over nodes and insert em
		for (Integer id : locationNodesMap.keySet()) {
			Location location = MetaDataCache.getLocationById(id);
			if (location == null)
				continue;
			// determine which node this is a child of
			DefaultMutableTreeNode parentNode = null;
			if (location.getParentLocationId() == 0)
				parentNode = rootNode;
			else
				parentNode = locationNodesMap.get(location
						.getParentLocationId());
			// insert the current node in the tree model
			treeModel.insertNodeInto(
					locationNodesMap.get(location.getLocationId()), // what
					parentNode, // parent?
					parentNode.getChildCount()); // index

		}
		locationTree.setModel(treeModel);
		locationTree.updateUI();
		return true;
	}

	/**
	 * Setter for the "show only in selection" checkbox
	 * 
	 * @param true to enable the "limited" radio button, false to enable the
	 *        other
	 */
	public void setOnlyInSelection(boolean limited) {
		exclusivelyButton.setSelected(limited);
		prioritizedButton.setSelected(!limited);
	}

	/**
	 * Getter for the "show only in selection" checkbox
	 * 
	 * @return true if checked, false otherwise
	 */
	public boolean getOnlyInSelection() {
		return exclusivelyButton.isSelected();
	}

	/**
	 * Sets the selection to the locations corresponding to the given list of
	 * id's
	 * 
	 * @param list
	 *            of integers to set the locations to
	 */
	public void setSelectedLocationsAsIds(List<Integer> list) {
		if (list == null) {
			LOGGER.error("No list given!");
			return;
		}
		preselection = list;
		// if we are initialised, we need to explicitly set the selection
		if (initDone)
			setSelectionInternal(preselection);
	}

	/**
	 * Gets the selected locations as a list of id's
	 * 
	 * @return list of ids of the selection as Integer, empty list if the
	 *         selection is empty
	 */
	public List<Integer> getSelectedLocationsAsIds() {
		TreePath[] paths = locationTree.getCheckedPaths();
		if (paths == null || paths.length == 0) {
			return null;
		}
		// first try to reduce the amount of locations pushed to the server
		// by removing those which are implicitly selected:
		// all children of a node in the selection => keep only parent
		List<TreePath> treePathList = minify(paths);

		// now get the locationId of the nodes in the TreePath list
		List<Integer> idList = new ArrayList<Integer>(treePathList.size());
		// iterate over the paths
		for (TreePath path : treePathList) {
			if (path == null)
				continue;
			DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) path
					.getLastPathComponent();
			if (currentNode == null)
				continue;
			Object currentDataObject = currentNode.getUserObject();
			if (currentDataObject instanceof Location) {
				Location currentLocation = (Location) currentDataObject;
				idList.add(currentLocation.getLocationId());
			}
		}
		Collections.sort(idList);
		return idList;
	}

	private List<TreePath> minify(final TreePath[] paths) {
		// transform the array of paths to a list of leaf nodes
		List<TreePath> leavesPathsList = new ArrayList<TreePath>();
		for (TreePath path : paths) {
			DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
					.getLastPathComponent();
			if (node != null && node.isLeaf()) {
				leavesPathsList.add(path);
			}
		}
		// now get the root node of the tree and start a depth-first search for
		// leaves
		DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) locationTree
				.getModel().getRoot();
		Enumeration<?> depthEnum = rootNode.depthFirstEnumeration();
		while (depthEnum.hasMoreElements()) {
			DefaultMutableTreeNode current = (DefaultMutableTreeNode) depthEnum
					.nextElement();
			// descend til we found a leaf
			if (!current.isLeaf()) {
				continue;
			}
			// got a leaf, check the children of its parent
			DefaultMutableTreeNode parent = (DefaultMutableTreeNode) current
					.getParent();
			// this shouldnt happen but lets be safe...
			if (parent == null || parent.isLeaf())
				continue;
			// marker to see if all children are selected
			boolean allChildrenSelected = true;
			Enumeration<?> children = parent.children();
			while (children.hasMoreElements()) {
				DefaultMutableTreeNode child = (DefaultMutableTreeNode) children
						.nextElement();
				// check if this child is in our paths list
				if (!leavesPathsList.contains(getPath(child))) {
					// current child not in selection, we can stop the search of
					// this child's parent
					allChildrenSelected = false;
					break;
				}
			}
			// if all children were selected, remove them from the list
			if (allChildrenSelected) {
				LOGGER.debug("EXCLUDING FROM: " + getPath(parent));
				Enumeration<?> enumeration = parent.children();
				while (enumeration.hasMoreElements()) {
					DefaultMutableTreeNode nod = (DefaultMutableTreeNode) enumeration
							.nextElement();
					leavesPathsList.remove(getPath(nod)); // remove already
															// checks for
															// existence
				}
				// add this parent since we had only leaves in the
				// leavesPathList
				leavesPathsList.add(getPath(parent));
			}
		}
		return leavesPathsList;
	}

	/**
	 * Helper to get the TreePath of the given TreeNode
	 * @param treeNode
	 * @return
	 */
	public static TreePath getPath(TreeNode treeNode) {
		List<Object> nodes = new ArrayList<Object>();
		if (treeNode != null) {
			nodes.add(treeNode);
			treeNode = treeNode.getParent();
			while (treeNode != null) {
				nodes.add(0, treeNode);
				treeNode = treeNode.getParent();
			}
		}
		return nodes.isEmpty() ? null : new TreePath(nodes.toArray());
	}

	/**
	 * Internal helper to set the selection of the list of location
	 * corresponding the the given list of ids
	 * 
	 * @param list
	 *            of id's to set the selected locations to
	 */
	private void setSelectionInternal(List<Integer> list) {
		if (list == null || list.isEmpty()) {
			LOGGER.error("No list given for preselection!");
			return;
		}
		// get the paths in the tree of the given id list of nodes
		List<TreePath> selectedPathsList = new ArrayList<TreePath>(list.size());
		for (Integer id : list) {
			DefaultMutableTreeNode current = locationNodesMap.get(id);
			if (current == null)
				continue;
			TreePath path = new TreePath(current.getPath());
			selectedPathsList.add(path);
		}
		locationTree.setCheckedState(selectedPathsList);
	}
}

@SuppressWarnings("serial")
class LocationRenderer extends DefaultListCellRenderer {
	@Override
	public Component getListCellRendererComponent(JList<? extends Object> list,
			Object value, int index, boolean isSelected, boolean cellHasFocus) {
		if (value instanceof Location) {
			return super.getListCellRendererComponent(list,
					((Location) value).getLocationName(), index, isSelected,
					cellHasFocus);
		}
		return super.getListCellRendererComponent(list, value, index,
				isSelected, cellHasFocus);
	}
}