/* * SleepArchiver - cross-platform data manager for Sleeptracker-series watches. * Copyright (C) 2009-2011 Pavel Fatin <http://pavelfatin.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.pavelfatin.sleeparchiver.gui.night; import com.pavelfatin.sleeparchiver.gui.editor.EditorDialog; import com.pavelfatin.sleeparchiver.lang.Utilities; import com.pavelfatin.sleeparchiver.model.Ease; import com.pavelfatin.sleeparchiver.model.Night; import com.pavelfatin.sleeparchiver.model.Quality; import com.pavelfatin.sleeparchiver.model.Time; import com.pavelfatin.sleeparchiver.swing.*; import org.jdesktop.application.Action; import org.jdesktop.application.Application; import org.jdesktop.application.ResourceMap; import org.jdesktop.application.SingleFrameApplication; import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class NightDialog extends MyDialog { private static final Dimension DEFAULT_SIZE = new Dimension(700, 500); private boolean _removeMomentsEnabled; private boolean _removeConditionsEnabled; private MyDateField _date; private MyTimeField _alarm; private JComboBox _window; private MyTimeField _toBed; private JFormattedTextField _moment; private JList _moments; private MyComboBox _condition; private JList _conditions; private Night _original; private ArrayList<String> _allConditions; private JLabel _average = Builder.createLabel("lavelAverage"); private JComboBox _easeOfFallingAsleep; private JComboBox _qualityOfSleep; private JComboBox _easeOfWakingUp; private JTextArea _comments; private JCheckBox _wakeUpByAlarm; public NightDialog(JFrame owner, Night night, boolean isNew, List<String> conditions) { super(owner, "dialogNight", true); setPreferredSize(DEFAULT_SIZE); _original = night; _allConditions = new ArrayList<String>(conditions); setContent(createUI()); setFooter(_average); setTitle(getString(isNew ? "titleAddition" : "titleEditing")); Utilities.registerAction(_moments, getAction("removeMoments"), "ACTION_REMOVE_MOMENTS"); Utilities.registerAction(_conditions, getAction("removeConditions"), "ACTION_REMOVE_CONDITIONS"); setData(night); registerChangesListener(new ChangesListener()); updateModification(); updateAverage(); if (isNew) { if (_moments.getModel().getSize() > 0) { requestFocus(_condition); } else { positionFocus(_date, _alarm, _toBed, _moment); } } else { requestFocus(_date); } } private void updateAverage() { String format = getResources().getString("formatAverage"); Night data = getData(); _average.setText(data.isComplete() ? String.format(format, data.getMetrics().getAverage()) : ""); } private void scrollToSelectionVisible(JList list) { int index = list.getSelectedIndex(); if (index >= 0) { ListSelectionModel selection = list.getSelectionModel(); list.scrollRectToVisible( list.getCellBounds(selection.getMaxSelectionIndex(), selection.getMaxSelectionIndex())); } } private void registerChangesListener(ChangesListener listener) { _date.addPropertyChangeListener("date", listener); _alarm.addPropertyChangeListener("value", listener); _window.addActionListener(listener); _toBed.addPropertyChangeListener("value", listener); _moments.getModel().addListDataListener(listener); _conditions.getModel().addListDataListener(listener); _easeOfFallingAsleep.addActionListener(listener); _wakeUpByAlarm.addActionListener(listener); _easeOfWakingUp.addActionListener(listener); _comments.getDocument().addDocumentListener(listener); } protected void updateModification() { setModified(!getData().equals(_original)); } private void setData(Night night) { _date.setDate(night.getDate()); _alarm.setValue(night.getAlarm()); _window.setSelectedItem(night.getWindow()); _toBed.setValue(night.getToBed()); _easeOfFallingAsleep.setSelectedItem(night.getEaseOfFallingAsleep()); _qualityOfSleep.setSelectedItem(night.getQualityOfSleep()); _easeOfWakingUp.setSelectedItem(night.getEaseOfWakingUp()); _wakeUpByAlarm.setSelected(night.isAlarmWorked()); _comments.setText(night.getComments()); setListItems(_moments, night.getMoments()); setListItems(_conditions, night.getConditions()); } private void positionFocus(JFormattedTextField... fields) { for (final JFormattedTextField field : fields) { if (field.getValue() == null) { requestFocus(field); return; } } } private void requestFocus(final JComponent component) { SwingUtilities.invokeLater(new Runnable() { public void run() { component.requestFocusInWindow(); } }); } private void setListItems(JList list, List items) { DefaultListModel model = new DefaultListModel(); for (Object item : items) { model.addElement(item); } list.setModel(model); if (model.getSize() > 0) { list.setSelectedValue(model.get(0), true); } } public Night getData() { return new Night(_date.getDate(), _alarm.getTime(), (Integer) _window.getSelectedItem(), _toBed.getTime(), (Ease) _easeOfFallingAsleep.getSelectedItem(), (Quality) _qualityOfSleep.getSelectedItem(), (Ease) _easeOfWakingUp.getSelectedItem(), _wakeUpByAlarm.isSelected(), _comments.getText().trim(), getListItems(_moments), getListItems(_conditions)); } private List getListItems(JList list) { ListModel model = list.getModel(); List items = new ArrayList(); for (int i = 0; i < model.getSize(); i++) { items.add(model.getElementAt(i)); } return items; } protected JPanel createUI() { Builder b = new Builder( "default:grow", "default, 9dlu, fill:default:grow"); b.add(createTop(), 1, 1); b.add(createBottom(), 1, 3); return b.getPanel(); } private JPanel createTop() { Builder b = new Builder( "3 * (default, $lcgap, pref, 9dlu), default, $lcgap, pref", "default"); _date = b.createDateField("fieldDate"); _alarm = b.createTimeField("fieldAlarm"); _window = b.createComboBox("fieldWindow"); _toBed = b.createTimeField("fieldToBed"); _window.setModel(new DefaultComboBoxModel(Night.getWindows())); _window.setSelectedItem(20); b.add(b.createLabel("labelDate"), 1, 1, _date, 3, 1); b.add(b.createLabel("labelAlarm"), 5, 1, _alarm, 7, 1); b.add(b.createLabel("labelWindow"), 9, 1, _window, 11, 1); b.add(b.createLabel("labelToBed"), 13, 1, _toBed, 15, 1); return b.getPanel(); } private JPanel createBottom() { Builder b = new Builder( "default, 6dlu, default:grow", "fill:default:grow"); b.add(b.withSeparator("separatorMoments", createMoments()), 1, 1); b.add(createRight(), 3, 1); return b.getPanel(); } private JPanel createMoments() { Builder b = new Builder( "pref, $lcgap, default, 6dlu, default", "default, $lgap, fill:default:grow"); _moment = b.createTimeField("fieldMoment"); _moment.addPropertyChangeListener("value", new MomentFieldListener()); _moments = b.createList("listMoments"); _moments.setCellRenderer(new TimeRenderer()); _moments.getSelectionModel().addListSelectionListener(new MomentsSelectionListener()); _moments.setPrototypeCellValue(Time.getPrototype()); _moments.addMouseListener(new ListPopupHandler(_moments, new MomentsPopupMenu(getActions()))); b.add(_moment, 1, 1); b.add(b.createLabel("labelAddMoment"), 3, 1); b.add(b.createButton(getAction("removeMoments")), 5, 1); b.add(new JScrollPane(_moments), 1, 3, 5); return b.getPanel(); } private JPanel createRight() { Builder b = new Builder( "default:grow, 6dlu, default", "fill:default:grow, 6dlu, pref"); b.add(b.withSeparator("separatorConditions", createConditions()), 1, 1); b.add(b.withSeparator("separatorObservations", createObservations()), 3, 1); b.add(b.withSeparator("separatorComments", createComments()), 1, 3, 3); return b.getPanel(); } private JPanel createConditions() { Builder b = new Builder( "default, $lcgap, default, 6dlu, right:default:grow", "default, $lgap, fill:default:grow"); _condition = b.createMyComboBox("fieldCondition"); _condition.setInsertion(getResources().getString("newItem")); _condition.setModel(new DefaultComboBoxModel(_allConditions.toArray())); _condition.addComboBoxListener(new ConditionFieldListener()); _conditions = b.createList("listConditions"); _conditions.setModel(new DefaultListModel()); _conditions.getSelectionModel().addListSelectionListener(new ConditionsSelectionListener()); _conditions.addMouseListener(new ListPopupHandler(_conditions, new ConditionsPopupMenu(getActions()))); b.add(_condition, 1, 1); b.add(b.createLabel("labelAddCondition"), 3, 1); b.add(b.createButton(getAction("removeConditions")), 5, 1); b.add(new JScrollPane(_conditions), 1, 3, 5); return b.getPanel(); } private JComponent createComments() { _comments = Builder.createTextArea("fieldComments"); _comments.setRows(3); return new JScrollPane(_comments); } private JPanel createObservations() { Builder b = new Builder( "right:default, $lcgap, default", "default, $lgap, default, $lgap, default, $lgap, default"); ResourceMap resources = getContext().getResourceMap(); _easeOfFallingAsleep = b.createComboBox("fieldEaseOfFallingAsleep"); _easeOfFallingAsleep.setModel(new DefaultComboBoxModel(Ease.values())); _easeOfFallingAsleep.setRenderer(new EaseRenderer(resources)); _qualityOfSleep = b.createComboBox("fieldQualityOfSleep"); _qualityOfSleep.setModel(new DefaultComboBoxModel(Quality.values())); _qualityOfSleep.setRenderer(new QualityRenderer(resources)); _easeOfWakingUp = b.createComboBox("fieldEaseOfWakingUp"); _easeOfWakingUp.setModel(new DefaultComboBoxModel(Ease.values())); _easeOfWakingUp.setRenderer(new EaseRenderer(resources)); _wakeUpByAlarm = b.createCheckBox("fieldWakeUpByAlarm"); b.add(b.createLabel("labelEaseOfFallingAsleep"), 1, 1, _easeOfFallingAsleep, 3, 1); b.add(b.createLabel("labelQualityOfSleep"), 1, 3, _qualityOfSleep, 3, 3); b.add(b.createLabel("labelEaseOfWakingUp"), 1, 5, _easeOfWakingUp, 3, 5); b.add(b.createLabel("labelWakeUpByAlarm"), 1, 7, _wakeUpByAlarm, 3, 7); return b.getPanel(); } private void addValueTo(JList list, Comparable value) { DefaultListModel model = (DefaultListModel) list.getModel(); int index = model.getSize(); model.insertElementAt(value, index); list.getSelectionModel().setSelectionInterval(index, index); scrollToSelectionVisible(list); } private void insertValueInto(JList list, Comparable value) { DefaultListModel model = (DefaultListModel) list.getModel(); if (!model.contains(value)) { int i; for (i = 0; i < model.getSize(); i++) { Comparable each = (Comparable) model.getElementAt(i); if (value.compareTo(each) < 0) { break; } } model.insertElementAt(value, i); } int index = model.indexOf(value); list.getSelectionModel().setSelectionInterval(index, index); scrollToSelectionVisible(list); } @Action(enabledProperty = "removeMomentsEnabled") public void removeMoments() { removeSelection(_moments); } private void removeSelection(JList list) { DefaultListModel model = (DefaultListModel) list.getModel(); ListSelectionModel selection = list.getSelectionModel(); int min = selection.getMinSelectionIndex(); int max = selection.getMaxSelectionIndex(); for (int i = max; i >= min; i--) { if (selection.isSelectedIndex(i)) { model.remove(i); } } int index = min < model.size() ? min : model.size() - 1; selection.setSelectionInterval(index, index); scrollToSelectionVisible(list); } public boolean isRemoveMomentsEnabled() { return _removeMomentsEnabled; } private void setRemoveMomentsEnabled(boolean enabled) { boolean oldValue = _removeMomentsEnabled; _removeMomentsEnabled = enabled; firePropertyChange("removeMomentsEnabled", oldValue, enabled); } @Action(enabledProperty = "removeConditionsEnabled") public void removeConditions() { removeSelection(_conditions); } public boolean isRemoveConditionsEnabled() { return _removeConditionsEnabled; } private void setRemoveConditionsEnabled(boolean enabled) { boolean oldValue = _removeConditionsEnabled; _removeConditionsEnabled = enabled; firePropertyChange("removeConditionsEnabled", oldValue, enabled); } private class MomentFieldListener implements PropertyChangeListener, Runnable { public void propertyChange(PropertyChangeEvent evt) { Time time = (Time) evt.getNewValue(); if (time != null) { SwingUtilities.invokeLater(this); addValueTo(_moments, time); } } public void run() { _moment.setValue(null); } } private class MomentsSelectionListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent e) { setRemoveMomentsEnabled(_moments.getSelectedIndex() >= 0); } } private class ConditionFieldListener implements ComboBoxListener { public void itemSelected(Object item) { doAddCondition((String) item); } public void insertionSelected() { EditorDialog editor = new EditorDialog(NightDialog.this, getResources().getString("titleAddCondition")); ((SingleFrameApplication) Application.getInstance()).show(editor); if (editor.isAccepted()) { String condition = editor.getText(); doAddCondition(condition); if (!_allConditions.contains(condition)) { _allConditions.add(condition); Collections.sort(_allConditions); _condition.setModel(new DefaultComboBoxModel(_allConditions.toArray())); } } } private void doAddCondition(String condition) { insertValueInto(_conditions, condition); } } private class ConditionsSelectionListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent e) { setRemoveConditionsEnabled(_conditions.getSelectedIndex() >= 0); } } private class ChangesListener implements PropertyChangeListener, ActionListener, ListDataListener, DocumentListener { public void propertyChange(PropertyChangeEvent evt) { processChange(); } public void actionPerformed(ActionEvent e) { processChange(); } public void intervalAdded(ListDataEvent e) { processChange(); } public void intervalRemoved(ListDataEvent e) { processChange(); } public void contentsChanged(ListDataEvent e) { processChange(); } public void insertUpdate(DocumentEvent e) { processChange(); } public void removeUpdate(DocumentEvent e) { processChange(); } public void changedUpdate(DocumentEvent e) { } private void processChange() { updateModification(); updateAverage(); } } }