summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LocationSelector.java
blob: 863028f0861931ef59902cc6bf2875778d33fe25 (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.ButtonGroup;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
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.gui.Gui;
import org.openslx.dozmod.gui.changemonitor.AbstractControlWrapper;
import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor;
import org.openslx.dozmod.gui.control.JCheckBoxTree.CheckChangeEventListener;
import org.openslx.dozmod.gui.helper.GridManager;
import org.openslx.dozmod.thrift.Session;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.thrifthelper.Comparators;
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 final JRadioButton btnLimitToLocations;
	private final JRadioButton btnPrioritizeInLocations;
	private final ButtonGroup grpLocationExclusive;
	/**
	 * Flag for the initialization state
	 */
	private boolean initDone = false;

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

	private AbstractControlWrapper<?> treeChangeHandler = null;

	/**
	 * Constructor. Initializes the grid layout, the location lists and
	 * initializes the data
	 */
	public LocationSelector() {
		btnLimitToLocations = new JRadioButton(
				"Veranstaltung ausschließlich in den ausgewählten Räumen anzeigen");
		btnPrioritizeInLocations = new JRadioButton(
				"Veranstaltung mit höherer Priorität in den ausgewählten Räumen anzeigen");
		btnPrioritizeInLocations.setSelected(true);

		grpLocationExclusive = new ButtonGroup();
		grpLocationExclusive.add(btnLimitToLocations);
		grpLocationExclusive.add(btnPrioritizeInLocations);

		// build the grid
		GridManager grid = new GridManager(this, 1);
		grid.add(new JScrollPane(locationTree)).fill(true, true).expand(true, true);
		grid.nextRow();
		grid.add(btnLimitToLocations);
		grid.nextRow();
		grid.add(btnPrioritizeInLocations);
		grid.nextRow();
		grid.add(Box.createVerticalGlue());
		grid.nextRow();
		grid.finish(false);

		// initialise the data
		init();
	}

	/**
	 * Async fetching of the list of the locations from the server
	 */
	private void init() {
		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				final List<Location> locsList = MetaDataCache.getLocations();
				if (locsList == null)
					return;
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						fillLocationsList(locsList);
						initDone = true;
						// check if preselection was set before we were done
						// initialising
						if (preselection != null) {
							setSelectionInternal(preselection);
							preselection = null;
							if (treeChangeHandler != null) {
								treeChangeHandler.reset();
							}
						}
					}
				});
			}
		});
	}

	/**
	 * 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 locations list of locations to set the available list to
	 */
	private void fillLocationsList(final List<Location> locations) {
		DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(Session.getSatelliteAddress());
		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());
			// determine the right index to insert the child into
			int childrenCount = parentNode.getChildCount();
			int insertionIndex = childrenCount;
			if (childrenCount != 0) {
				// loop through kids
				Enumeration<?> enumeration = parentNode.children();
				while (enumeration.hasMoreElements()) {
					DefaultMutableTreeNode currentChild = (DefaultMutableTreeNode) enumeration.nextElement();
					if (currentChild == null)
						continue;
					Location childLocation = (Location) currentChild.getUserObject();
					if (childLocation == null)
						continue;
					if (Comparators.location.compare(location, childLocation) <= 0) {
						insertionIndex = parentNode.getIndex(currentChild);
						break;
					}
				}
			}
			// insert the current node in the tree model
			treeModel.insertNodeInto(locationNodesMap.get(location.getLocationId()), // what
					parentNode, // parent?
					insertionIndex); // index

		}
		locationTree.setModel(treeModel);
		locationTree.updateUI();
		locationTree.repaint();
	}

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

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

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

	/**
	 * 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 new ArrayList<Integer>();
		}
		// 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
		// 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);
			}
		}

		List<TreePath> currentList = leavesPathsList;
		List<TreePath> tmp;
		do {
			tmp = currentList;
			currentList = minify(currentList);
		} while (!tmp.equals(currentList));

		// now get the locationId of the nodes in the TreePath list
		List<Integer> idList = new ArrayList<Integer>(currentList.size());
		// iterate over the paths
		for (TreePath path : currentList) {
			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;
	}

	/**
	 * Minimize the given set of TreePath: if all children of an inner node are
	 * selected, remove all child from the result list and just keep the inner
	 * node
	 * 
	 * @param leavesPathsList as a List of TreePath to minimize
	 * @return Resulting minimal list of TreePaths
	 */
	private List<TreePath> minify(final List<TreePath> leavesPathsList) {
		List<TreePath> resultList = new ArrayList<TreePath>();
		for (TreePath leaf : leavesPathsList) {
			DefaultMutableTreeNode leafNode = (DefaultMutableTreeNode) leaf.getLastPathComponent();
			if (leafNode.getParent() == null)
				continue;
			if (leafNode.getLevel() == 1) {
				resultList.add(getPath(leafNode));
				continue;
			}
			Enumeration<?> leafSiblings = leafNode.getParent().children();
			List<TreePath> selectedSiblings = new ArrayList<TreePath>();
			while (leafSiblings.hasMoreElements()) {
				DefaultMutableTreeNode leafSibling = (DefaultMutableTreeNode) leafSiblings.nextElement();
				if (leavesPathsList.contains(getPath(leafSibling))) {
					selectedSiblings.add(getPath(leafSibling));
				}
			}
			if (selectedSiblings.size() == leafNode.getParent().getChildCount()) {
				// all selected, keep parent
				if (!resultList.contains(getPath(leafNode.getParent())))
					resultList.add(getPath(leafNode.getParent()));
			} else {
				for (TreePath sibling : selectedSiblings) {
					if (!resultList.contains(sibling))
						resultList.add(sibling);
				}
			}
		}
		return resultList;
	}

	/**
	 * Helper to get the TreePath of the given TreeNode
	 * 
	 * @param treeNode node to get the TreePath of
	 * @return TreePath of the given TreeNode if it can be determined, null
	 *         otherwise
	 */
	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 ids to set the selected locations to
	 */
	private void setSelectionInternal(List<Integer> list) {
		if (list == null || list.isEmpty()) {
			return;
		}
		// first, deselect everything
		List<TreePath> selectedPathsList = new ArrayList<>(Math.max(MetaDataCache.getLocations().size(),
				list.size()));
		for (Location location : MetaDataCache.getLocations()) {
			DefaultMutableTreeNode current = locationNodesMap.get(location.locationId);
			if (current == null)
				continue;
			TreePath path = new TreePath(current.getPath());
			selectedPathsList.add(path);
		}
		locationTree.setCheckedState(selectedPathsList, false);
		// get the paths in the tree of the given id list of nodes
		selectedPathsList.clear();
		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, true);
		locationTree.repaint();
	}

	public void addToChangeMonitor(DialogChangeMonitor changeMonitor) {
		treeChangeHandler = changeMonitor.add(locationTree);
		changeMonitor.add(grpLocationExclusive);
	}

	public void addCheckChangeEventListener(CheckChangeEventListener checkChangeEventListener) {
		locationTree.addCheckChangeEventListener(checkChangeEventListener);
	}
}

@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);
	}
}