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<ListModel<LectureSummary>, Integer>() {
public boolean include(Entry<? extends ListModel<LectureSummary>, ? 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<ListModel<LectureSummary>, Integer>() {
public boolean include(Entry<? extends ListModel<LectureSummary>, ? extends Integer> entry) {
LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier());
return lecture.isEnabled && lecture.endTime > Util.unixTime();
}
}),
EDITABLE("Nur editierbare anzeigen", new RowFilter<ListModel<LectureSummary>, Integer>() {
public boolean include(Entry<? extends ListModel<LectureSummary>, ? extends Integer> entry) {
LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier());
return LecturePerms.canEdit(lecture);
}
}),
EXPIRING("Bald auslaufende anzeigen", new RowFilter<ListModel<LectureSummary>, Integer>() {
public boolean include(Entry<? extends ListModel<LectureSummary>, ? 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<ListModel<LectureSummary>, Integer>() {
public boolean include(Entry<? extends ListModel<LectureSummary>, ? extends Integer> entry) {
LectureSummary lecture = entry.getModel().getModelRow(entry.getIdentifier());
return lecture.isExam;
}
});
private final String name;
private final RowFilter<ListModel<LectureSummary>, Integer> filter;
private FilterType(String name, RowFilter<ListModel<LectureSummary>, Integer> filter) {
this.name = name;
this.filter = filter;
}
@Override
public String toString() {
return name;
}
}
private Pattern searchFieldPattern = null;
private final RowFilter<ListModel<LectureSummary>, Integer> filterSearchTerm = new RowFilter<ListModel<LectureSummary>, Integer>() {
public boolean include(Entry<? extends ListModel<LectureSummary>, ? 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<LectureSummary> lectureList) {
ThriftActions.deleteLecture(JOptionPane.getFrameForComponent(me), lectureList,
new DeleteLectureCallback() {
@Override
public void deleted(Map<LectureSummary, TException> 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<RowFilter<ListModel<LectureSummary>, 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<ListModel<LectureSummary>, 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<LectureSummary> 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<ListModel<LectureSummary>, Integer> imageFilter = new RowFilter<ListModel<LectureSummary>, Integer>() {
public boolean include(Entry<? extends ListModel<LectureSummary>, ? 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());
}
}