/* * 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.components.timeroptions; import static lazybones.gui.settings.DescriptionSelectorItem.TIMER; import static lazybones.gui.settings.DescriptionSelectorItem.TVB_DESC; import static lazybones.gui.settings.DescriptionSelectorItem.TVB_PREFIX; import static lazybones.gui.settings.DescriptionSelectorItem.VDR; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import lazybones.ChannelManager; import lazybones.ChannelManager.ChannelNotFoundException; import lazybones.LazyBones; import lazybones.LazyBonesTimer; import lazybones.RecordingManager; import lazybones.TimerManager; import lazybones.gui.components.daychooser.BrowseTextField; import lazybones.gui.components.daychooser.DayChooser; import lazybones.gui.components.historycombobox.SuggestingJHistoryComboBox; import lazybones.gui.components.timeroptions.TimerOptionsDialog.Mode; import lazybones.gui.settings.DescriptionComboBoxModel; import lazybones.gui.settings.DescriptionSelectorItem; import lazybones.programmanager.ProgramDatabase; import lazybones.programmanager.ProgramManager; import org.hampelratte.svdrp.responses.highlevel.Channel; import org.hampelratte.svdrp.responses.highlevel.Recording; import org.hampelratte.svdrp.responses.highlevel.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tvbrowser.core.ChannelList; import util.ui.Localizer; import com.thoughtworks.xstream.XStream; import devplugin.Plugin; import devplugin.Program; public class TimerOptionsEditor extends JPanel implements ActionListener, ItemListener, WindowListener, ChangeListener { private static transient Logger logger = LoggerFactory.getLogger(TimerOptionsEditor.class); private final JLabel lChannels = new JLabel(Localizer.getLocalization(Localizer.I18N_CHANNEL)); private final JComboBox<Object> channels = new JComboBox<Object>(); private final JLabel lDay = new JLabel(LazyBones.getTranslation("day", "Day")); private DayChooser dayChooser; private BrowseTextField day; private final JLabel lStarttime = new JLabel(LazyBones.getTranslation("start", "Start")); private SpinnerCalendarModel spinnerStarttimeModel;// = new SpinnerCalendarModel(); private JSpinner spinnerStarttime;// = new JSpinner(spinnerStarttimeModel); private final JLabel lEndtime = new JLabel(LazyBones.getTranslation("stop", "Stop")); private SpinnerCalendarModel spinnerEndtimeModel;// = new SpinnerCalendarModel(); private JSpinner spinnerEndtime; // = new JSpinner(spinnerEndtimeModel); private final JLabel lPriority = new JLabel(LazyBones.getTranslation("priority", "Priority")); private final JSpinner priority = new JSpinner(); private final JLabel lLifetime = new JLabel(LazyBones.getTranslation("lifetime", "Lifetime")); private final JSpinner lifetime = new JSpinner(); private final JLabel lTitle = new JLabel(LazyBones.getTranslation("title", "Title")); private final JTextField title = new JTextField(); private final JLabel lDescription = new JLabel(LazyBones.getTranslation("description", "Description")); private DescriptionComboBoxModel comboDescModel; private final JComboBox<DescriptionSelectorItem> comboDesc = new JComboBox<DescriptionSelectorItem>(); private final JTextArea description = new JTextArea(); private final JCheckBox cbActive = new JCheckBox(LazyBones.getTranslation("active", "Active")); private final JLabel lVpsTimeHint = new JLabel(LazyBones.getTranslation("vpsTimeHint", "Starttime has been changed for VPS")); private final JCheckBox cbVps = new JCheckBox(LazyBones.getTranslation("vps", "VPS")); private final JCheckBox cbSeries = new JCheckBox(LazyBones.getTranslation("series", "Series")); private final JLabel lDirectory = new JLabel(LazyBones.getTranslation("directory", "Directory")); private SuggestingJHistoryComboBox comboDirectory; private TimerManager timerManager; private RecordingManager recordingManager; /** * The actual timer */ private LazyBonesTimer timer; /** * A clone of the timer containing the old settings */ private LazyBonesTimer oldTimer; private Program prog; private final Mode mode; private String originalTitel = ""; private String originalPath = ""; public TimerOptionsEditor(TimerManager timerManager, RecordingManager recordingManager, LazyBonesTimer timer, Mode mode) { this.timerManager = timerManager; this.recordingManager = recordingManager; this.mode = mode; logger.debug("Creating timer options panel"); initGUI(); setTimer(timer); logger.debug("Timer options panel ready"); } public TimerOptionsEditor(TimerManager timerManager, RecordingManager recordingManager, Mode mode) { this.timerManager = timerManager; this.recordingManager = recordingManager; this.mode = mode; initGUI(); } private void initGUI() { setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridheight = 1; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 0; gbc.weighty = 0; int row = 0; gbc.gridx = 1; gbc.gridy = row++; gbc.gridwidth = 1; add(createCheckboxGrid(), gbc); gbc.gridx = 0; gbc.gridy = row++; gbc.gridwidth = 1; gbc.insets = new Insets(5, 5, 5, 5); add(lTitle, gbc); gbc.gridx = 0; gbc.gridy = row++; add(lDirectory, gbc); gbc.gridx = 0; gbc.gridy = row++; add(lChannels, gbc); gbc.gridx = 0; gbc.gridy = row++; add(lDay, gbc); gbc.gridx = 0; gbc.gridy = row++; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.SOUTHEAST; add(lVpsTimeHint, gbc); lVpsTimeHint.setForeground(Color.RED); lVpsTimeHint.setHorizontalAlignment(JLabel.TRAILING); lVpsTimeHint.setVisible(false); gbc.gridx = 0; gbc.gridy = row++; gbc.gridwidth = 1; gbc.anchor = GridBagConstraints.WEST; add(lStarttime, gbc); gbc.gridx = 0; gbc.gridy = row++; add(lEndtime, gbc); gbc.gridx = 0; gbc.gridy = row++; add(lPriority, gbc); gbc.gridx = 0; gbc.gridy = row++; add(lLifetime, gbc); gbc.gridx = 0; gbc.gridy = row++; add(lDescription, gbc); gbc.gridx = 0; gbc.gridy = row++; gbc.gridwidth = 2; gbc.weightx = 1; gbc.weighty = 1; gbc.fill = GridBagConstraints.BOTH; description.setRows(10); description.setLineWrap(true); description.setWrapStyleWord(true); add(new JScrollPane(description), gbc); gbc.gridwidth = 1; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1; gbc.weighty = 0; gbc.gridx = 1; row = 1; gbc.gridy = row++; add(title, gbc); comboDirectory = new SuggestingJHistoryComboBox(Arrays.asList("")); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.gridx = 1; gbc.gridy = row++; add(comboDirectory, gbc); gbc.gridx = 1; gbc.gridy = row++; add(channels, gbc); devplugin.Channel[] c = ChannelList.getSubscribedChannels(); for (int i = 0; i < c.length; i++) { channels.addItem(c[i]); } gbc.gridx = 1; gbc.gridy = row++; dayChooser = new DayChooser(); day = new BrowseTextField(dayChooser); day.setEditable(false); add(day, gbc); gbc.gridx = 1; row++; gbc.gridy = row++; spinnerStarttime = new JSpinner(); spinnerStarttime.addChangeListener(this); add(spinnerStarttime, gbc); gbc.gridx = 1; gbc.gridy = row++; spinnerEndtime = new JSpinner(); spinnerEndtime.addChangeListener(this); add(spinnerEndtime, gbc); gbc.gridx = 1; gbc.gridy = row++; add(priority, gbc); gbc.gridx = 1; gbc.gridy = row++; add(lifetime, gbc); gbc.gridx = 1; gbc.gridy = row++; add(comboDesc, gbc); comboDescModel = new DescriptionComboBoxModel(false, mode == Mode.UPDATE); comboDesc.setModel(comboDescModel); comboDesc.addItemListener(this); } private Component createCheckboxGrid() { JPanel checkboxGrid = new JPanel(new GridLayout(1, 3)); checkboxGrid.add(cbActive); cbVps.addActionListener(this); checkboxGrid.add(cbVps); cbSeries.addActionListener(this); checkboxGrid.add(cbSeries); return checkboxGrid; } public void setTimer(LazyBonesTimer timer) { logger.debug("Updating gui with timer values"); if (timer != null) { this.timer = timer; this.prog = null; this.oldTimer = (LazyBonesTimer) timer.clone(); lVpsTimeHint.setVisible(false); spinnerStarttime.setBorder(spinnerEndtime.getBorder()); spinnerStarttime.repaint(); // we have to remove the buffers again, to get the right start date // example: start time is 00.00 h with time buffers we have 23.45 // Calendar then decreases the start date, so that we don't have the right date, but // the date of the day before. LazyBonesTimer tmp = timer.getTimerWithoutBuffers(); Program prog = null; try { prog = ProgramDatabase.getProgram(tmp); this.prog = prog; } catch (ChannelNotFoundException e) { // fail silently } if (prog != null) { channels.setSelectedItem(prog.getChannel()); } else { Channel chan = ChannelManager.getInstance().getChannelByNumber(timer.getChannelNumber()); channels.addItem(chan); channels.setSelectedItem(chan); } // set the description if (mode == Mode.UPDATE) { description.setText(oldTimer.getDescription()); comboDescModel.setSelected(DescriptionSelectorItem.TIMER); } else { String descVdr = timer.getDescription() == null ? "" : timer.getDescription(); String selectedDescriptionId = LazyBones.getProperties().getProperty("descSourceTvb"); String descriptionText = LazyBonesTimer.createDescription(selectedDescriptionId, descVdr, prog); description.setText(descriptionText); comboDescModel.setSelected(selectedDescriptionId); } // set timer is active switch cbActive.setSelected(timer.isActive()); // set timer title title.setText(timer.getTitle()); // set path List<String> dirSuggestions = new ArrayList<String>(); // add directory suggestions from existing recordings List<Recording> recordings = recordingManager.getRecordings(); if (recordings != null) { // if the user is fast enough, the recordings might not have been loaded for (Recording recording : recordings) { // abuse Timer class to parse path and title Timer dummy = new Timer(); dummy.setFile(recording.getTitle()); if (!dummy.getPath().isEmpty()) { dirSuggestions.add(dummy.getPath()); } } } // add directory suggestions from upcoming timers List<LazyBonesTimer> timers = timerManager.getTimers(); for (LazyBonesTimer _timer : timers) { if (!_timer.getPath().isEmpty()) { dirSuggestions.add(_timer.getPath()); } } Collections.sort(dirSuggestions); Collections.reverse(dirSuggestions); for (String suggestion : dirSuggestions) { comboDirectory.addItem(suggestion); } // add suggestions from the default directory setting history XStream xstream = new XStream(); try { @SuppressWarnings("unchecked") List<String> defaultDirectoryHistory = (List<String>) xstream.fromXML(LazyBones.getProperties().getProperty("default.directory.history")); for (String dir : defaultDirectoryHistory) { comboDirectory.addItem(dir); } } catch (Exception e) { logger.warn("Couldn't load history of default directories", e); } comboDirectory.setText(timer.getPath().replace('~', '/')); // if title is EPISODE and path is TITLE, this is a series cbSeries.setSelected("TITLE".equals(timer.getPath()) && "EPISODE".equals(timer.getTitle())); // initialize the start and end time spinners spinnerStarttimeModel = new SpinnerCalendarModel(timer.getStartTime()); spinnerStarttime.setModel(spinnerStarttimeModel); SpinnerCalendarEditor editor = new SpinnerCalendarEditor(spinnerStarttime, spinnerStarttimeModel); spinnerStarttime.setEditor(editor); spinnerEndtimeModel = new SpinnerCalendarModel(timer.getEndTime()); spinnerEndtime.setModel(spinnerEndtimeModel); editor = new SpinnerCalendarEditor(spinnerEndtime, spinnerEndtimeModel); spinnerEndtime.setEditor(editor); priority.setModel(new SpinnerNumberModel(timer.getPriority(), 0, 99, 1)); lifetime.setModel(new SpinnerNumberModel(timer.getLifetime(), 0, 99, 1)); dayChooser.setTimer(timer); day.setText(timer.getDayString()); // set timer uses VPS switch if (mode == Mode.NEW) { boolean vpsDefault = Boolean.parseBoolean(LazyBones.getProperties().getProperty("vps.default")); cbVps.setSelected(vpsDefault); setVps(vpsDefault); } else { cbVps.setSelected(timer.hasState(Timer.VPS)); } } } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == cbVps) { setVps(cbVps.isSelected()); lVpsTimeHint.setVisible(true); spinnerStarttime.setBorder(BorderFactory.createLineBorder(Color.RED)); spinnerStarttime.repaint(); } else if (e.getSource() == cbSeries) { if (cbSeries.isSelected()) { originalTitel = title.getText(); originalPath = comboDirectory.getText(); title.setText("EPISODE"); comboDirectory.setText("TITLE"); } else { if (originalTitel.trim().isEmpty() && prog != null) { title.setText(prog.getTitle()); } else { title.setText(originalTitel); } comboDirectory.setText(originalPath); } } } private void setVps(boolean activated) { /* * to set the right vps time we use start time of the tvb program the start time of the vdr epg should be rather used, but it's to complicated for this * simple case */ if (activated) { if (prog != null) { // VPS needs the unbuffered start time timer.setStartTime(prog.getDate().getCalendar()); timer.getStartTime().set(Calendar.HOUR_OF_DAY, prog.getHours()); timer.getStartTime().set(Calendar.MINUTE, prog.getMinutes()); spinnerStarttimeModel.setValue(timer.getStartTime().clone()); day.setText(Integer.toString(prog.getDate().getDayOfMonth())); logger.debug("Setting start time to start time of the TVB-program {}", timer.getStartTime().getTime()); } else { logger.warn("No programm found to determine the VPS time"); } } else { if (oldTimer != null && !oldTimer.hasState(Timer.VPS)) { // set the timer to the previous startTime Calendar startTime = (Calendar) oldTimer.getStartTime().clone(); spinnerStarttimeModel.setValue(startTime); day.setText(Integer.toString(oldTimer.getStartTime().get(Calendar.DAY_OF_MONTH))); timer.setStartTime(startTime); } else { if (prog != null) { Calendar calStart = prog.getDate().getCalendar(); calStart.set(Calendar.HOUR_OF_DAY, prog.getHours()); calStart.set(Calendar.MINUTE, prog.getMinutes()); // start the recording x min before the beginning of the program int bufferBefore = Integer.parseInt(LazyBones.getProperties().getProperty("timer.before")); calStart.add(Calendar.MINUTE, -bufferBefore); timer.setStartTime(calStart); spinnerStarttimeModel.setValue(timer.getStartTime()); } } } } public LazyBonesTimer getOldTimer() { return oldTimer; } public LazyBonesTimer getTimer() { timer.setTitle(title.getText()); timer.setPath(comboDirectory.getText().trim().replace('/', '~')); Channel vdrc = null; Object selected = channels.getSelectedItem(); if (selected instanceof devplugin.Channel) { devplugin.Channel c = (devplugin.Channel) selected; vdrc = ChannelManager.getChannelMapping().get(c.getId()); } else if (selected instanceof Channel) { vdrc = (Channel) selected; } timer.setChannelNumber(vdrc.getChannelNumber()); timer.setPriority(((Integer) priority.getValue()).intValue()); timer.setLifetime(((Integer) lifetime.getValue()).intValue()); timer.setDescription(description.getText()); timer.changeStateTo(Timer.ACTIVE, cbActive.isSelected()); timer.changeStateTo(Timer.VPS, cbVps.isSelected()); timer.getStartTime().set(Calendar.HOUR_OF_DAY, ((Calendar) spinnerStarttimeModel.getValue()).get(Calendar.HOUR_OF_DAY)); timer.getStartTime().set(Calendar.MINUTE, ((Calendar) spinnerStarttimeModel.getValue()).get(Calendar.MINUTE)); timer.getStartTime().set(Calendar.SECOND, 0); timer.getEndTime().set(Calendar.HOUR_OF_DAY, ((Calendar) spinnerEndtimeModel.getValue()).get(Calendar.HOUR_OF_DAY)); timer.getEndTime().set(Calendar.MINUTE, ((Calendar) spinnerEndtimeModel.getValue()).get(Calendar.MINUTE)); timer.getEndTime().set(Calendar.SECOND, 0); return timer; } @Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { DescriptionSelectorItem selectedDescription = (DescriptionSelectorItem) e.getItem(); if (selectedDescription.getId().equals(TIMER)) { description.setText(oldTimer.getDescription()); } else if (selectedDescription.getId().equals(VDR)) { Program prog = Plugin.getPluginManager().getProgram(timer.getTvBrowserProgIDs().get(0)); Calendar tmpCal = (Calendar) timer.getStartTime().clone(); tmpCal.set(Calendar.HOUR_OF_DAY, prog.getHours()); tmpCal.set(Calendar.MINUTE, prog.getMinutes()); tmpCal.add(Calendar.MINUTE, prog.getLength() / 2); // take the middle of the playtime to be sure, that the right program is chosen getParent().setCursor(new Cursor(Cursor.WAIT_CURSOR)); Timer tmp = ProgramManager.getInstance().getTimerForTime(tmpCal, prog.getChannel()); String desc = tmp == null ? "" : tmp.getDescription(); description.setText(desc); getParent().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } else if (selectedDescription.getId().equals(TVB_DESC)) { Program prog = null; if (this.prog != null) { prog = this.prog; } else { prog = Plugin.getPluginManager().getProgram(timer.getTvBrowserProgIDs().get(0)); } if (prog != null && prog.getDescription() != null) { description.setText(prog.getDescription()); } else { description.setText(""); } } else if (selectedDescription.getId().startsWith(TVB_PREFIX)) { Program prog = null; if (this.prog != null) { prog = this.prog; } else { prog = Plugin.getPluginManager().getProgram(timer.getTvBrowserProgIDs().get(0)); } String desc = LazyBonesTimer.createDescription(selectedDescription.getId(), "", prog); description.setText(desc); } } } @Override public void windowActivated(WindowEvent e) { } @Override public void windowClosed(WindowEvent e) { } @Override public void windowClosing(WindowEvent e) { } @Override public void windowDeactivated(WindowEvent e) { } @Override public void windowDeiconified(WindowEvent e) { } @Override public void windowIconified(WindowEvent e) { } @Override public void windowOpened(WindowEvent e) { title.requestFocus(); } @Override public void stateChanged(ChangeEvent e) { if (e.getSource() == spinnerStarttime) { Calendar startTime = (Calendar) spinnerStarttimeModel.getValue(); day.setText(Integer.toString(startTime.get(Calendar.DAY_OF_MONTH))); } } }