/*
* Copyright (c) Henrik Niehaus
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the project (Lazy Bones) nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package lazybones.gui.timers;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
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.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.hampelratte.svdrp.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lazybones.ChannelManager.ChannelNotFoundException;
import lazybones.LazyBones;
import lazybones.LazyBonesTimer;
import lazybones.RecordingManager;
import lazybones.TimerManager;
import lazybones.VDRCallback;
import lazybones.actions.ModifyTimerAction;
import lazybones.actions.VDRAction;
import lazybones.gui.components.timeroptions.TimerOptionsDialog.Mode;
import lazybones.gui.components.timeroptions.TimerOptionsEditor;
import lazybones.gui.components.timeroptions.TimerOptionsView;
import lazybones.programmanager.ProgramManager;
public class TimerManagerPanel extends JPanel implements ActionListener, ListSelectionListener, ListDataListener {
private static transient Logger logger = LoggerFactory.getLogger(TimerManagerPanel.class);
private JScrollPane scrollPane = null;
private final JList<LazyBonesTimer> timerList = new JList<LazyBonesTimer>();
private JToolBar buttonBar = null;
private JButton buttonNew = null;
private JButton buttonEdit = null;
private JButton buttonSync = null;
private JButton buttonRemove = null;
private JButton buttonSave = null;
private JButton buttonCancel = null;
private final CardLayout timerOptionsLayout = new CardLayout();
private final JPanel timerOptionsPanel = new JPanel(timerOptionsLayout);
private final TimerOptionsView timerOptionsView = new TimerOptionsView();
private final TimerOptionsEditor timerOptionsEditor;
private TimerManager timerManager;
public TimerManagerPanel(TimerManager timerManager, RecordingManager recordingManager) {
this.timerManager = timerManager;
timerOptionsEditor = new TimerOptionsEditor(timerManager, recordingManager, Mode.UPDATE);
initGUI();
}
/**
* This method initializes the GUI
*
*/
private void initGUI() {
this.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// add toolbar
gbc.fill = java.awt.GridBagConstraints.NONE;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.insets = new java.awt.Insets(5, 10, 5, 10);
gbc.anchor = GridBagConstraints.WEST;
buttonBar = createButtonBar();
this.add(buttonBar, gbc);
// add timer list
gbc.fill = java.awt.GridBagConstraints.BOTH;
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 0.33;
gbc.weighty = 1;
gbc.gridwidth = 1;
gbc.insets = new java.awt.Insets(0, 10, 10, 10);
timerList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
timerList.setCellRenderer(new TimerListCellRenderer());
timerList.addListSelectionListener(this);
timerList.setModel(new TimerListAdapter());
timerList.getModel().addListDataListener(this);
scrollPane = new JScrollPane(timerList);
scrollPane.setPreferredSize(new Dimension(300, 0));
scrollPane.setMinimumSize(new Dimension(200, 0));
this.add(scrollPane, gbc);
// add cardlayout for timer details view and timer editor
gbc.gridx = 1;
gbc.gridy = 1;
gbc.weightx = 0.66;
gbc.insets = new java.awt.Insets(0, 0, 5, 10);
timerOptionsView.setEnabled(false);
timerOptionsPanel.add(timerOptionsView, "VIEW");
timerOptionsPanel.add(timerOptionsEditor, "EDITOR");
timerOptionsEditor.setPreferredSize(new Dimension(300, 0));
timerOptionsPanel.setPreferredSize(new Dimension(300, 0));
this.add(timerOptionsPanel, gbc);
timerList.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
mayTriggerPopup(e);
}
@Override
public void mouseReleased(MouseEvent e) {
mayTriggerPopup(e);
}
private void mayTriggerPopup(MouseEvent e) {
int index = timerList.locationToIndex(e.getPoint());
LazyBonesTimer timer = timerList.getModel().getElementAt(index);
if (e.isPopupTrigger()) {
JPopupMenu popup = ProgramManager.getInstance().getContextMenuForTimer(timer);
popup.setLocation(e.getPoint());
popup.show(e.getComponent(), e.getX(), e.getY());
} else if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() >= 2) {
ProgramManager.getInstance().handleTimerDoubleClick(timer, e);
}
}
});
timerList.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DELETE) {
LazyBonesTimer timer = timerList.getSelectedValue();
if (timer != null) {
deleteTimer(timer);
}
}
}
});
}
private JToolBar createButtonBar() {
JToolBar buttonBar = new JToolBar();
buttonBar.setBorder(BorderFactory.createEmptyBorder());
buttonBar.setRollover(true);
buttonBar.setFloatable(false);
buttonSync = new JButton();
buttonSync.setToolTipText(LazyBones.getTranslation("resync", "Synchronize"));
buttonSync.setIcon(LazyBones.getInstance().createImageIcon("action", "view-refresh", 22));
buttonSync.addActionListener(this);
buttonBar.add(buttonSync);
buttonNew = new JButton();
buttonNew.setToolTipText(LazyBones.getTranslation("new_timer", "New Timer"));
buttonNew.setIcon(LazyBones.getInstance().createImageIcon("action", "document-new", 22));
buttonNew.addActionListener(this);
buttonBar.add(buttonNew);
buttonBar.addSeparator();
buttonSave = new JButton();
buttonSave.setToolTipText(LazyBones.getTranslation("save", "Update Timer"));
buttonSave.setIcon(LazyBones.getInstance().createImageIcon("action", "document-save", 22));
buttonSave.addActionListener(this);
buttonSave.setVisible(false);
buttonBar.add(buttonSave);
buttonCancel = new JButton();
buttonCancel.setToolTipText(LazyBones.getTranslation("cancel", "Cancel editing"));
buttonCancel.setIcon(LazyBones.getInstance().createImageIcon("action", "process-stop", 22));
buttonCancel.addActionListener(this);
buttonCancel.setVisible(false);
buttonBar.add(buttonCancel);
buttonEdit = new JButton();
buttonEdit.setToolTipText(LazyBones.getTranslation("edit", "Edit Timer"));
buttonEdit.setIcon(LazyBones.getInstance().createImageIcon("action", "document-edit", 22));
buttonEdit.addActionListener(this);
buttonEdit.setEnabled(false);
buttonBar.add(buttonEdit);
buttonRemove = new JButton();
buttonRemove.setToolTipText(LazyBones.getTranslation("dont_capture", "Delete Timer"));
buttonRemove.setIcon(LazyBones.getInstance().createImageIcon("action", "edit-delete", 22));
buttonRemove.addActionListener(this);
buttonRemove.setEnabled(false);
buttonBar.add(buttonRemove);
return buttonBar;
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttonNew) {
try {
cancelEditing();
timerManager.createTimerFromScratch();
} catch (ChannelNotFoundException ex) {
logger.error("An error occured", ex);
}
} else if (e.getSource() == buttonEdit) {
if (timerList.getSelectedIndex() >= 0) {
LazyBonesTimer timer = timerList.getSelectedValue();
timerOptionsEditor.setTimer(timer);
startEditing();
}
} else if (e.getSource() == buttonRemove) {
if (timerList.getSelectedIndex() >= 0) {
LazyBonesTimer timer = timerList.getSelectedValue();
deleteTimer(timer);
}
} else if (e.getSource() == buttonSync) {
cancelEditing();
timerManager.synchronize();
} else if (e.getSource() == buttonCancel) {
cancelEditing();
timerManager.synchronize();
} else if (e.getSource() == buttonSave) {
LazyBonesTimer oldTimer = timerOptionsEditor.getOldTimer();
LazyBonesTimer changedTimer = timerOptionsEditor.getTimer();
ModifyTimerAction mta = new ModifyTimerAction(changedTimer, oldTimer);
mta.setCallback(new VDRCallback<VDRAction>() {
@Override
public void receiveResponse(VDRAction cmd, Response response) {
timerManager.synchronize();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
cancelEditing();
}
});
if (!cmd.isSuccess()) {
String mesg = LazyBones.getTranslation("couldnt_change", "Couldn\'t change timer:") + " " + cmd.getResponse().getMessage();
logger.error(mesg);
}
}
});
mta.enqueue();
}
}
private void startEditing() {
timerOptionsLayout.show(timerOptionsPanel, "EDITOR");
buttonSave.setVisible(true);
buttonCancel.setVisible(true);
buttonEdit.setVisible(false);
buttonSync.setEnabled(false);
buttonNew.setEnabled(false);
buttonRemove.setEnabled(false);
}
private void cancelEditing() {
buttonSave.setVisible(false);
buttonCancel.setVisible(false);
buttonEdit.setVisible(true);
buttonSync.setEnabled(true);
buttonNew.setEnabled(true);
buttonRemove.setEnabled(true);
timerOptionsLayout.show(timerOptionsPanel, "VIEW");
}
private void deleteTimer(LazyBonesTimer timer) {
timerList.setEnabled(false);
buttonNew.setEnabled(false);
buttonEdit.setEnabled(false);
buttonSync.setEnabled(false);
buttonRemove.setEnabled(false);
final boolean requestFocus = timerList.hasFocus();
timerManager.deleteTimer(timer, new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
timerList.setEnabled(true);
buttonSync.setEnabled(true);
buttonNew.setEnabled(true);
if (requestFocus) {
timerList.requestFocus();
}
}
});
}
});
}
@Override
public void valueChanged(ListSelectionEvent e) {
updateGuiState();
}
private synchronized void updateGuiState() {
try {
cancelEditing();
int selectedIndex = timerList.getSelectedIndex();
if (selectedIndex >= 0 && timerList.getModel().getSize() - 1 >= selectedIndex) {
LazyBonesTimer timer = timerList.getModel().getElementAt(selectedIndex);
timerOptionsView.setTimer(timer);
buttonEdit.setEnabled(true);
buttonRemove.setEnabled(true);
} else {
timerOptionsView.reset();
buttonEdit.setEnabled(false);
buttonRemove.setEnabled(false);
}
} catch (RuntimeException t) {
logger.error(t.getMessage(), t);
throw t;
}
}
private class TimerComparator implements Comparator<LazyBonesTimer> {
@Override
public int compare(LazyBonesTimer t1, LazyBonesTimer t2) {
return t1.getStartTime().compareTo(t2.getStartTime());
}
}
private class TimerListAdapter extends AbstractListModel<LazyBonesTimer> implements Observer {
private List<LazyBonesTimer> timers;
public TimerListAdapter() {
timerManager.addObserver(this);
timers = timerManager.getTimers();
Collections.sort(timers, new TimerComparator());
}
@Override
public int getSize() {
return timers != null ? timers.size() : 0;
}
@Override
public LazyBonesTimer getElementAt(int index) {
return timers.get(index);
}
@Override
public void update(Observable o, Object arg) {
timers = timerManager.getTimers();
Collections.sort(timers, new TimerComparator());
fireContentsChanged(this, 0, timers.size() - 1);
}
@Override
protected void fireContentsChanged(Object source, int start, int end) {
super.fireContentsChanged(source, start, end);
}
}
@Override
public void intervalAdded(ListDataEvent e) {
updateGuiState();
}
@Override
public void intervalRemoved(ListDataEvent e) {
updateGuiState();
}
@Override
public void contentsChanged(ListDataEvent e) {
updateGuiState();
}
}