package net.sf.dz.setup.core; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.io.File; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import org.freehold.jukebox.logger.Logger; import net.sf.dz.view.component.ScheduleListener; import net.sf.dz.view.component.SchedulePanel; import net.sf.dz.util.wizard.DoublePanel; import net.sf.dz.util.wizard.Wizard; import net.sf.dz.util.wizard.WizardPage; class SchedulePage extends WizardPage implements ScheduleListener { private DoublePanel dp; private JList zoneList = new JList(); private SchedulePanel coolingSchedulePanel; private SchedulePanel heatingSchedulePanel; private JButton selectDirButton = new JButton(); private JFileChooser fileChooser = new JFileChooser(); private File currentSelection; public SchedulePage(Logger logger, Wizard owner) { super(logger, owner, "Set the schedule"); dp = new DoublePanel(getContentPane(), this, new LinkedList()); dp.leftPanel.setBorder(BorderFactory.createTitledBorder("Temperature Zones")); dp.rightPanel.setBorder(BorderFactory.createTitledBorder("Weekly Schedule")); zoneList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); zoneList.addListSelectionListener(this); JScrollPane zoneScroller = new JScrollPane(zoneList); coolingSchedulePanel = new SchedulePanel(this); heatingSchedulePanel = new SchedulePanel(this); coolingSchedulePanel.setBorder(BorderFactory.createTitledBorder("Cooling Schedule")); heatingSchedulePanel.setBorder(BorderFactory.createTitledBorder("Heating Schedule")); dp.add(dp.leftPanel, zoneScroller); GridBagLayout layout = new GridBagLayout(); GridBagConstraints cs = new GridBagConstraints(); dp.rightPanel.setLayout(layout); cs.gridx = 0; cs.gridy = 0; cs.weightx = 0; cs.weighty = 1; cs.fill = GridBagConstraints.BOTH; JPanel fillerTop = new JPanel(); layout.setConstraints(fillerTop, cs); dp.rightPanel.add(fillerTop); cs.gridy++; cs.weighty = 0; cs.fill = GridBagConstraints.NONE; layout.setConstraints(coolingSchedulePanel, cs); dp.rightPanel.add(coolingSchedulePanel); cs.gridy++; layout.setConstraints(heatingSchedulePanel, cs); dp.rightPanel.add(heatingSchedulePanel); cs.gridy++; cs.weighty = 1; cs.fill = GridBagConstraints.BOTH; JPanel fillerMiddle = new JPanel(); layout.setConstraints(fillerMiddle, cs); dp.rightPanel.add(fillerMiddle); cs.gridy++; cs.weighty = 0; cs.fill = GridBagConstraints.NONE; JPanel selectDirPanel = new JPanel(); layout.setConstraints(selectDirPanel, cs); dp.rightPanel.add(selectDirPanel); renderButtons(selectDirPanel); currentSelection = new File((String)getOwner().getContext().get("schedule.dir")); selectDirButton.setText(currentSelection.toString()); selectDirButton.addActionListener(this); cs.gridy++; cs.weighty = 1; cs.fill = GridBagConstraints.BOTH; JPanel fillerBottom = new JPanel(); layout.setConstraints(fillerBottom, cs); dp.rightPanel.add(fillerBottom); } private void renderButtons(JPanel selectDirPanel) { //selectDirPanel.setBorder(BorderFactory.createTitledBorder("Schedule persistence directory")); GridBagLayout layout = new GridBagLayout(); GridBagConstraints cs = new GridBagConstraints(); selectDirPanel.setLayout(layout); cs.gridx = 0; cs.gridy = 0; cs.gridwidth = 1; cs.gridheight = 1; cs.weightx = 1; cs.weighty = 0; cs.fill = GridBagConstraints.NONE; JLabel l = new JLabel("Persist schedule in"); layout.setConstraints(l, cs); selectDirPanel.add(l); cs.gridx++; cs.weightx = 1; cs.fill = GridBagConstraints.HORIZONTAL; layout.setConstraints(selectDirButton, cs); selectDirPanel.add(selectDirButton); } public void activate() { currentSelection = new File(getOwner().getContext().get("schedule.dir").toString()); selectDirButton.setText(currentSelection.toString()); Set zoneSet = getSortedContextValues("zone."); Object selected = null; if ( zoneList.getSelectedIndex() != -1 ) { selected = zoneList.getSelectedValue(); } zoneList.setListData(new Vector(zoneSet)); if ( selected != null ) { zoneList.setSelectedValue(selected, true); } else if ( !zoneSet.isEmpty() ) { // We get here if there was no selection, but there were // some zones - in other words, the first time zoneList.setSelectedIndex(0); } complain(LOG_CRIT, CH_WP, "FIXME: read the actual schedule"); } private void parse(SchedulePanel target, String type, String event) { StringTokenizer st = new StringTokenizer(event, ":"); try { target.parse(type, st); } catch ( Throwable t ) { complain(LOG_ERR, CH_WP, "Unable to parse event '" + event + "':", t); } } public String getHelpURL() { return "FIXME"; } public String validate() { ZoneDescriptor selected = (ZoneDescriptor)zoneList.getSelectedValue(); coolingSchedulePanel.setEnabled(selected != null); heatingSchedulePanel.setEnabled(selected != null); if ( !currentSelection.exists() || !currentSelection.isDirectory() || !currentSelection.canRead() || !currentSelection.canWrite() ) { return "Schedule directory (" + currentSelection + ") doesn't exist, not readable, writable, or not a directory"; } for ( Iterator i = getSortedContextValues("zone.").iterator(); i.hasNext(); ) { ZoneDescriptor zd = (ZoneDescriptor)i.next(); if ( zd.coolingSchedule.isEmpty() || zd.heatingSchedule.isEmpty() ) { return "Some zones (at least '" + zd.name + "') don't have a schedule yet, please review it"; } } return ""; } public boolean isEnabled() { return true; } public void scheduleEventChanged(SchedulePanel source, String renderCommand) { complain(LOG_NOTICE, CH_WP, "FIXME: Schedule command: " + renderCommand, new Throwable()); // Have to figure out which event changed. The only constant // parts would be the day name and the period name, this is how // we find that event. // This will not be null, for we won't get the event unless the // schedule panel is enabled ZoneDescriptor zd = (ZoneDescriptor)zoneList.getSelectedValue(); Set eventSet = null; if ( source == coolingSchedulePanel ) { eventSet = zd.coolingSchedule; } else if ( source == heatingSchedulePanel ) { eventSet = zd.heatingSchedule; } else { throw new IllegalArgumentException("Source is neither cooling nor heating???"); } StringTokenizer st = new StringTokenizer(renderCommand, ":"); String signature = st.nextToken() + ":" + st.nextToken(); for ( Iterator i = eventSet.iterator(); i.hasNext(); ) { String event = i.next().toString(); if ( event.startsWith(signature) ) { complain(LOG_DEBUG, CH_WP, "Matching event: " + event); if ( event.equals(renderCommand) ) { // That's all right, they will send it to us every // time (though it's annoying) // VT: FIXME: Make the SchedulePanel *not* send the // event unless it's changed return; } i.remove(); eventSet.add(renderCommand); break; } } try { // Let's give them the feedback so they can update themselves source.parse("event", new StringTokenizer(renderCommand, ":")); } catch ( Throwable t ) { complain(LOG_ERR, CH_WP, "Can't parse the event:", t); } } public void actionPerformed2(ActionEvent e) { Object source = e.getSource(); if ( source == selectDirButton ) { fileChooser.setCurrentDirectory(currentSelection); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int rc = fileChooser.showDialog(getContentPane(), "Use this directory to store schedule"); if ( rc == JFileChooser.APPROVE_OPTION ) { currentSelection = fileChooser.getSelectedFile(); selectDirButton.setText(currentSelection.toString()); getOwner().getContext().put("schedule.dir", currentSelection.toString()); } } } public void valueChanged2(ListSelectionEvent e) { if ( e.getValueIsAdjusting() ) { // This prevents double triggering return; } ZoneDescriptor selected = (ZoneDescriptor)zoneList.getSelectedValue(); if ( selected == null ) { return; } // Extract the schedule from the descriptor and populate the // panels. Create a default one if there's none. parseSchedule(selected.coolingSchedule, coolingSchedulePanel); parseSchedule(selected.heatingSchedule, heatingSchedulePanel); } private void parseSchedule(Set schedule, SchedulePanel panel) { if ( schedule.isEmpty() ) { createDefault(schedule); parseSchedule(schedule, panel); return; } for ( Iterator i = schedule.iterator(); i.hasNext(); ) { parse(panel, "event", i.next().toString()); } } private void createDefault(Set schedule) { complain(LOG_DEBUG, CH_WP, "Creating a default schedule"); // Let's fill the panel with the default schedule String events[] = { "MO:Wake:8:00:24.5:on:voting:1", "MO:Leave:9:00:30.0:on:voting:1", "MO:Return:17:00:26.0:on:voting:1", "MO:Sleep:0:00:25.0:on:voting:1", "TU:Wake:8:00:24.5:on:voting:1", "TU:Leave:9:00:30.0:on:voting:1", "TU:Return:17:00:26.0:on:voting:1", "TU:Sleep:0:00:25.0:on:voting:1", "WE:Wake:8:00:24.5:on:voting:1", "WE:Leave:9:00:30.0:on:voting:1", "WE:Return:17:00:26.0:on:voting:1", "WE:Sleep:0:00:25.0:on:voting:1", "TH:Wake:8:00:24.5:on:voting:1", "TH:Leave:9:00:30.0:on:voting:1", "TH:Return:17:00:26.0:on:voting:1", "TH:Sleep:0:00:25.0:on:voting:1", "FR:Wake:8:00:24.5:on:voting:1", "FR:Leave:9:00:30.0:on:voting:1", "FR:Return:17:00:26.0:on:voting:1", "FR:Sleep:0:00:25.0:on:voting:1", "SA:Wake:6:00:24.5:on:voting:1", "SA:Leave:9:00:26.0:on:voting:1", "SA:Return:17:00:26.0:on:voting:1", "SA:Sleep:0:00:25.0:on:voting:1", "SU:Wake:6:00:24.5:on:voting:1", "SU:Leave:9:00:26.0:on:voting:1", "SU:Return:17:00:26.0:on:voting:1", "SU:Sleep:0:00:25.0:on:voting:1" }; for ( int offset = 0; offset < events.length; offset++ ) { schedule.add(events[offset]); } } }