diff options
author | Jonathan Bauer | 2016-01-21 18:30:16 +0100 |
---|---|---|
committer | Jonathan Bauer | 2016-01-21 18:30:16 +0100 |
commit | 7e6f4a7c094ac014d1c63b163c57d62e800dfe53 (patch) | |
tree | a6a79e170dda51e9f2cbf0fbf1b1ad6cd7f5b55b /dozentenmodul/src/main/java/org/openslx/dozmod | |
parent | [server] Add section entry to xml (diff) | |
download | tutor-module-7e6f4a7c094ac014d1c63b163c57d62e800dfe53.tar.gz tutor-module-7e6f4a7c094ac014d1c63b163c57d62e800dfe53.tar.xz tutor-module-7e6f4a7c094ac014d1c63b163c57d62e800dfe53.zip |
[client] Changed location selector from lists to tree selection
Diffstat (limited to 'dozentenmodul/src/main/java/org/openslx/dozmod')
7 files changed, 558 insertions, 286 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/JCheckBoxTree.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/JCheckBoxTree.java new file mode 100644 index 00000000..0936e584 --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/JCheckBoxTree.java @@ -0,0 +1,294 @@ +package org.openslx.dozmod.gui.control; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.EventListener; +import java.util.EventObject; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import javax.swing.JCheckBox; +import javax.swing.JPanel; +import javax.swing.JTree; +import javax.swing.event.EventListenerList; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeSelectionModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import org.openslx.bwlp.thrift.iface.Location; +import org.openslx.dozmod.util.FormatHelper; + +/** + * Credits to 'SomethingSomething': http://stackoverflow.com/a/21851201 + * With minor changes: + * TODO + */ +public class JCheckBoxTree extends JTree { + + private static final long serialVersionUID = -4194122328392241790L; + + JCheckBoxTree selfPointer = this; + + // Defining data structure that will enable to fast check-indicate the state + // of each node + // It totally replaces the "selection" mechanism of the JTree + private class CheckedNode { + boolean isSelected; + boolean hasChildren; + boolean allChildrenSelected; + + public CheckedNode(boolean isSelected_, boolean hasChildren_, + boolean allChildrenSelected_) { + isSelected = isSelected_; + hasChildren = hasChildren_; + allChildrenSelected = allChildrenSelected_; + } + } + + HashMap<TreePath, CheckedNode> nodesCheckingState; + HashSet<TreePath> checkedPaths = new HashSet<TreePath>(); + + // Defining a new event type for the checking mechanism and preparing + // event-handling mechanism + protected EventListenerList listenerList = new EventListenerList(); + + public class CheckChangeEvent extends EventObject { + private static final long serialVersionUID = -8100230309044193368L; + + public CheckChangeEvent(Object source) { + super(source); + } + } + + public interface CheckChangeEventListener extends EventListener { + public void checkStateChanged(CheckChangeEvent event); + } + + public void addCheckChangeEventListener(CheckChangeEventListener listener) { + listenerList.add(CheckChangeEventListener.class, listener); + } + + public void removeCheckChangeEventListener(CheckChangeEventListener listener) { + listenerList.remove(CheckChangeEventListener.class, listener); + } + + void fireCheckChangeEvent(CheckChangeEvent evt) { + Object[] listeners = listenerList.getListenerList(); + for (int i = 0; i < listeners.length; i++) { + if (listeners[i] == CheckChangeEventListener.class) { + ((CheckChangeEventListener) listeners[i + 1]) + .checkStateChanged(evt); + } + } + } + + // Override + public void setModel(TreeModel newModel) { + super.setModel(newModel); + resetCheckingState(); + // expand all nodes + for (int i = 0; i < this.getRowCount(); i++) { + this.expandRow(i); + } + } + // Preselection stuff + public void setCheckedState(List<TreePath> paths) { + if (paths == null) + return; + for (TreePath path : paths) { + if (path != null) + checkSubTree(path, true); + } + } + + // New method that returns only the checked paths (totally ignores original + // "selection" mechanism) + public TreePath[] getCheckedPaths() { + return checkedPaths.toArray(new TreePath[checkedPaths.size()]); + } + + // Returns true in case that the node is selected, has children but not all + // of them are selected + public boolean isSelectedPartially(TreePath path) { + CheckedNode cn = nodesCheckingState.get(path); + return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected; + } + + private void resetCheckingState() { + nodesCheckingState = new HashMap<TreePath, CheckedNode>(); + checkedPaths = new HashSet<TreePath>(); + DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel() + .getRoot(); + if (node == null) { + return; + } + addSubtreeToCheckingStateTracking(node); + } + + // Creating data structure of the current model for the checking mechanism + private void addSubtreeToCheckingStateTracking(DefaultMutableTreeNode node) { + TreeNode[] path = node.getPath(); + TreePath tp = new TreePath(path); + CheckedNode cn = new CheckedNode(false, node.getChildCount() > 0, false); + nodesCheckingState.put(tp, cn); + for (int i = 0; i < node.getChildCount(); i++) { + addSubtreeToCheckingStateTracking((DefaultMutableTreeNode) tp + .pathByAddingChild(node.getChildAt(i)) + .getLastPathComponent()); + } + } + + // Overriding cell renderer by a class that ignores the original "selection" + // mechanism + // It decides how to show the nodes due to the checking-mechanism + private class CheckBoxCellRenderer extends JPanel implements + TreeCellRenderer { + private static final long serialVersionUID = -7341833835878991719L; + JCheckBox checkBox; + + public CheckBoxCellRenderer() { + super(); + this.setLayout(new BorderLayout()); + checkBox = new JCheckBox(); + add(checkBox, BorderLayout.CENTER); + setOpaque(false); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; + Object obj = node.getUserObject(); + TreePath tp = new TreePath(node.getPath()); + CheckedNode cn = nodesCheckingState.get(tp); + if (cn == null) { + return this; + } + // HACK ... + String textString; + if (obj instanceof Location) + textString = FormatHelper.locName((Location)obj); + else + textString = obj.toString(); + checkBox.setSelected(cn.isSelected); + checkBox.setText(textString); + checkBox.setOpaque(cn.isSelected && cn.hasChildren + && !cn.allChildrenSelected); + return this; + } + } + + public JCheckBoxTree() { + super(); + // Disabling toggling by double-click + this.setToggleClickCount(0); + // Overriding cell renderer by new one defined above + CheckBoxCellRenderer cellRenderer = new CheckBoxCellRenderer(); + this.setCellRenderer(cellRenderer); + // Overriding selection model by an empty one + DefaultTreeSelectionModel dtsm = new DefaultTreeSelectionModel() { + private static final long serialVersionUID = -8190634240451667286L; + + // Totally disabling the selection mechanism + public void setSelectionPath(TreePath path) { + } + + public void addSelectionPath(TreePath path) { + } + + public void removeSelectionPath(TreePath path) { + } + + public void setSelectionPaths(TreePath[] pPaths) { + } + }; + // Calling checking mechanism on mouse click + this.addMouseListener(new MouseListener() { + public void mouseClicked(MouseEvent arg0) { + TreePath tp = selfPointer.getPathForLocation(arg0.getX(), + arg0.getY()); + if (tp == null) { + return; + } + boolean checkMode = !nodesCheckingState.get(tp).isSelected; + checkSubTree(tp, checkMode); + updatePredecessorsWithCheckMode(tp, checkMode); + // Firing the check change event + fireCheckChangeEvent(new CheckChangeEvent(new Object())); + // Repainting tree after the data structures were updated + selfPointer.repaint(); + } + + public void mouseEntered(MouseEvent arg0) { + } + + public void mouseExited(MouseEvent arg0) { + } + + public void mousePressed(MouseEvent arg0) { + } + + public void mouseReleased(MouseEvent arg0) { + } + }); + this.setSelectionModel(dtsm); + } + + // When a node is checked/unchecked, updating the states of the predecessors + protected void updatePredecessorsWithCheckMode(TreePath tp, boolean check) { + TreePath parentPath = tp.getParentPath(); + // If it is the root, stop the recursive calls and return + if (parentPath == null) { + return; + } + CheckedNode parentCheckedNode = nodesCheckingState.get(parentPath); + DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parentPath + .getLastPathComponent(); + parentCheckedNode.allChildrenSelected = true; + parentCheckedNode.isSelected = false; + for (int i = 0; i < parentNode.getChildCount(); i++) { + TreePath childPath = parentPath.pathByAddingChild(parentNode + .getChildAt(i)); + CheckedNode childCheckedNode = nodesCheckingState.get(childPath); + // It is enough that even one subtree is not fully selected + // to determine that the parent is not fully selected + if (!childCheckedNode.allChildrenSelected) { + parentCheckedNode.allChildrenSelected = false; + } + } + if (parentCheckedNode.allChildrenSelected) { + parentCheckedNode.isSelected = true; + } + if (parentCheckedNode.isSelected) { + checkedPaths.add(parentPath); + } else { + checkedPaths.remove(parentPath); + } + // Go to upper predecessor + updatePredecessorsWithCheckMode(parentPath, check); + } + + // Recursively checks/unchecks a subtree + protected void checkSubTree(TreePath tp, boolean check) { + CheckedNode cn = nodesCheckingState.get(tp); + cn.isSelected = check; + DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp + .getLastPathComponent(); + for (int i = 0; i < node.getChildCount(); i++) { + checkSubTree(tp.pathByAddingChild(node.getChildAt(i)), check); + } + cn.allChildrenSelected = check; + if (check) { + checkedPaths.add(tp); + } else { + checkedPaths.remove(tp); + } + } +}
\ No newline at end of file diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LocationSelector.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LocationSelector.java index cf2553e1..0d522e44 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LocationSelector.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LocationSelector.java @@ -1,31 +1,30 @@ package org.openslx.dozmod.gui.control; import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; 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.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; +import javax.swing.JLabel; 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.Config; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.helper.GridManager; -import org.openslx.dozmod.thrift.Sorters; import org.openslx.dozmod.thrift.cache.MetaDataCache; import org.openslx.util.QuickTimer; import org.openslx.util.QuickTimer.Task; @@ -44,16 +43,13 @@ public class LocationSelector extends JPanel { private final static Logger LOGGER = Logger .getLogger(LocationSelector.class); - private DefaultListModel<Location> availableLocationModel = new DefaultListModel<Location>(); - private DefaultListModel<Location> selectedLocationModel = new DefaultListModel<Location>(); - - private JButton addAllButton = new JButton(">>"); - private JButton addButton = new JButton(">"); - private JButton removeButton = new JButton("<"); - private JButton removeAllButton = new JButton("<<"); - 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); + 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 @@ -61,121 +57,26 @@ public class LocationSelector extends JPanel { private boolean initDone = false; /** - * List of ID's of locations to set the selection to when we finished initializing + * 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() { - GridManager grid = new GridManager(this, 3); - LocationRenderer locationRenderer = new LocationRenderer(); - - // the available list of locations - final JList<Location> availableLocationList = new JList<Location>(); - availableLocationList.setCellRenderer(locationRenderer); - availableLocationList.setModel(availableLocationModel); - - // the selected list of locations - final JList<Location> selectedLocationList = new JList<Location>(); - selectedLocationList.setCellRenderer(locationRenderer); - selectedLocationList.setModel(selectedLocationModel); - - // the listeners for lists - selectedLocationList.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - Location selection = selectedLocationList.getSelectedValue(); - if (selection != null) { - if (selectedLocationModel.contains(selection)) { - selectedLocationModel.removeElement(selection); - if (!availableLocationModel.contains(selection)) { - availableLocationModel.addElement(selection); - } - sortLists(); - } - } - } - } - }); - - availableLocationList.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - Location selection = availableLocationList.getSelectedValue(); - if (selection != null) { - if (!selectedLocationModel.contains(selection)) { - selectedLocationModel.addElement(selection); - availableLocationModel.removeElement(selection); - } - sortLists(); - } - } - } - }); - - // the listeners for the button panel in the middle - addAllButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - moveAll(availableLocationModel, selectedLocationModel); - } - }); - addButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - addToSelection(availableLocationList.getSelectedValuesList()); - } - }); - removeButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - removeFromSelection(selectedLocationList.getSelectedValuesList()); - - } - }); - removeAllButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - moveAll(selectedLocationModel, availableLocationModel); - } - }); - - // the text header - grid.add(new QLabel("Verfügbare Räume")); - grid.skip(); - grid.add(new QLabel("Ausgewählte Räume")); - grid.nextRow(); - - // the available location panel - JScrollPane availableScrollPane = new JScrollPane(availableLocationList); - availableScrollPane.setPreferredSize(Gui.getScaledDimension(200, 150)); - grid.add(availableScrollPane).fill(true, true).expand(true, true); - - // the middle button panel - JPanel buttonPanel = new JPanel(); - GridManager buttonGrid = new GridManager(buttonPanel, 1); - buttonGrid.add(addAllButton).fill(true, false); - buttonGrid.nextRow(); - buttonGrid.add(addButton).fill(true, false); - buttonGrid.nextRow(); - buttonGrid.add(removeButton).fill(true, false); - buttonGrid.nextRow(); - buttonGrid.add(removeAllButton).fill(true, false); - buttonGrid.nextRow(); - buttonGrid.finish(true); - grid.add(buttonPanel).fill(false, false).expand(false, false); - - // the selection location panel - JScrollPane selectionScrollPane = new JScrollPane(selectedLocationList); - selectionScrollPane.setPreferredSize(Gui.getScaledDimension(200, 150)); - grid.add(selectionScrollPane).fill(true, true).expand(true, true); + // 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 + // the radio buttons, default is to show the lecture exclusively in the + // selected locations exclusivelyButton.setSelected(true); ButtonGroup group = new ButtonGroup(); group.add(exclusivelyButton); @@ -186,12 +87,18 @@ public class LocationSelector extends JPanel { radioPanel.add(prioritizedButton); grid.add(radioPanel, 3); grid.nextRow(); -// grid.add(limitToAllowedUsers, 3); + 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 @@ -219,31 +126,65 @@ public class LocationSelector extends JPanel { } /** - * Getter for the available location model + * 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. * - * @return the locationModel of the "available location" list + * @param list + * of locations to set the available list to + * @return true if setting the list worked, false otherwise */ - public DefaultListModel<Location> getLocationModel() { - return availableLocationModel; - } + public boolean fillLocationsList(final List<Location> locations) { + if (locations == null) + return false; - /** - * Getter for the selected location model - * - * @return the locationModel of the "selected location" list - */ - public DefaultListModel<Location> getSelectedLocationModel() { - return selectedLocationModel; + 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 + * @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 * @@ -274,161 +215,140 @@ public class LocationSelector extends JPanel { /** * 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 + * @return list of ids of the selection as Integer, empty list if the + * selection is empty */ public List<Integer> getSelectedLocationsAsIds() { - if (selectedLocationModel == null || selectedLocationModel.getSize() == 0) + TreePath[] paths = locationTree.getCheckedPaths(); + if (paths == null || paths.length == 0) { return null; - - // prepare integer list to be returned - List<Integer> idList = new ArrayList<Integer>(selectedLocationModel.getSize()); - // iterate over the selected location model - Enumeration<Location> selection = selectedLocationModel.elements(); - while (selection.hasMoreElements()) { - Location current = selection.nextElement(); - if (current == 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; - idList.add(current.getLocationId()); + 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; } - /** - * Sets the list of available locations. Should be called with the list - * given by the server - * - * @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) { - LOGGER.error("No locations returned from the metadata cache."); - return false; - } - for (Location loc : locations) { - if (loc != null) { - availableLocationModel.addElement(loc); - } else { - LOGGER.error("A location returned from the server was null"); + 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); } } - return true; - } - - /** - * Moves all elements from the available to the selected list - * - * @param model - * to move from - * @param model - * to move to - */ - private void moveAll(DefaultListModel<Location> from, - DefaultListModel<Location> to) { - for (Enumeration<Location> location = from.elements(); location - .hasMoreElements();) { - Location current = location.nextElement(); - to.addElement(current); - } - from.clear(); - sortLists(); - } - - /** - * Adds the given location to the selection - * - * @param location - * to add to selection - */ - private void addToSelection(Location location) { - if (location == null) { - return; - } - List<Location> newLocations = new ArrayList<Location>(); - newLocations.add(location); - addToSelection(newLocations); - } - - /** - * Adds the given list of locations to the selection - * - * @param list - * of locations to add to the selection - */ - private void addToSelection(List<Location> locations) { - if (locations == null) - return; - // LOGGER.debug("AddToSelection: " + locations); - for (Location location : locations) { - selectedLocationModel.addElement(location); - availableLocationModel.removeElement(location); - } - sortLists(); - } - - /** - * Removes the given list of locations from the selections - * - * @param locations - * to remove from the selection - */ - private void removeFromSelection(List<Location> locations) { - if (locations == null) - return; - // LOGGER.debug("RemoveFromSelection: " + locations); - for (Location location : locations) { - selectedLocationModel.removeElement(location); - availableLocationModel.addElement(location); + // 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)); + } } - sortLists(); + return leavesPathsList; } /** - * Helper to sort both lists when changes occur. TODO: doing this via model - * change listeners? + * Helper to get the TreePath of the given TreeNode + * @param treeNode + * @return */ - private void sortLists() { - // sort the available location list - List<Location> list = Collections.list(availableLocationModel.elements()); - Collections.sort(list, Sorters.locByName); - availableLocationModel.clear(); - for (Location loc : list) { - availableLocationModel.addElement(loc); - } - // sort the selected location list - list = Collections.list(selectedLocationModel.elements()); - Collections.sort(list, Sorters.locByName); - selectedLocationModel.clear(); - for (Location loc : list) { - selectedLocationModel.addElement(loc); + 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 + * + * @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; } - moveAll(selectedLocationModel, availableLocationModel); - - for (Integer id : preselection) { - Location loc = MetaDataCache.getLocationById(id); - if (loc != null) { - addToSelection(loc); - } + // 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); } - } - - public List<Integer> getPreselection() { - return preselection; + locationTree.setCheckedState(selectedPathsList); } } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java index b7e509cb..00c35381 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java @@ -36,6 +36,7 @@ import org.openslx.bwlp.thrift.iface.LectureWrite; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.MainWindow; +import org.openslx.dozmod.gui.control.JCheckBoxTree; import org.openslx.dozmod.gui.helper.DateTimeHelper; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.gui.helper.TextChangeListener; @@ -49,6 +50,7 @@ import org.openslx.dozmod.thrift.ThriftActions; import org.openslx.dozmod.thrift.ThriftActions.DownloadCallback; import org.openslx.dozmod.thrift.ThriftActions.LectureMetaCallback; import org.openslx.dozmod.thrift.ThriftError; +import org.openslx.dozmod.thrift.cache.MetaDataCache; import org.openslx.dozmod.thrift.cache.UserCache; import org.openslx.dozmod.util.FormatHelper; import org.openslx.dozmod.util.MapHelper; @@ -201,15 +203,19 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements btnLocations.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - locationInfo = LocationSelectionWindow.open(me, lecture.locationIds, lecture.limitToLocations); - if (locationInfo == null) { + LocationInfo ret = LocationSelectionWindow.open(me, locationInfo.locationList, locationInfo.limitToLocations); + if (ret == null) { // nothing changed return; } - LOGGER.debug("New location info: " + locationInfo); + // TODO check size of returns + locationInfo = ret; reactToChange(); } }); + if (MetaDataCache.getLocations() == null) + btnLocations.setVisible(false); + btnSaveChanges.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -288,6 +294,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements if (lecture != null) { customPermissions = ThriftActions.getLecturePermissions( JOptionPane.getFrameForComponent(me), lecture.lectureId); + locationInfo = new LocationInfo(lecture.locationIds, lecture.limitToLocations); } } fillDetails(); @@ -632,6 +639,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements txtTitle.setEditable(editable); txtDescription.setEditable(editable); btnLinkImage.setEnabled(editable); + if (MetaDataCache.getLocations() == null) + btnLocations.setEnabled(editable); //TODO Temporarily disabled until implemented chkIsExam.setEnabled(false); diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LocationSelectionWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LocationSelectionWindow.java index adc48cda..ce308d90 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LocationSelectionWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LocationSelectionWindow.java @@ -8,8 +8,11 @@ import java.util.List; import org.apache.log4j.Logger; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.Gui.GuiCallable; +import org.openslx.dozmod.gui.control.JCheckBoxTree.CheckChangeEvent; +import org.openslx.dozmod.gui.control.JCheckBoxTree.CheckChangeEventListener; import org.openslx.dozmod.gui.helper.UiFeedback; import org.openslx.dozmod.gui.window.layout.LocationSelectionWindowLayout; +import org.openslx.dozmod.thrift.Session; /** * Minimal window containing a LocationSelector widget @@ -20,6 +23,10 @@ import org.openslx.dozmod.gui.window.layout.LocationSelectionWindowLayout; public class LocationSelectionWindow extends LocationSelectionWindowLayout implements UiFeedback { + /** + * + */ + private static final long serialVersionUID = -5898656786160024212L; private static final Logger LOGGER = Logger .getLogger(LocationSelectionWindow.class); private boolean apply = false; @@ -52,6 +59,26 @@ public class LocationSelectionWindow extends LocationSelectionWindowLayout dispose(); } }); + + + // addCheckChangeEventListener + locationSelector.getTree().addCheckChangeEventListener(new CheckChangeEventListener() { + @Override + public void checkStateChanged(CheckChangeEvent event) { + List<Integer> tempIntList = locationSelector.getSelectedLocationsAsIds(); + if (tempIntList != null) { + if (tempIntList.size() > Session.getSatelliteConfig().maxLocationsPerLecture) { + // add error + btnSaveChanges.setEnabled(false); + lblError.setText("Zu viele Orte ausgewählt!"); + } else { + btnSaveChanges.setEnabled(true); + lblError.setText(""); + } + } + } + }); + // btnSaveChanges.setEnabled(false); Gui.centerShellOverShell(modalParent, this); } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LocationSelectionWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LocationSelectionWindowLayout.java index 47ac1691..3a3b8a5b 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LocationSelectionWindowLayout.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LocationSelectionWindowLayout.java @@ -1,7 +1,7 @@ package org.openslx.dozmod.gui.window.layout; +import java.awt.Color; import java.awt.Window; -import java.awt.Dialog.ModalityType; import java.util.List; import javax.swing.BorderFactory; @@ -9,6 +9,7 @@ import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; @@ -19,9 +20,14 @@ import org.openslx.dozmod.gui.helper.GridManager; public class LocationSelectionWindowLayout extends JDialog { + /** + * + */ + private static final long serialVersionUID = -7722131149214063979L; protected LocationSelector locationSelector; protected JButton btnSaveChanges; protected JButton btnClose; + protected JLabel lblError; public LocationSelectionWindowLayout(Window modalParent, List<Integer> locationList, boolean limitToLocations) { @@ -43,14 +49,17 @@ public class LocationSelectionWindowLayout extends JDialog { // button panel on the bottom JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); + lblError = new JLabel(); + lblError.setForeground(Color.red); btnClose = new JButton("Abbrechen"); btnSaveChanges = new JButton("Übernehmen"); + buttonPanel.add(lblError); buttonPanel.add(Box.createHorizontalGlue()); buttonPanel.add(btnClose); buttonPanel.add(btnSaveChanges); grid.add(buttonPanel).fill(true, false).expand(true, false); grid.finish(false); - setPreferredSize(Gui.getScaledDimension(600, 350)); + setPreferredSize(Gui.getScaledDimension(600, 600)); pack(); Gui.centerShellOverShell(modalParent, this); } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureLocationSelectionPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureLocationSelectionPage.java index e02f2829..40e1110c 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureLocationSelectionPage.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureLocationSelectionPage.java @@ -35,24 +35,24 @@ public class LectureLocationSelectionPage extends LectureLocationSelectionPageLa } private boolean updateState() { - DefaultListModel<Location> selectedLocationModel = locationSelector.getSelectedLocationModel(); - if (selectedLocationModel != null && selectedLocationModel.elements() != null) { - // prepare the final list - if (state.locations == null) { - state.locations = new ArrayList<Integer>(); - } else { - state.locations.clear(); - } - List<Location> selectedLocationList = Collections.list(selectedLocationModel.elements()); - for (Location loc : selectedLocationList) { - state.locations.add(loc.getLocationId()); - } - // check the state of the checkbox only if we have a selection - state.onlyInSelectedLocations = locationSelector.getOnlyInSelection(); - } else { - // allow empty location selection? - return false; - } +// DefaultListModel<Location> selectedLocationModel = locationSelector.getSelectedLocationModel(); +// if (selectedLocationModel != null && selectedLocationModel.elements() != null) { +// // prepare the final list +// if (state.locations == null) { +// state.locations = new ArrayList<Integer>(); +// } else { +// state.locations.clear(); +// } +// List<Location> selectedLocationList = Collections.list(selectedLocationModel.elements()); +// for (Location loc : selectedLocationList) { +// state.locations.add(loc.getLocationId()); +// } +// // check the state of the checkbox only if we have a selection +// state.onlyInSelectedLocations = locationSelector.getOnlyInSelection(); +// } else { +// // allow empty location selection? +// return false; +// } return true; } } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/util/FormatHelper.java b/dozentenmodul/src/main/java/org/openslx/dozmod/util/FormatHelper.java index 5594be6a..34108eaa 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/util/FormatHelper.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/util/FormatHelper.java @@ -9,6 +9,7 @@ import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.PeriodFormat; import org.joda.time.format.PeriodFormatter; +import org.openslx.bwlp.thrift.iface.Location; import org.openslx.bwlp.thrift.iface.OperatingSystem; import org.openslx.bwlp.thrift.iface.UserInfo; @@ -149,4 +150,16 @@ public class FormatHelper { } return "-"; // Error } + + /** + * Format the given OS's name. + * + * @param OS a {@link OperatingSystem} instance + * @return "LastName, FirstName" + */ + public static String locName(Location location) { + if (location == null) + return "Unknown"; + return location.getLocationName(); + } } |