package chatty.gui.components.admin; import chatty.gui.MainGui; import static chatty.gui.components.admin.AdminDialog.hideableLabel; import static chatty.gui.components.admin.AdminDialog.makeGbc; import chatty.gui.components.settings.DurationSetting; import chatty.util.DateTime; import chatty.util.api.TwitchApi; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.swing.ButtonGroup; import javax.swing.ButtonModel; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JToggleButton; /** * * @author tduva */ public class CommercialPanel extends JPanel { /** * After a commercial was attempted to run/the result was returned wait for * this long before displaying new data of a scheduled commercial. */ private static final int COMMERCIAL_RUN_ATTEMPT_DELAY = 5*1000; // Saves last commercial run time for several channels (although it's // probably unncessary, at least this way another currentChannel can be opened in // the dialog and it's still saved) private final Map<String, Long> lastCommercial = new HashMap<>(); private final static int[] commercialButtonsDef = {30,60,90,120,180}; private final Map<Integer, JToggleButton> commercialButtons = new LinkedHashMap<>(); private final JLabel commercialResult; private final JLabel lastCommercialInfo = new JLabel(""); private final JCheckBox useCommercialDelay = new JCheckBox("Use delay: "); private final JCheckBox repeatCommercial = new JCheckBox("Repeat"); private final DurationSetting commercialDelay = new DurationSetting(3, true); private final MainGui main; private String currentChannel; private long lastCommercialRun; private long scheduledCommercialTime; private int scheduledCommercialLength; private long lastCommercialRunAttempt; public CommercialPanel(MainGui main) { GridBagConstraints gbc; this.main = main; setLayout(new GridBagLayout()); gbc = makeGbc(0,1,1,1); add(new JLabel("Run commercial: "), gbc); gbc = makeGbc(1,1,4,1); gbc.insets = new Insets(5,5,0,5); add(createCommercialButtons(), gbc); gbc = makeGbc(1,2,1,1); gbc.insets = new Insets(0,5,5,5); add(useCommercialDelay, gbc); gbc = makeGbc(2,2,1,1); gbc.insets = new Insets(0,5,5,5); gbc.anchor = GridBagConstraints.WEST; add(commercialDelay, gbc); gbc = makeGbc(3,2,1,1); gbc.insets = new Insets(0,5,5,5); add(repeatCommercial, gbc); gbc = makeGbc(4,2,1,1); gbc.insets = new Insets(0,10,5,5); add(lastCommercialInfo, gbc); commercialResult = new JLabel("..."); gbc = makeGbc(0,3,5,1); gbc.insets = new Insets(3,5,15,5); add(commercialResult, gbc); setCommercialResult(""); } public boolean checkOnClose() { if (scheduledCommercialTime != 0) { int result = confirmContinueScheduledOnClose(); if (result == JOptionPane.CLOSED_OPTION) { return false; } if (result == 1) { clearScheduledCommercial(); } } return true; } public void update() { if (isVisible()) { if (lastCommercialRun > 0) { long ago = System.currentTimeMillis() - lastCommercialRun; lastCommercialInfo.setText("Last run: "+DateTime.duration(ago, 1, 0)+" ago"); lastCommercialInfo.setToolTipText("Last run: "+DateTime.formatFullDatetime(lastCommercialRun)); } } } public void checkScheduled() { if (scheduledCommercialTime > 0) { long timeLeft = scheduledCommercialTime - System.currentTimeMillis(); if (timeLeft <= 0) { runCommercialNow(scheduledCommercialLength); } else if (System.currentTimeMillis() - lastCommercialRunAttempt > COMMERCIAL_RUN_ATTEMPT_DELAY) { setCommercialResult( String.format("Commercial (%ds) scheduled to run in %s", scheduledCommercialLength, DateTime.duration(timeLeft, 2, 0)) ); } } } /** * Save commercial settings. */ protected void saveSettings() { boolean enabled = useCommercialDelay.isSelected(); long length = commercialDelay.getSettingValue(); main.saveCommercialDelaySettings(enabled, length); } /** * Allows the commercial settings to be set. * * @param enabled * @param length */ public void updateCommercialDelaySettings(boolean enabled, long length) { useCommercialDelay.setSelected(enabled); commercialDelay.setSettingValue(length); } public void commercialHotkey(int length) { if (commercialButtons.containsKey(length)) { commercialButtons.get(length).doClick(); } else { commercialButtons.get(30).doClick(); } } public void changeChannel(String channel) { this.currentChannel = channel; // Load/reset last commercial run time Long lastCommercialTemp = lastCommercial.get(channel); if (lastCommercialTemp != null) { lastCommercialRun = lastCommercialTemp; } else { lastCommercialRun = 0; lastCommercialInfo.setText(null); } // Clear scheduled commercial clearScheduledCommercial(); // Reset result setCommercialResult(""); } /** * Creates buttons for the defined commercial lengths, adds them to a JPanel, * a ButtonGroup and a Map for further reference. * * @return The JPanel to be added to the GUI. */ private JPanel createCommercialButtons() { ButtonGroup g = new NoneSelectedButtonGroup(); JPanel panel = new JPanel(); ActionListener listener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { commercialButtonClicked(e.getActionCommand()); } }; for (int i : commercialButtonsDef) { JToggleButton b = new JToggleButton(i+"s"); b.setActionCommand(String.valueOf(i)); g.add(b); commercialButtons.put(i, b); panel.add(b); b.addActionListener(listener); } return panel; } /** * One of the commercial buttons was clicked. Find out which one by the * action command and check if it was selected/unselected. * * @param command */ private void commercialButtonClicked(String command) { int length = Integer.parseInt(command); JToggleButton button = commercialButtons.get(length); if (button.isSelected()) { // If commercial is already scheduled, just change the length. if (scheduledCommercialTime == 0) { runCommercial(length); } else { scheduledCommercialLength = length; update(); } } else { scheduledCommercialTime = 0; setCommercialResult("Canceled scheduled commercial."); } } /** * Unselect all run commercial buttons. */ private void clearCommercialButtonsSelection() { for (JToggleButton b : commercialButtons.values()) { b.setSelected(false); } } /** * A run commercial button was pressed. Either play commercial immediately * or schedule it for the given time if that is enabled. * * @param length */ private void runCommercial(int length) { saveSettings(); if (useCommercialDelay.isSelected()) { scheduleCommercial(length); } else { runCommercialNow(length); } } /** * Schedules a commercial of the given length to be run after the delay * currently set in the delay input field. * * @param length */ private void scheduleCommercial(int length) { Long delay = commercialDelay.getSettingValue(); if (delay == null) { setCommercialResult("Invalid delay specified."); clearCommercialButtonsSelection(); } else { scheduledCommercialTime = System.currentTimeMillis() + delay * 1000; scheduledCommercialLength = length; update(); } } /** * Immediately run commercial of the given length. * * @param length */ private void runCommercialNow(int length) { lastCommercialRunAttempt = System.currentTimeMillis(); if (repeatCommercial.isSelected()) { scheduleCommercial(length); } else { clearScheduledCommercial(); } main.runCommercial(currentChannel, length); setLoadingCommercial(true); } /** * Unschedules a scheduled commercial. */ private void clearScheduledCommercial() { clearCommercialButtonsSelection(); scheduledCommercialTime = 0; setCommercialResult(""); } /** * Changes the status to waiting for commercial to run. * * @param loading */ private void setLoadingCommercial(boolean loading) { for (JToggleButton b : commercialButtons.values()) { b.setEnabled(!loading); } if (loading) { setCommercialResult("Trying to run commercial.."); } } /** * Sets the text of the commercialResult label. * * @param result */ private void setCommercialResult(String result) { hideableLabel(commercialResult, result); } /** * Dialog to confirm whether the currentChannel should be switched when a * commercial is currently scheduled on currentChannel. * * @param channel * @return */ public boolean confirmChannelChange(String channel) { if (scheduledCommercialTime == 0) { return true; } String message = "<html><body style='width:240'>" + "There is currently a commercial scheduled on '"+currentChannel +"'. Changing channel to '"+channel+"' will cancel that."; String[] options = new String[]{"Change channel", "Don't change"}; int result = JOptionPane.showOptionDialog(main, message, "Changing channel will cancel commercial", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[1]); return result == 0; } /** * The commercial result as returned from the API. Updates the result text * and when the commercial was last run, also sets it to "not loading" * state. * * @param stream * @param resultText * @param result */ public void commercialResult(String stream, String resultText, TwitchApi.RequestResultCode result) { setCommercialResult(DateTime.currentTime()+" "+resultText); lastCommercialRunAttempt = System.currentTimeMillis(); setLoadingCommercial(false); if (result == TwitchApi.RequestResultCode.RUNNING_COMMERCIAL) { lastCommercial.put(stream, System.currentTimeMillis()); if (stream != null && stream.equals(currentChannel)) { lastCommercialRun = System.currentTimeMillis(); } } } /** * Confirm dialog when a commercial is still scheduled when the dialog * is closed. * * @return */ private int confirmContinueScheduledOnClose() { String message = "<html><body style='width:240'>" + "There is currently a commercial scheduled to be run. It can also" + " be run if you close this. What do you want to do?"; String[] options = new String[]{"Run on schedule", "Cancel commercial"}; int result = JOptionPane.showOptionDialog(main, message, "Closing Admin Dialog while commercial is scheduled", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); return result; } /** * Allows to deselect a button on a ButtonGroup, instead of always having * one selected. * * Source: http://blog.frankel.ch/unselect-all-toggle-buttons-of-a-group */ public static class NoneSelectedButtonGroup extends ButtonGroup { @Override public void setSelected(ButtonModel model, boolean selected) { if (selected) { super.setSelected(model, selected); } else { clearSelection(); } } } }