package org.openslx.dozmod.gui.window; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.RowFilter; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.apache.log4j.Logger; import org.apache.thrift.TException; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.bwlp.thrift.iface.ImageVersionDetails; import org.openslx.bwlp.thrift.iface.LectureSummary; 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.table.ListTable.ListModel; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.gui.helper.PopupMenu; import org.openslx.dozmod.gui.helper.TextChangeListener; import org.openslx.dozmod.gui.window.LectureDetailsWindow.LectureUpdatedCallback; import org.openslx.dozmod.gui.window.layout.LectureListWindowLayout; import org.openslx.dozmod.gui.wizard.LectureWizard; import org.openslx.dozmod.permissions.LecturePerms; import org.openslx.dozmod.thrift.ImageLocalDetailsActions; import org.openslx.dozmod.thrift.Session; import org.openslx.dozmod.thrift.ThriftActions; import org.openslx.dozmod.thrift.ThriftActions.DeleteLectureCallback; import org.openslx.dozmod.thrift.cache.LectureCache; import org.openslx.dozmod.thrift.cache.UserCache; import org.openslx.util.QuickTimer; import org.openslx.util.QuickTimer.Task; import org.openslx.util.Util; /** * Window for displaying a table with the available lectures. * Lectures can be created and deleted here. */ @SuppressWarnings("serial") public class LectureListWindow extends LectureListWindowLayout { private final static Logger LOGGER = Logger.getLogger(LectureListWindow.class); public final LectureListWindow me = this; // Filter types as an enum public enum FilterType { ALL("Alle anzeigen", null), OWN("Nur eigene/zugewiesene anzeigen", new RowFilter, Integer>() { public boolean include(Entry, ? extends Integer> entry) { LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier()); return lecture.userPermissions.admin || lecture.ownerId.equals(Session.getUserId()); } }), ACTIVE("Nur aktive anzeigen", new RowFilter, Integer>() { public boolean include(Entry, ? extends Integer> entry) { LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier()); return lecture.isEnabled && lecture.endTime > Util.unixTime(); } }), EDITABLE("Nur editierbare anzeigen", new RowFilter, Integer>() { public boolean include(Entry, ? extends Integer> entry) { LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier()); return LecturePerms.canEdit(lecture); } }), EXPIRING("Bald auslaufende anzeigen", new RowFilter, Integer>() { public boolean include(Entry, ? extends Integer> entry) { LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier()); return Math.abs(lecture.endTime - Util.unixTime()) < 86400 * 7; } }), EXAMS("Prüfungsveranstaltungen anzeigen", new RowFilter, Integer>() { public boolean include(Entry, ? extends Integer> entry) { LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier()); return lecture.isExam; } }); private final String name; private final RowFilter, Integer> filter; private FilterType(String name, RowFilter, Integer> filter) { this.name = name; this.filter = filter; } @Override public String toString() { return name; } } private Pattern searchFieldPattern = null; private final RowFilter, Integer> filterSearchTerm = new RowFilter, Integer>() { public boolean include(Entry, ? extends Integer> entry) { LectureSummary lecture = tblLectures.getModelRow(entry.getIdentifier()); if (searchFieldPattern.matcher(lecture.lectureName).find()) return true; UserInfo user = UserCache.find(lecture.ownerId); if (user == null) return false; if (searchFieldPattern.matcher(user.firstName).find()) return true; if (searchFieldPattern.matcher(user.lastName).find()) return true; if (searchFieldPattern.matcher(user.eMail).find()) return true; return false; } }; /** * Popup menu items */ private JMenuItem popupItemNew = new JMenuItem("Neu"); private JMenuItem popupItemEdit = new JMenuItem("Detailansicht"); private JMenuItem popupItemLinked = new JMenuItem("VM-Details anzeigen"); private JMenuItem popupItemDownload = new JMenuItem("Verlinkte VM herunterladen"); private JMenuItem popupItemDelete = new JMenuItem("Löschen"); public LectureListWindow() { super(); tblLectures.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); // filter the objects in the table depending on the search field txtSearch.getDocument().addDocumentListener(new TextChangeListener() { @Override public void changed() { String str = txtSearch.getText(); if (str == null || str.isEmpty()) { searchFieldPattern = null; } else { try { searchFieldPattern = Pattern.compile(str, Pattern.CASE_INSENSITIVE); txtSearch.setForeground(UIManager.getColor("TextField.foreground")); } catch (PatternSyntaxException ex) { txtSearch.setForeground(Color.RED); } } applyFilterOnTable(); } }); cboFilter.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { txtSearch.setEnabled(true); applyFilterOnTable(); } }); btnNewLecture.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { new LectureWizard(SwingUtilities.getWindowAncestor(me), null, null).setVisible(true); } }); btnEditLecture.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openLectureDetails(tblLectures.getSelectedItem()); } }); // delete lecture btnDeleteLecture.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { deleteLectures(tblLectures.getSelectedItems()); } }); // return to mainMenu btnSwitchView.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { MainWindow.showPage(ImageListWindow.class); } }); /** * Popup menu for the version table on the right side */ final PopupMenu pop = new PopupMenu(new ActionListener() { public void actionPerformed(ActionEvent e) { LectureSummary lecture = tblLectures.getSelectedItem(); if (lecture == null) return; if (e.getSource().equals(popupItemNew)) { if (Gui.showMessageBox(me, "Um eine Veranstaltung zu erstellen, " + "müssen Sie eine VM auswählen. Zur VM-Auswahl wechseln?", MessageType.QUESTION_YESNO, LOGGER, null)) { MainWindow.showPage(ImageListWindow.class); } } if (e.getSource().equals(popupItemEdit)) { openLectureDetails(lecture); } if (e.getSource().equals(popupItemDownload)) { //TODO fix for students, who cannot see imageDetails somehow get filesize from server. ImageDetailsRead image = ThriftActions.getImageDetails( JOptionPane.getFrameForComponent(me), lecture.imageBaseId); if (image != null) { long versionSize = -1; for (ImageVersionDetails version : image.getVersions()) { if (version.versionId.equals(lecture.imageVersionId)) { versionSize = version.fileSize; break; } } if (versionSize != -1) { ThriftActions.initDownload(JOptionPane.getFrameForComponent(me), lecture.imageVersionId, image.imageName, image.virtId, image.osId, versionSize, null); } } } if (e.getSource().equals(popupItemLinked)) { ImageDetailsWindow.open(JOptionPane.getFrameForComponent(me), lecture.imageBaseId, null, new ImageLocalDetailsActions(JOptionPane.getFrameForComponent(me))); } if (e.getSource().equals(popupItemDelete)) { deleteLectures(tblLectures.getSelectedItems()); } } }); pop.addMenuItem(popupItemNew); pop.addMenuItem(popupItemEdit); pop.addMenuItem(popupItemLinked); pop.addMenuItem(popupItemDownload); pop.addSeparator(); pop.addMenuItem(popupItemDelete); // finally the table listeners tblLectures.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { if (!Session.canListImages()) { Gui.showMessageBox("Ihnen fehlen die benötigten Berechtigungen.", MessageType.ERROR, LOGGER, null); return; } openLectureDetails(tblLectures.getSelectedItem()); } processClick(e); } @Override public void mousePressed(MouseEvent e) { processClick(e); } @Override public void mouseReleased(MouseEvent e) { processClick(e); } private void processClick(MouseEvent e) { // rowIndex at mouse cursor int mouseRowIndex = tblLectures.rowAtPoint(e.getPoint()); // is the click event on an already selected row? boolean alreadySelectedRow = false; for (int i : tblLectures.getSelectedRows()) { if (i == mouseRowIndex) { alreadySelectedRow = true; break; } } if (mouseRowIndex >= 0 && mouseRowIndex < tblLectures.getRowCount() && SwingUtilities.isRightMouseButton(e)) { // select row if it wasn't in selection before if (!alreadySelectedRow) { tblLectures.setRowSelectionInterval(mouseRowIndex, mouseRowIndex); } if (e.isPopupTrigger()) { pop.show(e.getComponent(), e.getX(), e.getY()); } } } }); tblLectures.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_F5) { refreshList(true); } } }); tblLectures.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { LectureSummary item = tblLectures.getSelectedItem(); updateAvailableOptions(item); } }); if (Session.canListImages()) { cboFilter.setSelectedItem(FilterType.OWN); } else { cboFilter.setSelectedItem(FilterType.ALL); } updateAvailableOptions(null); // Some keyboard shortcuts tblLectures.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "details"); tblLectures.getActionMap().put("details", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { btnEditLecture.doClick(); } }); tblLectures.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete"); tblLectures.getActionMap().put("delete", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { btnDeleteLecture.doClick(); } }); } /** * Updates the buttons/popup menu items according to the user's permissions, * status and number of selections * * @param lecture the lecture to check the user's permissions for, can be * null */ private void updateAvailableOptions(LectureSummary lecture) { boolean admin = LecturePerms.canAdmin(lecture); boolean singleSelection = !(tblLectures.getSelectedRows().length > 1); btnEditLecture.setEnabled(Session.canListImages() && singleSelection); btnDeleteLecture.setEnabled(admin || !singleSelection); popupItemDelete.setEnabled(admin || !singleSelection); popupItemLinked.setEnabled(Session.canListImages() && singleSelection); popupItemDownload.setEnabled(Session.canListImages() && singleSelection); popupItemNew.setEnabled(Session.canListImages() && singleSelection); popupItemEdit.setEnabled(Session.canListImages() && singleSelection); } /** * Opens the given lecture's details as a popup window */ private void openLectureDetails(final LectureSummary lecture) { if (lecture == null) return; LectureDetailsWindow.open((JFrame) SwingUtilities.getWindowAncestor(me), lecture.getLectureId(), new LectureUpdatedCallback() { @Override public void updated(boolean success) { refreshList(success); } }); } /** * Delete a list of lectures and display the lectures, which couldn't be * deleted. * * @param lectureList the lectures to be deleted. */ private void deleteLectures(List lectureList) { ThriftActions.deleteLecture(JOptionPane.getFrameForComponent(me), lectureList, new DeleteLectureCallback() { @Override public void deleted(Map fLectures) { refreshList(true); if (fLectures.size() > 0) { StringBuilder builder = new StringBuilder(); for (LectureSummary lecture : fLectures.keySet()) { builder.append("\n\n"); builder.append(lecture.lectureName + " \n" ); builder.append("Begründung: " + fLectures.get(lecture).getLocalizedMessage()); LOGGER.error("Couldn't delete lecture " + lecture.lectureName + ", reason:", fLectures.get(lecture) ); // TODO Should the exception be printed in the debugger or is this to much information? } Gui.showMessageBox("Folgende Veranstaltungen konnten nicht gelöscht werden: " + builder.toString(), MessageType.INFO, LOGGER, null); } } }); } /** * Applies the filter entered in the search field to the table */ private void applyFilterOnTable() { if (cboFilter.getSelectedIndex() == -1) return; // Filter list we are about to fill List, Integer>> filters = new ArrayList<>(); // string for text in filter text field if (searchFieldPattern != null) { filters.add(filterSearchTerm); } // filters from the combobox FilterType filterType = (FilterType) cboFilter.getSelectedItem(); tblLectures.setHighlightOwn(true); if (filterType != null && filterType.filter != null) { filters.add(filterType.filter); } // for using multiple filters RowFilter, Integer> andFilters = RowFilter.andFilter(filters); tblLectures.getRowSorter().setRowFilter(andFilters); setLectureCountLabel(tblLectures.getRowCount()); } private void refreshList(final boolean force) { QuickTimer.scheduleOnce(new Task() { @Override public void fire() { final List lectureList = LectureCache.get(force); Gui.asyncExec(new Runnable() { @Override public void run() { tblLectures.setData(lectureList, true); setLectureCountLabel(tblLectures.getRowCount()); } }); } }); } @Override public boolean requestHide() { if (cboFilter.getSelectedIndex() == -1) { cboFilter.setSelectedIndex(0); } return true; } @Override public void requestShow() { // en-/disable buttons when student btnSwitchView.setEnabled(Session.canListImages()); btnNewLecture.setEnabled(Session.canListImages()); updateAvailableOptions(null); refreshList(false); } public void filterByImageBaseId(final String imageBaseId) { if (imageBaseId == null) return; RowFilter, Integer> imageFilter = new RowFilter, Integer>() { public boolean include(Entry, ? extends Integer> entry) { LectureSummary lecture = tblLectures.getModelRow(entry.getIdentifier()); return imageBaseId.equals(lecture.imageBaseId); } }; cboFilter.setSelectedIndex(-1); txtSearch.setText(""); txtSearch.setEnabled(false); tblLectures.getRowSorter().setRowFilter(imageFilter); setLectureCountLabel(tblLectures.getRowCount()); } }