diff options
| author | Jonathan Bauer | 2016-03-15 14:08:22 +0100 |
|---|---|---|
| committer | Jonathan Bauer | 2016-03-15 14:08:22 +0100 |
| commit | 400fc72fc9d606e56a1d20efba37514b556aed30 (patch) | |
| tree | 6fc23ba996283b952c5170d5647396506c37a777 | |
| parent | [client] fix '.part' not beeing removed from TransferPanel when the transfer ... (diff) | |
| download | tutor-module-400fc72fc9d606e56a1d20efba37514b556aed30.tar.gz tutor-module-400fc72fc9d606e56a1d20efba37514b556aed30.tar.xz tutor-module-400fc72fc9d606e56a1d20efba37514b556aed30.zip | |
[client] reworked lecture details window
now uses tabs for subsets of options - clearing up the gui a bit
9 files changed, 677 insertions, 351 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java index 4f66e54d..ff1327bb 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java @@ -5,6 +5,8 @@ import java.awt.Insets; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.EventListener; +import java.util.EventObject; import java.util.Iterator; import java.util.List; @@ -13,8 +15,8 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextPane; +import javax.swing.event.EventListenerList; import javax.swing.text.BadLocationException; -import javax.swing.text.MutableAttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; @@ -25,6 +27,7 @@ import org.openslx.bwlp.thrift.iface.NetRule; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.helper.GridManager; import org.openslx.dozmod.gui.helper.MessageType; +import org.openslx.dozmod.gui.helper.TextChangeListener; /** * Widget for advanced configuration options for lectures. This handles @@ -35,7 +38,8 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { private static final long serialVersionUID = -3497629601818983994L; private final static Logger LOGGER = Logger .getLogger(AdvancedConfigurator.class); - + private String originalRawRuleText = null; + private String originalRunScript = null; /** * Character defining how the rules are parsed, e.g. for whitespace \\s * Example: "8.8.8.8 80 in" would be split in -hostname "8.8.8.8" -port "80" @@ -45,11 +49,28 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { public AdvancedConfigurator() { super(); + + final TextChangeListener docListener = new TextChangeListener() { + @Override + public void changed() { + fireAdvancedConfigurationChangeEvent(new AdvancedConfigurationChangeEvent( + new Object())); + } + }; + tpNetworkRules.getDocument().addDocumentListener(docListener); + taRunScript.getDocument().addDocumentListener(docListener); + + } + + public boolean hasChanged() { + return !originalRawRuleText.equalsIgnoreCase(tpNetworkRules.getText()) + || !originalRunScript.equalsIgnoreCase(taRunScript.getText()); } /** - * Gets the state of the widget. This will first try to parse the tpNetworkRules - * and taRunScript and build the corresponding AdvancedConfiguration Object returned. + * Gets the state of the widget. This will first try to parse the + * tpNetworkRules and taRunScript and build the corresponding + * AdvancedConfiguration Object returned. * * @return advanced configuration object composed of the parsed network * rules as List<NetRule> and the raw runscript text as String @@ -58,10 +79,6 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { public AdvancedConfiguration getState() { // cleanup the TextPane for network rules if needed String input = tpNetworkRules.getText().trim(); - input = input.replaceAll("(?m)^\\s*", ""); - input = input.replaceAll("(?m)\\s*$", ""); - tpNetworkRules.setText(input); - List<NetRule> rules = parseNetRules(input); if (rules != null) { return new AdvancedConfiguration(rules, taRunScript.getText()); @@ -70,20 +87,25 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { } /** - * 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 config AdvancedConfiguration to set the state to + * 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 config + * AdvancedConfiguration to set the state to */ public void setState(final AdvancedConfiguration config) { // setText() blanks the text area if null is given, so no null checks - this.tpNetworkRules.setText(decodeNetRulesToText(config.netRulesList)); - this.taRunScript.setText(config.runScriptText); + originalRawRuleText = decodeNetRulesToText(config.netRulesList); + originalRunScript = config.runScriptText != null ? config.runScriptText : ""; + this.tpNetworkRules.setText(originalRawRuleText); + this.taRunScript.setText(originalRunScript); } /** - * "Decodes" the given list of NetRule to a single String. This should be used - * to set the text in the TextPane for the network rules + * "Decodes" the given list of NetRule to a single String. This should be + * used to set the text in the TextPane for the network rules + * * @param netRulesList * list of NetRule to decode * @return String representation of the list of rules @@ -107,7 +129,8 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { currentLine += currentRule.host + " "; currentLine += currentRule.port + " "; currentLine += currentRule.direction.name(); - decodedRules += currentLine + System.lineSeparator(); + decodedRules += currentLine + + (it.hasNext() ? System.lineSeparator() : ""); } return decodedRules; } @@ -129,6 +152,9 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { if (rawNetRules.isEmpty()) { return rulesList; } + // prune the text first + String prunedRawNetRules = rawNetRules.replaceAll("(?m)^\\s*", ""); + prunedRawNetRules = prunedRawNetRules.replaceAll("(?m)\\s*$", ""); // split it line by line final String[] netRules = rawNetRules.split("[" @@ -141,12 +167,12 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { String[] fields = ruleLine.split(FIELD_DELIMITER); if (fields.length != 3) { markText(ruleLine, Color.RED); - // log numbers for fields independently... + // log numbers for fields independently... LOGGER.debug("Invalid number of fields! Expected 3, got: " + fields.length); Gui.showMessageBox( - "Ungültige Syntax: Nutzen Sie: <host> <port> [in|out|in/out]", - MessageType.ERROR, LOGGER, null); + "Ungültige Syntax: Nutzen Sie: <host> <port> [in|out|in/out]", + MessageType.ERROR, LOGGER, null); break; } @@ -159,8 +185,8 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { LOGGER.debug("Invalid net direction! Expected 'in' or out'. Got: " + ruleDirection); Gui.showMessageBox( - "Ungültige Richtung: nur 'in', 'out', 'in/out' und 'out/in' erlaubt!", - MessageType.ERROR, LOGGER, null); + "Ungültige Richtung: nur 'in', 'out', 'in/out' und 'out/in' erlaubt!", + MessageType.ERROR, LOGGER, null); break; } // check port: accept if > -2 and < 65535 @@ -176,8 +202,8 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { markText(ruleLine, Color.RED); LOGGER.debug("Invalid port number! Got: " + port); Gui.showMessageBox( - "Ungültiges Port! Muss zwischen 0 und 65536 sein.", - MessageType.ERROR, LOGGER, null); + "Ungültiges Port! Muss zwischen 0 und 65536 sein.", + MessageType.ERROR, LOGGER, null); break; } // check hostname: bit more to do here @@ -203,11 +229,11 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { LOGGER.debug("Invalid host/IP! Got: " + fields[0]); if (checkHostnameSimple(fields[0])) { markText(ruleLine, Color.ORANGE); - if (!Gui.showMessageBox( - "Konnte '" - + fields[0] - + "' nicht verifizieren. Wollen Sie es trotzdem verwenden?", - MessageType.WARNING_RETRY, LOGGER, null)) { + if (!Gui.showMessageBox( + "Konnte '" + + fields[0] + + "' nicht verifizieren. Wollen Sie es trotzdem verwenden?", + MessageType.WARNING_RETRY, LOGGER, null)) { break; } else { // keep it @@ -224,6 +250,9 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { .toUpperCase()), fields[0], port)); } if (netRules.length == rulesList.size()) { + // pruned rules were successfully parsed so they are valid: set the textpane to it + tpNetworkRules.setText(prunedRawNetRules); + LOGGER.debug("Success"); return rulesList; } return null; @@ -286,7 +315,6 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { // TODO still buggy with text colors: the marking works fine // but when trying to input new text, funny things happen private void markText(final String txt, final Color color) { - MutableAttributeSet origSet = tpNetworkRules.getInputAttributes(); SimpleAttributeSet set = new SimpleAttributeSet(); StyleConstants.setForeground(set, color); StyleConstants.setBold(set, color == Color.red); @@ -307,9 +335,12 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { "Failed to set '" + txt + "' to color " + color.toString(), e); } - // resetting the char attr to what they were before (buggy) - tpNetworkRules.setCharacterAttributes(origSet, true); + // resetting the char attr to what they were before (buggy) + tpNetworkRules.setStyledDocument(doc); + StyleConstants.setForeground(set, Color.WHITE); + StyleConstants.setBold(set, false); + tpNetworkRules.setCharacterAttributes(set, true); } /** @@ -328,6 +359,49 @@ public class AdvancedConfigurator extends AdvancedConfiguratorLayout { this.runScriptText = runScriptText; } } + + /** + * Custom event mechanism to detect changes to the user list (Mostly needed + * for the reactToChange() stuff in LectureDetailsWindow) + */ + protected EventListenerList listenerList = new EventListenerList(); + + public class AdvancedConfigurationChangeEvent extends EventObject { + + private static final long serialVersionUID = -8779550754760035845L; + + public AdvancedConfigurationChangeEvent(Object source) { + super(source); + } + } + + public interface AdvancedConfigurationChangeEventListener extends + EventListener { + public void stateChanged(AdvancedConfigurationChangeEvent event); + } + + public void addAdvancedConfigurationChangeEventListener( + AdvancedConfigurationChangeEventListener listener) { + listenerList.add(AdvancedConfigurationChangeEventListener.class, + listener); + } + + public void removeAdvancedConfigurationChangeEventListener( + AdvancedConfigurationChangeEventListener listener) { + listenerList.remove(AdvancedConfigurationChangeEventListener.class, + listener); + } + + void fireAdvancedConfigurationChangeEvent( + AdvancedConfigurationChangeEvent evt) { + Object[] listeners = listenerList.getListenerList(); + for (int i = 0; i < listeners.length; i++) { + if (listeners[i] == AdvancedConfigurationChangeEventListener.class) { + ((AdvancedConfigurationChangeEventListener) listeners[i + 1]) + .stateChanged(evt); + } + } + } } /** @@ -398,6 +472,5 @@ class AdvancedConfiguratorLayout extends JPanel { grid.nextRow(); grid.add(pnlRunScript).fill(true, true).expand(true, true); grid.finish(false); - } } 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 index 7ead4112..21460ba2 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/JCheckBoxTree.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/JCheckBoxTree.java @@ -100,12 +100,14 @@ public class JCheckBoxTree extends JTree { // Hack to get rid of leaf nodes with an expand button // first expand all nodes for (int i = 0; i < this.getRowCount(); i++) { + System.out.println("Open: " + i); this.expandRow(i); } // Now collapse again if it's not first level - for (int i = 0; i < this.getRowCount(); i++) { + for (int i = 0; i > this.getRowCount(); i++) { TreePath path = this.getPathForRow(i); if (path.getPathCount() > 1) { + System.out.println("Collpasing: " + path); this.collapseRow(i); } } @@ -115,9 +117,16 @@ public class JCheckBoxTree extends JTree { if (paths == null) return; for (TreePath path : paths) { - if (path != null) + if (path != null) { checkSubTree(path, check); + if (check) { + System.out.println("Checking: " + path); + this.expandPath(path); + } + } } + // here we still have all but the first level nodes collapsed + // so we need to expand the nodes we just checked... } // New method that returns only the checked paths (totally ignores original @@ -132,12 +141,15 @@ public class JCheckBoxTree extends JTree { CheckedNode cn = nodesCheckingState.get(path); return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected; } - + // Collapses all nodes that have only selected children public void collapseFullySelectedNodes() { + // the paths need to be sorted by length, otherwise some nodes will get expanded again + // even though they should be collapsed for (Entry<TreePath, CheckedNode> it : nodesCheckingState.entrySet()) { TreePath path = it.getKey(); CheckedNode cn = it.getValue(); - if (cn.hasChildren && cn.allChildrenSelected) { + if (cn.hasChildren && cn.allChildrenSelected && !isCollapsed(path)) { +// System.out.println("Collapsing: " + path); collapsePath(path); } } @@ -210,6 +222,8 @@ public class JCheckBoxTree extends JTree { public JCheckBoxTree() { super(); + // remove default model + ((DefaultMutableTreeNode) this.getModel().getRoot()).removeAllChildren(); // Disabling toggling by double-click this.setToggleClickCount(0); // Overriding cell renderer by new one defined above diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LectureCustomPermissionManager.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LectureCustomPermissionManager.java index 2e02b683..d943d4c6 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LectureCustomPermissionManager.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/LectureCustomPermissionManager.java @@ -4,6 +4,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.ArrayList; +import java.util.EventListener; +import java.util.EventObject; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -18,13 +20,14 @@ import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.event.EventListenerList; import org.apache.log4j.Logger; import org.openslx.bwlp.thrift.iface.LecturePermissions; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.dozmod.gui.control.table.LecturePermissionTable; -import org.openslx.dozmod.gui.control.table.QScrollPane; import org.openslx.dozmod.gui.control.table.LecturePermissionTable.UserLecturePermissions; +import org.openslx.dozmod.gui.control.table.QScrollPane; import org.openslx.dozmod.gui.helper.GridManager; import org.openslx.dozmod.gui.window.UserListWindow; import org.openslx.dozmod.gui.window.UserListWindow.UserAddedCallback; @@ -33,6 +36,10 @@ import org.openslx.dozmod.gui.window.UserListWindow.UserAddedCallback; * Panel including LecturePermissionTable and add/remove buttons for setting * customLecturePermissions. */ +/** + * @author joe + * + */ @SuppressWarnings("serial") public class LectureCustomPermissionManager extends JPanel { @@ -101,6 +108,7 @@ public class LectureCustomPermissionManager extends JPanel { permissionList.add(new UserLecturePermissions(newUser.userId, new LecturePermissions( defaultPermissions))); permissionTable.setData(permissionList, false); + fireUserChangeEvent(new UserChangeEvent(new Object())); } }, "Hinzufügen", ownerId); } @@ -115,6 +123,7 @@ public class LectureCustomPermissionManager extends JPanel { LOGGER.debug("Could not remove: " + selected); } permissionTable.setData(permissionList, false); + fireUserChangeEvent(new UserChangeEvent(new Object())); } }); @@ -128,6 +137,9 @@ public class LectureCustomPermissionManager extends JPanel { } }); } + public LecturePermissionTable getTable() { + return permissionTable; + } /** * Initialise the PermissionManager @@ -180,4 +192,39 @@ public class LectureCustomPermissionManager extends JPanel { defaultPermissions.edit = edit; } + + /** + * Custom event mechanism to detect changes to the user list + * (Mostly needed for the reactToChange() stuff in LectureDetailsWindow) + */ + protected EventListenerList listenerList = new EventListenerList(); + + public class UserChangeEvent extends EventObject { + + public UserChangeEvent(Object source) { + super(source); + } + } + + public interface UserChangeEventListener extends EventListener { + public void stateChanged(UserChangeEvent event); + } + + public void addUserChangeEventListener(UserChangeEventListener listener) { + listenerList.add(UserChangeEventListener.class, listener); + } + + public void removeUserChangeEventListener(UserChangeEventListener listener) { + listenerList.remove(UserChangeEventListener.class, listener); + } + + void fireUserChangeEvent(UserChangeEvent evt) { + Object[] listeners = listenerList.getListenerList(); + for (int i = 0; i < listeners.length; i++) { + if (listeners[i] == UserChangeEventListener.class) { + ((UserChangeEventListener) listeners[i + 1]) + .stateChanged(evt); + } + } + } } 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 032ba80f..d54dfa61 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 @@ -43,8 +43,7 @@ public class LocationSelector extends JPanel { private final static Logger LOGGER = Logger.getLogger(LocationSelector.class); private final JRadioButton btnLimitToLocations; - private final JRadioButton btnPrioritizeInLocations; - + private final JRadioButton btnPrioritizeInLocations; /** * Flag for the initialization state */ @@ -69,9 +68,9 @@ public class LocationSelector extends JPanel { "Veranstaltung mit höherer Priorität in den ausgewählten Räumen anzeigen"); btnPrioritizeInLocations.setSelected(true); - ButtonGroup group = new ButtonGroup(); - group.add(btnLimitToLocations); - group.add(btnPrioritizeInLocations); + ButtonGroup grpButtons = new ButtonGroup(); + grpButtons.add(btnLimitToLocations); + grpButtons.add(btnPrioritizeInLocations); // build the grid GridManager grid = new GridManager(this, 1); @@ -92,6 +91,12 @@ public class LocationSelector extends JPanel { public JCheckBoxTree getTree() { return locationTree; } + public JRadioButton[] getButtons() { + JRadioButton[] buttons = new JRadioButton[2]; + buttons[0] = btnLimitToLocations; + buttons[1] = btnPrioritizeInLocations; + return buttons; + } /** * Async fetching of the list of the locations from the server @@ -229,17 +234,31 @@ public class LocationSelector extends JPanel { public List<Integer> getSelectedLocationsAsIds() { TreePath[] paths = locationTree.getCheckedPaths(); if (paths == null || paths.length == 0) { - return null; + 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 - List<TreePath> treePathList = minify(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); + } + } + + 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>(treePathList.size()); + List<Integer> idList = new ArrayList<Integer>(currentList.size()); // iterate over the paths - for (TreePath path : treePathList) { + for (TreePath path : currentList) { if (path == null) continue; DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) path.getLastPathComponent(); @@ -264,61 +283,39 @@ public class LocationSelector extends JPanel { * selected, remove all child from the result list and just keep the inner * node * - * @param paths Array of TreePath to minimize + * @param leavesPathsList as a List of TreePath to minimize * @return Resulting minimal list of TreePaths */ - 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()) { + 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; - } - // 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()) + if (leafNode.getLevel() == 1) { + resultList.add(getPath(leafNode)); 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; + } + 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 all children were selected, remove them from the list - if (allChildrenSelected) { - Enumeration<?> enumeration = parent.children(); - while (enumeration.hasMoreElements()) { - DefaultMutableTreeNode nod = (DefaultMutableTreeNode) enumeration.nextElement(); - leavesPathsList.remove(getPath(nod)); // remove already - // checks for - // existence + 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); } - // add this parent since we had only leaves in the - // leavesPathList - leavesPathsList.add(getPath(parent)); } } - return leavesPathsList; + return resultList; } /** @@ -372,6 +369,7 @@ public class LocationSelector extends JPanel { selectedPathsList.add(path); } locationTree.setCheckedState(selectedPathsList, true); + locationTree.collapseFullySelectedNodes(); locationTree.repaint(); } } @@ -387,4 +385,4 @@ class LocationRenderer extends DefaultListCellRenderer { } return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); } -}
\ No newline at end of file +} 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 df91e811..c4b3700e 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 @@ -6,6 +6,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; @@ -22,6 +24,7 @@ import java.util.Map.Entry; import javax.swing.DefaultComboBoxModel; import javax.swing.JFrame; import javax.swing.JOptionPane; +import javax.swing.JRadioButton; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -37,6 +40,12 @@ 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.AdvancedConfigurator.AdvancedConfiguration; +import org.openslx.dozmod.gui.control.AdvancedConfigurator.AdvancedConfigurationChangeEvent; +import org.openslx.dozmod.gui.control.AdvancedConfigurator.AdvancedConfigurationChangeEventListener; +import org.openslx.dozmod.gui.control.JCheckBoxTree.CheckChangeEvent; +import org.openslx.dozmod.gui.control.JCheckBoxTree.CheckChangeEventListener; +import org.openslx.dozmod.gui.control.LectureCustomPermissionManager.UserChangeEvent; +import org.openslx.dozmod.gui.control.LectureCustomPermissionManager.UserChangeEventListener; import org.openslx.dozmod.gui.helper.DateTimeHelper; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.gui.helper.TextChangeListener; @@ -50,7 +59,6 @@ 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; @@ -105,20 +113,36 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements */ private ImageDetailsRead image = null; + /** + * Flag indicating a change in this lecture's linked image + */ private boolean imageLinkChanged = false; + /** + * Flag indicating a change in this lecture's metadata + */ private boolean metadataChanged = false; + /** + * Flag indicating a change in this lecture's permissions + */ private boolean permissionsChanged = false; + /** + * Holder of the location information of this lecture + */ private LocationInfo locationInfo = null; + /** + * Holder of the netrules/runscript of this lecture + */ private AdvancedConfiguration currentAdvConf = null; /** * Constructor * * @param modalParent parent of this popup window + * @param callback function to be called when a lecture update occured */ public LectureDetailsWindow(Frame modalParent, LectureUpdatedCallback callback) { super(modalParent); @@ -127,6 +151,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements this.callback = callback; setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { @@ -176,16 +201,6 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements cboVersions.setEnabled(!chkAutoUpdate.isSelected()); } }); - btnAdvancedOptions.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - AdvancedConfiguration conf = LectureAdvancedSettingsWindow.open(JOptionPane.getFrameForComponent(me), currentAdvConf); - if (conf == null) - return; - currentAdvConf = conf; - reactToChange(); - } - }); btnChangeOwner.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -201,48 +216,61 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements }, "Besitzer festlegen", lecture.ownerId); } }); - btnPermissions.addActionListener(new ActionListener() { + + final ActionListener changeReacter = new ActionListener() { @Override - public void actionPerformed(ActionEvent arg0) { - // NOTE once the following window is closed, customPermissions and lecture.defaultPermissions - // objects will contain the changes the user did. We will later only compare these with - // the original values as saved in originalCustomPermissions and originalDefaultPermissions - LecturePermissionWindow.open(me, customPermissions, lecture.defaultPermissions, - lecture.ownerId); + public void actionPerformed(ActionEvent e) { reactToChange(); } - }); - btnLocations.addActionListener(new ActionListener() { + }; + + ctlLocationSelector.getTree().addCheckChangeEventListener(new CheckChangeEventListener() { @Override - public void actionPerformed(ActionEvent e) { - LocationInfo ret = LocationSelectionWindow.open(me, locationInfo.locationList, locationInfo.limitToLocations); - if (ret == null) { - // nothing changed - return; - } - // TODO check size of returns - locationInfo = ret; + public void checkStateChanged(CheckChangeEvent event) { reactToChange(); } }); - if (MetaDataCache.getLocations() == null || MetaDataCache.getLocations().isEmpty()) { - btnLocations.setVisible(false); + for (JRadioButton btn : ctlLocationSelector.getButtons()) { + btn.addActionListener(changeReacter); } - - btnSaveChanges.addActionListener(new ActionListener() { + final ActionListener updateDefaultPermissionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - saveChanges(); + ctlPermissionManager.updateDefaultPermissions(chkCustomPermAdmin.isSelected(), + chkCustomPermEdit.isSelected()); + reactToChange(); + } + }; + chkCustomPermAdmin.addActionListener(updateDefaultPermissionListener); + chkCustomPermEdit.addActionListener(updateDefaultPermissionListener); + ctlPermissionManager.getTable().addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + reactToChange(); + } + }); + ctlPermissionManager.addUserChangeEventListener(new UserChangeEventListener() { + @Override + public void stateChanged(UserChangeEvent event) { + reactToChange(); } }); + chkCustomPermAdmin.addActionListener(changeReacter); + chkCustomPermEdit.addActionListener(changeReacter); - // final step, add listeners to react to change + ctlAdvancedConfigurator.addAdvancedConfigurationChangeEventListener(new AdvancedConfigurationChangeEventListener() { + @Override + public void stateChanged(AdvancedConfigurationChangeEvent event) { + btnSaveChanges.setEnabled(ctlAdvancedConfigurator.hasChanged()); + } + }); final TextChangeListener docListener = new TextChangeListener() { @Override public void changed() { reactToChange(); } }; + txtTitle.getDocument().addDocumentListener(docListener); txtDescription.getDocument().addDocumentListener(docListener); @@ -282,10 +310,17 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements // save default color of date/time stuff to reset the background later dateTimeTextColor = dtpStartDate.getForeground(); + // last step, the save button + btnSaveChanges.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveChanges(); + } + }); // disable save button btnSaveChanges.setEnabled(false); // wait for the image to be loaded before (potentially) enabling fields - makeEditable(false); + toggleEditable(false); } /** @@ -308,28 +343,30 @@ 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(); } - fillDetails(); } }); } /** - * callback function when we received the lecture's details from the server + * Internal callback function when we received the lecture's details from the server */ private void fillDetails() { if (lecture == null) { txtTitle.setText("-"); - makeEditable(false); + lblTitleInfo.setText("-"); + toggleEditable(false); return; } - + LOGGER.debug("LECT: " + lecture); if (image == null) { txtImageName.setText("-"); + lblImageNameInfo.setText("-"); } else { txtImageName.setText(image.getImageName()); + lblImageNameInfo.setText(image.getImageName()); } // remember original advanced config currentAdvConf = new AdvancedConfiguration(lecture.networkExceptions, lecture.runscript); @@ -350,10 +387,24 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements originalCustomPermissions.put(entry.getKey(), new LecturePermissions(entry.getValue())); } } - + // init permission info + ctlPermissionManager.initPanel(customPermissions, lecture.defaultPermissions, lecture.ownerId); + chkCustomPermAdmin.setSelected(lecture.defaultPermissions.admin); + chkCustomPermEdit.setSelected(lecture.defaultPermissions.edit); + // init location info + locationInfo = new LocationInfo(lecture.locationIds, lecture.limitToLocations); + ctlLocationSelector.setOnlyInSelection(locationInfo.limitToLocations); + ctlLocationSelector.setSelectedLocationsAsIds(locationInfo.locationList); + // advanced config stuff, save current information + currentAdvConf = new AdvancedConfiguration(lecture.networkExceptions, lecture.runscript); + // init advanced info + ctlAdvancedConfigurator.setState(new AdvancedConfiguration(lecture.networkExceptions, lecture.runscript)); + txtTitle.setText(lecture.getLectureName()); + lblTitleInfo.setText(lecture.getLectureName()); txtDescription.setText(lecture.getDescription()); lblOwner.setUser(UserCache.find(lecture.getOwnerId())); + lblOwnerInfo.setUser(UserCache.find(lecture.getOwnerId())); lblUpdater.setUser(UserCache.find(lecture.getUpdaterId())); lblCreateTime.setText(FormatHelper.longDate(lecture.getCreateTime())); lblUpdateTime.setText(FormatHelper.longDate(lecture.getUpdateTime())); @@ -369,7 +420,6 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements cboVersions.setEnabled(!lecture.autoUpdate); lblUseCount.setText(Integer.toString(lecture.useCount)); - fillVersionsCombo(); Calendar startCal = Calendar.getInstance(); @@ -384,7 +434,10 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements endCal.get(Calendar.DATE)); spnEndTime.getModel().setValue(endCal.getTime()); - makeEditable(true); + // now enable the tabs the user can see given its permissions + toggleEditable(true); + // and always switch to the "About" tab + pnlTabs.setSelectedIndex(pnlTabs.indexOfTab("Übersicht")); setVisible(true); } @@ -433,7 +486,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements // success Gui.showMessageBox(me, "Besitzrechte übertragen an " + FormatHelper.userName(user), MessageType.INFO, null, null); - makeEditable(false); + toggleEditable(false); String lectureId = lecture.getLectureId(); synchronized (me) { image = null; @@ -484,7 +537,87 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements } /** - * Push the changes of the image details to the satellite + * Checks whether the user changed any fields of the image details and + * enables the save button if so. + */ + private void reactToChange() { + // before checks we need to update the local objects holding the information of the "widgets" + // update the location information + locationInfo = new LocationInfo(ctlLocationSelector.getSelectedLocationsAsIds(), + ctlLocationSelector.getOnlyInSelection()); + // update the custom permissions + customPermissions = ctlPermissionManager.updatePermissionReferences(); + // NOTE: AdvancedConfiguration stuff will only get checked when clicking "Save" since + // checking for rules syntax while the user is still typing does not make much sense... + // now we can check for changes + metadataChanged = reactToChangeInternal(); + permissionsChanged = MapHelper.hasChanged(originalCustomPermissions, customPermissions); + btnSaveChanges.setEnabled(metadataChanged || permissionsChanged); + } + + /** + * Checks whether the user changed any fields of the image details and + * enables the save button if so. + */ + private boolean reactToChangeInternal() { + if (lecture == null) + return false; + + boolean changed = false; + // mandatory fields checks + if (txtTitle.getText().isEmpty()) { + pnlTabs.setSelectedIndex(pnlTabs.indexOfTab("Allgemein")); + lblError.setText("Kein Veranstaltungsname gesetzt!"); + return false; + } + if (txtDescription.getText().isEmpty()) { + pnlTabs.setSelectedIndex(pnlTabs.indexOfTab("Allgemein")); + lblError.setText("Keine Beschreibung angegeben!"); + return false; + } + // version checkbox changed? + ImageVersionDetails currentVersion = cboVersions.getItemAt(cboVersions.getSelectedIndex()); + if (currentVersion == null) { + pnlTabs.setSelectedIndex(pnlTabs.indexOfTab("Allgemein")); + lblError.setText("Keine Version ausgewählt!"); + return false; + } + // Date stuff + Date start = DateTimeHelper.getDateFrom(dtpStartDate, spnStartTime); + Date end = DateTimeHelper.getDateFrom(dtpEndDate, spnEndTime); + if (!isPeriodValid(start, end, false) && dateHasChanged()) { + pnlTabs.setSelectedIndex(pnlTabs.indexOfTab("Allgemein")); + lblError.setText("Ungültiger Zeitraum!"); + return false; + } + + // done with mandatory checks, remove error message + lblError.setText(null); + + // check for changes in all fields + changed = !txtTitle.getText().equals(lecture.getLectureName()) + || !txtDescription.getText().equals(lecture.getDescription()) + || !currentVersion.getVersionId().equals(lecture.getImageVersionId()) + || (DateTimeHelper.getDateFrom(dtpStartDate, spnStartTime).getTime() / 1000L) != lecture.getStartTime() + || (DateTimeHelper.getDateFrom(dtpEndDate, spnEndTime).getTime() / 1000L) != lecture.getEndTime() + || chkAutoUpdate.isSelected() != lecture.autoUpdate + || chkIsExam.isSelected() != lecture.isExam + || chkHasInternetAccess.isSelected() != lecture.hasInternetAccess + || chkHasUsbAccess.isSelected() != lecture.hasUsbAccess + || chkIsActive.isSelected() != lecture.isEnabled + || !lecture.defaultPermissions.equals(originalDefaultPermissions) + || (locationInfo != null && lecture.locationIds != null && !lecture.locationIds.equals(locationInfo.locationList)) + || (locationInfo != null && lecture.limitToLocations != locationInfo.limitToLocations) + || (currentAdvConf != null && lecture.networkExceptions != null && !lecture.networkExceptions.equals(currentAdvConf.netRulesList)) + || (currentAdvConf != null && lecture.runscript != null && !lecture.runscript.equals(currentAdvConf.runScriptText)) + || imageLinkChanged; + + return changed; + } + + /** + * Triggers the saving of the changes of this lecture + * And inform the callback function about the outcome */ private void saveChanges() { boolean saved = saveChangesInternal(); @@ -495,14 +628,30 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements btnSaveChanges.setEnabled(true); } + /** + * Internal helper actually saving the changes to the satellite + * + * @return true if saving succeeded, false otherwise. + */ private boolean saveChangesInternal() { // check, whether autoupdate is selected and choose version accordingly if (image != null) { lecture.imageVersionId = chkAutoUpdate.isSelected() ? image.latestVersionId : cboVersions.getItemAt(cboVersions.getSelectedIndex()).versionId; } + // goto to advanced tab and check (by getting) the state of the textAreas + pnlTabs.setSelectedIndex(pnlTabs.indexOfTab("Erweitert")); + currentAdvConf = ctlAdvancedConfigurator.getState(); + if (currentAdvConf == null) { + // getState() will return null only if it couldn't parse the rules + // which means that the user got informed about what was invalid + // So we just return here until the parsing works. + lblError.setText("Ungültiger Zeitraum!"); + return false; + } // now check if we need to push a new LectureWrite if (metadataChanged) { + LOGGER.info("Trying to save..."); // first build the LectureWrite from the GUI fields final LectureWrite metadata = new LectureWrite(txtTitle.getText(), txtDescription.getText(), lecture.getImageVersionId(), chkAutoUpdate.isSelected(), chkIsActive.isSelected(), @@ -588,100 +737,22 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements } /** - * Checks whether the user changed any fields of the image details and - * enables the save button if so. - */ - private void reactToChange() { - // check details fields - metadataChanged = reactToChangeInternal(); - permissionsChanged = MapHelper.hasChanged(originalCustomPermissions, customPermissions); - btnSaveChanges.setEnabled(metadataChanged || permissionsChanged); - } - - /** - * Checks whether the user changed any fields of the image details and - * enables the save button if so. - */ - private boolean reactToChangeInternal() { - if (lecture == null) - return false; - - boolean changed = false; - // mandatory fields checks - if (txtTitle.getText().isEmpty()) { - lblError.setText("Kein Veranstaltungsname gesetzt!"); - return false; - } - if (txtDescription.getText().isEmpty()) { - lblError.setText("Keine Beschreibung angegeben!"); - return false; - } - // version checkbox changed? - ImageVersionDetails currentVersion = cboVersions.getItemAt(cboVersions.getSelectedIndex()); - if (currentVersion == null) { - lblError.setText("Keine Version ausgewählt!"); - return false; - } - // Date stuff - Date start = DateTimeHelper.getDateFrom(dtpStartDate, spnStartTime); - Date end = DateTimeHelper.getDateFrom(dtpEndDate, spnEndTime); - if (!isPeriodValid(start, end, false) && dateHasChanged()) { - lblError.setText("Ungültiger Zeitraum!"); - return false; - } - - // done with mandatory checks, remove error message - lblError.setText(null); - - // check for changes in all fields - changed = !txtTitle.getText().equals(lecture.getLectureName()) - || !txtDescription.getText().equals(lecture.getDescription()) - || !currentVersion.getVersionId().equals(lecture.getImageVersionId()) - || (DateTimeHelper.getDateFrom(dtpStartDate, spnStartTime).getTime() / 1000L) != lecture.getStartTime() - || (DateTimeHelper.getDateFrom(dtpEndDate, spnEndTime).getTime() / 1000L) != lecture.getEndTime() - || chkAutoUpdate.isSelected() != lecture.autoUpdate - || chkIsExam.isSelected() != lecture.isExam - || chkHasInternetAccess.isSelected() != lecture.hasInternetAccess - || chkHasUsbAccess.isSelected() != lecture.hasUsbAccess - || chkIsActive.isSelected() != lecture.isEnabled - || !lecture.defaultPermissions.equals(originalDefaultPermissions) - || (locationInfo != null && lecture.locationIds != null && !lecture.locationIds.equals(locationInfo.locationList)) - || (locationInfo != null && lecture.limitToLocations != locationInfo.limitToLocations) - || (currentAdvConf != null && lecture.networkExceptions != null && !lecture.networkExceptions.equals(currentAdvConf.netRulesList)) - || (currentAdvConf != null && lecture.runscript != null && !lecture.runscript.equals(currentAdvConf.runScriptText)) - || imageLinkChanged; - - return changed; - } - - /** - * Enables/disables the editable fields based on 'editable' - * - * @param editable true to make fields editable, false otherwise. + * Enables/Disables the tabs based on the given flag 'editable'. + * + * @param editable when true, will enable the tabs if the user is allowed to see them. + * If false, this will disable all tabs but the first tab "About". */ - private void makeEditable(boolean editable) { - editable = editable && (LecturePerms.canEdit(lecture) || LecturePerms.canAdmin(lecture)); - txtTitle.setEditable(editable); - txtDescription.setEditable(editable); - btnLinkImage.setEnabled(editable); - if (MetaDataCache.getLocations() == null) - btnLocations.setEnabled(editable); - - //TODO Temporarily disabled until implemented - chkIsExam.setEnabled(false); - chkHasInternetAccess.setEnabled(false); -// chkIsExam.setEnabled(editable); -// chkHasInternetAccess.setEnabled(editable); - - chkIsActive.setEnabled(editable); - chkAutoUpdate.setEnabled(editable); - cboVersions.setEnabled(editable && !lecture.autoUpdate && image != null); - btnChangeOwner.setEnabled(editable && LecturePerms.canAdmin(lecture)); - btnPermissions.setEnabled(editable && LecturePerms.canAdmin(lecture)); - dtpStartDate.setEnabled(editable); - spnStartTime.setEnabled(editable); - dtpEndDate.setEnabled(editable); - spnEndTime.setEnabled(editable); + protected void toggleEditable(boolean editable) { + // if we don't have a lecture and an image set, just disable + editable &= (LecturePerms.canEdit(lecture)); + // "Übersicht" is always enabled + pnlTabs.setEnabledAt(pnlTabs.indexOfTab("Allgemein"), editable); + pnlTabs.setEnabledAt(pnlTabs.indexOfTab("Beschränkungen"), editable); + pnlTabs.setEnabledAt(pnlTabs.indexOfTab("Berechtigungen"), editable && LecturePerms.canAdmin(lecture)); + pnlTabs.setEnabledAt(pnlTabs.indexOfTab("Raumauswahl"), editable); + pnlTabs.setEnabledAt(pnlTabs.indexOfTab("Erweitert"), editable); + // special shit for those shitty special buttons.... + btnChangeOwner.setEnabled(LecturePerms.canAdmin(lecture)); btnDownloadImage.setEnabled(ImagePerms.canDownload(image)); } @@ -733,6 +804,10 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements && !Gui.showMessageBox(me, "Änderungen werden verworfen, wollen Sie wirklich schließen?", MessageType.QUESTION_YESNO, null, null)) return; + synchronized(me) { + lecture = null; + image = null; + } dispose(); } } 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 b90aee06..e1979c1f 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 @@ -56,7 +56,6 @@ public class LocationSelectionWindow extends LocationSelectionWindowLayout imple if (tempIntList != null && tempIntList.size() > Session.getSatelliteConfig().maxLocationsPerLecture) { ctlLocationSelector.setSelectedLocationsAsIds(tempIntList); - ctlLocationSelector.collapseFullySelectedNodes(); Gui.showMessageBox("Bitten reduzieren Sie die Anzahl gewählter Orte", MessageType.WARNING, null, null); return; diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureAdvancedSettingsWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureAdvancedSettingsWindowLayout.java index a81e390e..6769b862 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureAdvancedSettingsWindowLayout.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureAdvancedSettingsWindowLayout.java @@ -18,8 +18,8 @@ public class LectureAdvancedSettingsWindowLayout extends JDialog { private static final long serialVersionUID = 5565439063675405598L; private final static String txtTitle = "Erweiterte Einstellungen"; - private final static String txtButtonClose = "Schließen"; - private final static String txtButtonSave = "Speichern"; + private final static String txtButtonClose = "Abbrechen"; + private final static String txtButtonSave = "Übernehmen"; protected final AdvancedConfigurator ctlAdvancedConfigurator; diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java index 0d10d919..7b47ae69 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java @@ -21,6 +21,7 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; +import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SpinnerDateModel; @@ -31,12 +32,17 @@ import org.jdatepicker.impl.JDatePickerImpl; import org.jdatepicker.impl.UtilDateModel; import org.openslx.bwlp.thrift.iface.ImageVersionDetails; import org.openslx.dozmod.gui.Gui; +import org.openslx.dozmod.gui.control.AdvancedConfigurator; import org.openslx.dozmod.gui.control.ComboBox; import org.openslx.dozmod.gui.control.ComboBox.ComboBoxRenderer; +import org.openslx.dozmod.gui.control.LectureCustomPermissionManager; +import org.openslx.dozmod.gui.control.LocationSelector; import org.openslx.dozmod.gui.control.PersonLabel; import org.openslx.dozmod.gui.control.QDatePickerImpl; 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.thrift.cache.MetaDataCache; import org.openslx.dozmod.util.DateLabelFormatter; import org.openslx.dozmod.util.FormatHelper; import org.openslx.thrifthelper.Comparators; @@ -44,10 +50,14 @@ import org.openslx.thrifthelper.Comparators; @SuppressWarnings("serial") public abstract class LectureDetailsWindowLayout extends JDialog { + // stuff ending in '...Info' are supposed to be the read-only labels for the information tab + protected final QLabel lblTitleInfo; protected final JTextArea txtTitle; protected final JTextArea txtDescription; + protected final QLabel lblImageNameInfo; protected final QLabel txtImageName; protected final JButton btnLinkImage; + protected final PersonLabel lblOwnerInfo; protected final PersonLabel lblOwner; protected final JButton btnChangeOwner; protected final QLabel lblCreateTime; @@ -56,18 +66,17 @@ public abstract class LectureDetailsWindowLayout extends JDialog { protected final QLabel lblStartTime; protected final QLabel lblEndTime; protected final JCheckBox chkAutoUpdate; + protected final QLabel lblVersionInfo; protected final ComboBox<ImageVersionDetails> cboVersions; protected final JCheckBox chkIsExam; protected final JCheckBox chkHasInternetAccess; protected final JCheckBox chkHasUsbAccess; protected final JCheckBox chkIsActive; - protected final JButton btnAdvancedOptions; protected final JTextField txtId; protected final QLabel lblUseCount; protected final JButton btnPermissions; - protected final JButton btnLocations; protected final QLabel lblError; protected final JButton btnSaveChanges; @@ -80,9 +89,23 @@ public abstract class LectureDetailsWindowLayout extends JDialog { protected final JSpinner spnEndTime; protected Color dateTimeTextColor; + + protected JTabbedPane pnlTabs; + protected final LectureCustomPermissionManager ctlPermissionManager; + protected final LocationSelector ctlLocationSelector; + protected final AdvancedConfigurator ctlAdvancedConfigurator; private static final Properties pickerStrings = new Properties(); + protected JCheckBox chkCustomPermEdit; + protected JCheckBox chkCustomPermAdmin; + protected JPanel pnlTabGeneral; + protected JPanel pnlTabInfo; + protected JPanel pnlTabRestrictions; + protected JPanel pnlTabPermissions; + protected JPanel pnlTabLocations; + protected JPanel pnlTabAdvanced; + static { pickerStrings.put("text.today", "Heute"); pickerStrings.put("text.month", "Monat"); @@ -90,69 +113,118 @@ public abstract class LectureDetailsWindowLayout extends JDialog { } public LectureDetailsWindowLayout(Frame modalParent) { - super(modalParent, "Veranstaltungsdetails", ModalityType.APPLICATION_MODAL); + super(modalParent, "Veranstaltungsdetails", + ModalityType.APPLICATION_MODAL); setResizable(true); + setPreferredSize(Gui.getScaledDimension(525, 550)); setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); setLayout(new BorderLayout()); - ((JPanel)getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - // use panel to put every info related widget in it - // then we will set the panel in BorderLayout.CENTER - JPanel infoPanel = new JPanel(); - infoPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - GridManager grid = new GridManager(infoPanel, 3, true, new Insets(2, 2, 2, 2)); - - // -- name -- + ((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder( + 10, 10, 10, 10)); + + pnlTabInfo = new JPanel(); + pnlTabInfo.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); + GridManager grdInfo = new GridManager(pnlTabInfo, 3, true, new Insets(7, + 7, 7, 7)); + + // title + lblTitleInfo = new QLabel(); + lblTitleInfo.setFont(lblTitleInfo.getFont().deriveFont(Font.BOLD, + lblTitleInfo.getFont().getSize2D() * 1.5f)); + grdInfo.add(lblTitleInfo, 3); + grdInfo.nextRow(); + // lblOwner + lblOwnerInfo = new PersonLabel(); + grdInfo.add(new QLabel("Besitzer")); + grdInfo.add(lblOwnerInfo, 2).expand(true, false); + grdInfo.nextRow(); + // creation time + lblCreateTime = new QLabel(); + grdInfo.add(new QLabel("Erstellt am")); + grdInfo.add(lblCreateTime, 2); + grdInfo.nextRow(); + + // last updater + lblUpdater = new PersonLabel(); + grdInfo.add(new QLabel("Geändert durch")); + grdInfo.add(lblUpdater, 2); + grdInfo.nextRow(); + + // last updated + lblUpdateTime = new QLabel(); + grdInfo.add(new QLabel("Geändert am")); + grdInfo.add(lblUpdateTime, 2); + grdInfo.nextRow(); + lblVersionInfo = new QLabel(); // UNUSED + lblImageNameInfo = new QLabel(); + grdInfo.add(new QLabel("Verknüpfte VM"), 2); + grdInfo.add(lblImageNameInfo); + grdInfo.nextRow(); + lblStartTime = new QLabel(); + grdInfo.add(new QLabel("Startdatum"), 2); + grdInfo.add(lblStartTime); + grdInfo.nextRow(); + lblEndTime = new QLabel(); + grdInfo.add(new QLabel("Enddatum"), 2); + grdInfo.add(lblEndTime); + grdInfo.nextRow(); + lblUseCount = new QLabel(); + grdInfo.add(new QLabel("Aufrufe")); + grdInfo.add(lblUseCount, 2); + grdInfo.finish(true); + + pnlTabGeneral = new JPanel(); + pnlTabGeneral.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + GridManager grdGeneral = new GridManager(pnlTabGeneral, 5, true, + new Insets(3, 3, 3, 3)); + // lecture title txtTitle = new JTextArea(); - txtTitle.setFont(txtTitle.getFont().deriveFont(Font.BOLD, txtTitle.getFont().getSize2D() * 2)); - grid.add(txtTitle, 3).expand(true, false).fill(true, false); - grid.nextRow(); + grdGeneral.add(new QLabel("Name")); + grdGeneral.add(txtTitle, 4).expand(true, false).fill(true, false); + grdGeneral.nextRow(); // description txtDescription = new JTextArea(); txtDescription.setLineWrap(true); txtDescription.setWrapStyleWord(true); - grid.add(new QLabel("Beschreibung")).anchor(GridBagConstraints.FIRST_LINE_START); - JScrollPane jsp = new JScrollPane(txtDescription, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + grdGeneral.add(new QLabel("Beschreibung")).anchor( + GridBagConstraints.FIRST_LINE_START); + JScrollPane jsp = new JScrollPane(txtDescription, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); jsp.setMinimumSize(Gui.getScaledDimension(450, 120)); jsp.setPreferredSize(Gui.getScaledDimension(450, 120)); - grid.add(jsp, 2).expand(true, true).fill(true, true); - grid.nextRow(); + grdGeneral.add(jsp, 4).expand(true, true).fill(true, true); + grdGeneral.nextRow(); + + // ID. NOTE: currently disabled + txtId = new JTextField(); + txtId.setEditable(false); + txtId.setFont(txtId.getFont().deriveFont(Font.BOLD, + txtId.getFont().getSize2D() * 0.66f)); + grdGeneral.add(new QLabel("ID")); + grdGeneral.add(txtId, 4).expand(true, false).fill(true, false); + grdGeneral.nextRow(); // owner lblOwner = new PersonLabel(); btnChangeOwner = new JButton("Ändern"); - grid.add(new QLabel("Besitzer")); - grid.add(lblOwner).expand(true, false); - grid.add(btnChangeOwner).fill(true, false); - grid.nextRow(); + grdGeneral.add(new QLabel("Besitzer")); + grdGeneral.add(lblOwner, 3).expand(true, false); + grdGeneral.add(btnChangeOwner).fill(true, false); + grdGeneral.nextRow(); - // creation time - lblCreateTime = new QLabel(); - grid.add(new QLabel("Erstellt am")); - grid.add(lblCreateTime, 2); - grid.nextRow(); - - // last updater - lblUpdater = new PersonLabel(); - grid.add(new QLabel("Geändert durch")); - grid.add(lblUpdater, 2); - grid.nextRow(); - // last updated - lblUpdateTime = new QLabel(); - grid.add(new QLabel("Geändert am")); - grid.add(lblUpdateTime, 2); - grid.nextRow(); // linked image // name txtImageName = new QLabel(); - grid.add(new QLabel("Verknüpfte VM")); - grid.add(txtImageName).expand(true, false); + grdGeneral.add(new QLabel("Verknüpfte VM")); + grdGeneral.add(txtImageName, 3).expand(true, false); + // link button for image btnLinkImage = new JButton("Ändern"); - grid.add(btnLinkImage).fill(true, false); - grid.nextRow(); + grdGeneral.add(btnLinkImage).fill(true, false); + grdGeneral.nextRow(); // image version handling // auto update @@ -167,102 +239,148 @@ public abstract class LectureDetailsWindowLayout extends JDialog { if (item.isValid) { return FormatHelper.longDate(item.getCreateTime()); } else { - return FormatHelper.longDate(item.getCreateTime()) + " [ungültig]"; + return FormatHelper.longDate(item.getCreateTime()) + + " [ungültig]"; } } }); btnDownloadImage = new JButton("Download"); - btnDownloadImage.setToolTipText("Momentan verwendete Version herunterladen"); + btnDownloadImage + .setToolTipText("Momentan verwendete Version herunterladen"); JPanel versionPanel = new JPanel(new BorderLayout()); versionPanel.add(cboVersions, BorderLayout.CENTER); versionPanel.add(chkAutoUpdate, BorderLayout.SOUTH); - grid.add(new QLabel("VM-Version")); - grid.add(versionPanel); - grid.add(btnDownloadImage).fill(true, false); - grid.nextRow(); + grdGeneral.add(new QLabel("VM-Version")); + grdGeneral.add(versionPanel, 3); + grdGeneral.add(btnDownloadImage).fill(true, false); + grdGeneral.nextRow(); // start time of the lecture JPanel startTimePanel = new JPanel(); - startTimePanel.setLayout(new BoxLayout(startTimePanel, BoxLayout.LINE_AXIS)); - dtpStartDate = new QDatePickerImpl(new JDatePanelImpl(new UtilDateModel(new Date()), pickerStrings), + startTimePanel.setLayout(new BoxLayout(startTimePanel, + BoxLayout.LINE_AXIS)); + dtpStartDate = new QDatePickerImpl(new JDatePanelImpl( + new UtilDateModel(new Date()), pickerStrings), new DateLabelFormatter()); spnStartTime = makeTimeSpinner(23, 59); startTimePanel.add(dtpStartDate); startTimePanel.add(spnStartTime); startTimePanel.setMinimumSize(startTimePanel.getPreferredSize()); startTimePanel.setMaximumSize(startTimePanel.getPreferredSize()); - lblStartTime = new QLabel(); - grid.add(new QLabel("Startdatum")); - grid.add(startTimePanel, 2); - grid.nextRow(); + grdGeneral.add(new QLabel("Startdatum")); + grdGeneral.add(startTimePanel, 4); + grdGeneral.nextRow(); // end time of the lecture JPanel endTimePanel = new JPanel(); - endTimePanel.setLayout(new BoxLayout(endTimePanel, BoxLayout.LINE_AXIS)); - dtpEndDate = new QDatePickerImpl(new JDatePanelImpl(new UtilDateModel(new Date()), pickerStrings), - new DateLabelFormatter()); + endTimePanel + .setLayout(new BoxLayout(endTimePanel, BoxLayout.LINE_AXIS)); + dtpEndDate = new QDatePickerImpl(new JDatePanelImpl(new UtilDateModel( + new Date()), pickerStrings), new DateLabelFormatter()); spnEndTime = makeTimeSpinner(00, 59); endTimePanel.add(dtpEndDate); endTimePanel.add(spnEndTime); endTimePanel.setMinimumSize(endTimePanel.getPreferredSize()); endTimePanel.setMaximumSize(endTimePanel.getPreferredSize()); - - lblEndTime = new QLabel(); - grid.add(new QLabel("Enddatum")); - grid.add(endTimePanel, 2); - grid.nextRow(); - - // is exam - chkIsExam = new JCheckBox("Klausurmodus aktiv"); - grid.add(new JLabel("Beschränkungen")); - grid.add(chkIsExam); - btnAdvancedOptions = new JButton("Einstellungen"); - grid.add(btnAdvancedOptions); - grid.nextRow(); - - chkHasInternetAccess = new JCheckBox("Internetzugriff"); - grid.skip(); - grid.add(chkHasInternetAccess, 2); - grid.nextRow(); - - chkHasUsbAccess = new JCheckBox("USB-Zugriff"); - grid.skip(); - grid.add(chkHasUsbAccess, 2); - grid.nextRow(); - - chkIsActive = new JCheckBox("Aktiv"); - grid.skip(); - grid.add(chkIsActive, 2); - grid.nextRow(); - - // id - txtId = new JTextField(); - txtId.setEditable(false); - grid.add(new QLabel("ID")); - grid.add(txtId, 2).expand(true, false).fill(true, false); - grid.nextRow(); - - // use count - lblUseCount = new QLabel(); - grid.add(new QLabel("Aufrufe")); - grid.add(lblUseCount, 2); - grid.nextRow(); - - // button for the custom permissions - JPanel bottomButtonPanel = new JPanel(); - btnPermissions = new JButton("Berechtigungen"); - btnLocations = new JButton("Raumauswahl"); - grid.skip(); - bottomButtonPanel.add(btnPermissions); - bottomButtonPanel.add(btnLocations); - grid.add(bottomButtonPanel); - grid.skip(); - grid.nextRow(); - grid.finish(true); - - // finally add the infoPanel itself to the main view - add(infoPanel, BorderLayout.CENTER); - // button panel on the bottom + grdGeneral.add(new QLabel("Enddatum")); + grdGeneral.add(endTimePanel, 4); + grdGeneral.nextRow(); + + // lecture active + chkIsActive = new JCheckBox("Veranstaltung aktiv"); + grdGeneral.skip(); + grdGeneral.add(chkIsActive, 4); + grdGeneral.nextRow(); + grdGeneral.finish(true); + + pnlTabRestrictions = new JPanel(); + GridManager grdRestrictions = new GridManager(pnlTabRestrictions, 2, true, + new Insets(3, 3, 3, 3)); + + // "restrictions": internet access / usb access / exam + grdRestrictions.add(new JLabel("Internetzugriff")); + chkHasInternetAccess = new JCheckBox("zulassen"); + grdRestrictions.add(new WordWrapLabel("Legen Sie hier fest, ob die Veranstaltung Zugriff zum Internet haben soll", false, true)).fill(true, false).expand(true, false); + grdRestrictions.nextRow(); + grdRestrictions.skip(); + grdRestrictions.add(chkHasInternetAccess).fill(true, false).expand(true, false); + grdRestrictions.nextRow(); + + grdRestrictions.add(new JLabel("Speichermedium-Zugriff")); + chkHasUsbAccess = new JCheckBox("zulassen"); + grdRestrictions.add(new WordWrapLabel("Legen Sie hier fest, ob die Veranstaltung Zugriff Speichermedien (CD, USB, ...) haben soll", false, true)).fill(true, false).expand(true, false); + grdRestrictions.nextRow(); + grdRestrictions.skip(); + grdRestrictions.add(chkHasUsbAccess); + grdRestrictions.nextRow(); + + grdRestrictions.add(new JLabel("E-Prüfung")); + chkIsExam = new JCheckBox("aktiv"); + grdRestrictions.add(new WordWrapLabel("Gekennzeichnen Sie diese Veranstaltung als E-Prüfung", false, true)).fill(true, false).expand(true, false); + grdRestrictions.nextRow(); + grdRestrictions.skip(); + grdRestrictions.add(chkIsExam); + btnPermissions = new JButton("Ändern"); + grdRestrictions.finish(true); + + /* ******************************************************************************* + * + * Tab "Permissions" + * + ********************************************************************************/ + ctlPermissionManager = new LectureCustomPermissionManager(); + pnlTabPermissions = new JPanel(); + GridManager grdPermissions = new GridManager(pnlTabPermissions, 1, false); + JPanel defaultPermissionPane = new JPanel(); + defaultPermissionPane.setBorder(BorderFactory.createTitledBorder("Andere Nutzer")); + chkCustomPermEdit = new JCheckBox("Bearbeiten"); + chkCustomPermAdmin = new JCheckBox("Admin"); + defaultPermissionPane.add(chkCustomPermEdit); + defaultPermissionPane.add(chkCustomPermAdmin); + grdPermissions.add(ctlPermissionManager).fill(true, true).expand(true, true); + grdPermissions.nextRow(); + grdPermissions.add(defaultPermissionPane).fill(true, false).expand(true, false); + grdPermissions.finish(false); + + /* ******************************************************************************* + * + * Tab "Locations" + * + ********************************************************************************/ + ctlLocationSelector = new LocationSelector(); + pnlTabLocations = new JPanel(); + GridManager grdLocations = new GridManager(pnlTabLocations, 1, false); + grdLocations.add(ctlLocationSelector).fill(true, true).expand(true, true); + grdLocations.finish(false); + + /* ******************************************************************************* + * + * Tab "Advanced" + * + ********************************************************************************/ + ctlAdvancedConfigurator = new AdvancedConfigurator(); + pnlTabAdvanced = new JPanel(); + GridManager grdAdvanced = new GridManager(pnlTabAdvanced, 1, false); + grdAdvanced.add(ctlAdvancedConfigurator).fill(true, true).expand(true, true); + grdAdvanced.finish(false); + + /* ******************************************************************************* + * + * Main panel containing the tabs + * + ********************************************************************************/ + // finally build the tabbedPane and add it to the main view + pnlTabs = new JTabbedPane(); + pnlTabs.addTab("Übersicht", pnlTabInfo); + pnlTabs.addTab("Allgemein", pnlTabGeneral); + pnlTabs.addTab("Beschränkungen", pnlTabRestrictions); + if (MetaDataCache.getLocations() != null) { + pnlTabs.addTab("Raumauswahl", pnlTabLocations); + } + pnlTabs.addTab("Erweitert", pnlTabAdvanced); + pnlTabs.addTab("Berechtigungen", pnlTabPermissions); + add(pnlTabs, BorderLayout.CENTER); + // usage counter + button panel on the bottom JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); btnClose = new JButton("Schließen"); @@ -274,6 +392,7 @@ public abstract class LectureDetailsWindowLayout extends JDialog { buttonPanel.add(Box.createGlue()); buttonPanel.add(btnClose); buttonPanel.add(btnSaveChanges); + // buttons at the end of the page add(buttonPanel, BorderLayout.SOUTH); } @@ -286,7 +405,8 @@ public abstract class LectureDetailsWindowLayout extends JDialog { model.setValue(calendar.getTime()); JSpinner spinner = new JSpinner(model); JSpinner.DateEditor editor = new JSpinner.DateEditor(spinner, "HH:mm"); - DateFormatter formatter = (DateFormatter) editor.getTextField().getFormatter(); + DateFormatter formatter = (DateFormatter) editor.getTextField() + .getFormatter(); formatter.setAllowsInvalid(false); formatter.setOverwriteMode(true); spinner.setEditor(editor); 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 74801fc6..4ddf25e8 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 @@ -57,7 +57,7 @@ public class LocationSelectionWindowLayout extends JDialog { buttonPanel.add(btnSaveChanges); grid.add(buttonPanel).fill(true, false).expand(true, false); grid.finish(false); - setPreferredSize(Gui.getScaledDimension(600, 600)); + setPreferredSize(Gui.getScaledDimension(550, 500)); pack(); Gui.centerShellOverShell(modalParent, this); } |
