View Javadoc

1   /*
2    * Copyright 2006 - 2012 Christina Bohk and Roland Ewald
3    *  
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at 
7    *  
8    *  http://www.apache.org/licenses/LICENSE-2.0
9    *  
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package p3j.gui.dialogs;
17  
18  import java.awt.BorderLayout;
19  import java.awt.Component;
20  import java.awt.Dimension;
21  import java.awt.event.ActionEvent;
22  import java.awt.event.ActionListener;
23  import java.util.List;
24  
25  import javax.swing.BoxLayout;
26  import javax.swing.ButtonGroup;
27  import javax.swing.JButton;
28  import javax.swing.JCheckBox;
29  import javax.swing.JLabel;
30  import javax.swing.JPanel;
31  import javax.swing.JRadioButton;
32  import javax.swing.JScrollPane;
33  import javax.swing.JTextField;
34  
35  import org.jamesii.gui.utils.BasicUtilities;
36  
37  import p3j.misc.gui.GUI;
38  import p3j.pppm.SubPopulation;
39  import p3j.pppm.SubPopulationModel;
40  
41  import com.jgoodies.forms.builder.ButtonBarBuilder2;
42  
43  /**
44   * A dialog to edit the {@link SubPopulationModel} setup for a given
45   * {@link p3j.pppm.ProjectionModel}.
46   * 
47   * <p/>
48   * The must be at least one sub-population specified, and their order matters:
49   * it is used to define the parameter order in the tree. Additionally, the names
50   * of the sub-populations have to be unique.
51   * 
52   * <p/>
53   * This dialogs also supports a 'non-editable' mode (via {@link #editingAllowed}
54   * ) that can be used to only show the current setup. In this mode, buttons to
55   * change the state are either disabled or left out entirely.
56   * 
57   * <p/>
58   * Each defined sub-population is represented by a {@link JPanel} that is added
59   * to the {@link #subPopListPanel}. The methods
60   * {@link #addSubPopulation(SubPopulation, Component, int)},
61   * {@link #removeSubPopulation(SubPopulation)},
62   * {@link #moveSubPopulationUp(SubPopulation)}, and
63   * {@link #moveSubPopulationDown(SubPopulation)} are used to edit the list.
64   * 
65   * @see SubPopulationModel
66   * 
67   * @author Christina Bohk
68   * @author Roland Ewald
69   */
70  public class SubPopulationModelEditDialog extends ProjectionDialog {
71  
72    /** The Constant serialVersionUID. */
73    private static final long serialVersionUID = -3979733245241552349L;
74  
75    /** The sub-population model to be edited. */
76    private final SubPopulationModel subPopulationModel;
77  
78    /** The panel containing the panel describing the existing sub-populations. */
79    private final JPanel subPopListPanel = new JPanel();
80    {
81      subPopListPanel.setLayout(new BoxLayout(subPopListPanel, BoxLayout.Y_AXIS));
82    }
83  
84    /** The scroll pane for the list of sub-populations. */
85    private final JScrollPane listScroll = new JScrollPane(subPopListPanel);
86  
87    /** Flag to define whether editing is allowed. */
88    private final boolean editingAllowed;
89  
90    /**
91     * The flag that shows whether the edited sub-population model was confirmed
92     * by the user.
93     */
94    private boolean confirmed = false;
95  
96    /**
97     * Default constructor.
98     * 
99     * @param subPopModel
100    *          the sub-population model
101    * @param editingAllowed
102    *          the flag to allow editing
103    */
104   public SubPopulationModelEditDialog(SubPopulationModel subPopModel,
105       boolean editingAllowed) {
106     this.editingAllowed = editingAllowed;
107     subPopulationModel = new SubPopulationModel(subPopModel.getSubPopulations());
108 
109     setModal(true);
110     setTitle((editingAllowed ? "Edit" : "Show") + " Sub-Populations");
111     setSize(DIALOG_WIDTH, 2 * DIALOG_HEIGHT);
112     GUI.centerOnScreen(this);
113     initialize();
114   }
115 
116   @Override
117   protected void okAction() {
118     this.confirmed = true;
119     setVisible(false);
120   }
121 
122   public SubPopulationModel getSubPopulationModel() {
123     return subPopulationModel;
124   }
125 
126   public boolean isConfirmed() {
127     return confirmed;
128   }
129 
130   private void addSubPopulation(SubPopulation subPopulation,
131       Component subPopPanel, int index) {
132     subPopulationModel.getSubPopulations().add(index, subPopulation);
133     subPopListPanel.add(subPopPanel, index);
134   }
135 
136   private Component removeSubPopulation(int indexToDelete) {
137     Component deleted = subPopListPanel.getComponent(indexToDelete);
138     subPopListPanel.remove(indexToDelete);
139     subPopulationModel.getSubPopulations().remove(indexToDelete);
140     return deleted;
141   }
142 
143   private void refreshSubPopList() {
144     BasicUtilities.revalidateOnEDT(subPopListPanel);
145     listScroll.validate();
146   }
147 
148   /**
149    * Adds a sub-population (to the end of the list).
150    * 
151    * <p/>
152    * This and the following methods alter the state of the sub-population list
153    * and are thus synchronized, to grant them exclusive access to both
154    * {@link #subPopulationModel} and {@link #subPopListPanel}.
155    * 
156    * @param subPopulation
157    *          the sub-population
158    */
159   private synchronized void addSubPopulation(SubPopulation subPopulation) {
160     for (SubPopulation subPop : subPopulationModel.getSubPopulations())
161       if (subPop.getSimplifiedName().equals(subPopulation.getSimplifiedName())) {
162         GUI.printMessage(
163             this,
164             "Adding sub-population failed",
165             "Sub-population names must be unique (when converted to lower case, ignoring special characters like '/' and '\\'). \nThere already is a sub-population called '"
166                 + subPopulation.getName() + "'.");
167         return;
168       }
169     int index = subPopulationModel.getSubPopulations().size();
170     addSubPopulation(subPopulation,
171         createSubPopulationDisplayPanel(subPopulation, index), index);
172     refreshSubPopList();
173   }
174 
175   private synchronized void removeSubPopulation(SubPopulation subPopulation) {
176     if (subPopulationModel.getSubPopulations().size() == 1) {
177       GUI.printMessage(this, "Deletion failed",
178           "At least one sub-population has to be defined.");
179       return;
180     }
181     int indexToDelete = subPopulationModel.getSubPopulations().indexOf(
182         subPopulation);
183     if (indexToDelete >= 0) {
184       removeSubPopulation(indexToDelete);
185 
186     }
187     refreshSubPopList();
188   }
189 
190   private synchronized void moveSubPopulationUp(SubPopulation subPop) {
191     int currentIndex = subPopulationModel.getSubPopulations().indexOf(subPop);
192     if (currentIndex <= 0)
193       return;
194     Component componentToMove = removeSubPopulation(currentIndex);
195     addSubPopulation(subPop, componentToMove, currentIndex - 1);
196     refreshSubPopList();
197   }
198 
199   private synchronized void moveSubPopulationDown(SubPopulation subPop) {
200     int currentIndex = subPopulationModel.getSubPopulations().indexOf(subPop);
201     if (currentIndex == subPopulationModel.getSubPopulations().size() - 1
202         || currentIndex < 0)
203       return;
204     Component componentToMove = removeSubPopulation(currentIndex);
205     addSubPopulation(subPop, componentToMove, currentIndex + 1);
206     refreshSubPopList();
207   }
208 
209   /**
210    * Initializes the UI components.
211    */
212   private void initialize() {
213 
214     JPanel overall = new JPanel(GUI.getStdBorderLayout());
215     getContentPane().add(overall);
216 
217     JPanel content = new JPanel(GUI.getStdBorderLayout());
218     content.add(initializeSubPopListPanel(), BorderLayout.CENTER);
219     content.add(editingAllowed ? createAddSubPopPanel() : new JPanel(),
220         BorderLayout.SOUTH);
221 
222     overall.add(new JPanel(), BorderLayout.NORTH);
223     overall.add(new JPanel(), BorderLayout.WEST);
224     overall.add(new JPanel(), BorderLayout.EAST);
225     overall.add(content, BorderLayout.CENTER);
226     overall.add(createButtonPanel(), BorderLayout.SOUTH);
227   }
228 
229   private JPanel initializeSubPopListPanel() {
230     List<SubPopulation> subPops = subPopulationModel.getSubPopulations();
231     for (int i = 0; i < subPops.size(); i++)
232       subPopListPanel.add(createSubPopulationDisplayPanel(subPops.get(i), i));
233     JPanel surroundingPanel = new JPanel(GUI.getStdBorderLayout());
234     surroundingPanel.add(listScroll, BorderLayout.CENTER);
235     return surroundingPanel;
236   }
237 
238   private JPanel createButtonPanel() {
239     JPanel buttonPanel = new JPanel(GUI.getStdBorderLayout());
240     buttonPanel.add(createButtonSubPanel(), BorderLayout.EAST);
241     return buttonPanel;
242   }
243 
244   private JPanel createButtonSubPanel() {
245     ButtonBarBuilder2 bbBuilder = new ButtonBarBuilder2();
246     if (editingAllowed) {
247       bbBuilder.addButton(getCancelButton());
248       bbBuilder.addUnrelatedGap();
249     }
250     bbBuilder.addButton(getOkButton());
251     JPanel subPanel = new JPanel();
252     subPanel.add(bbBuilder.getPanel());
253     return subPanel;
254   }
255 
256   private JPanel createAddSubPopPanel() {
257     final JRadioButton additiveButton = new JRadioButton("+");
258     additiveButton.setFont(GUI.getDefaultFontBold());
259     additiveButton.setSelected(true);
260     final JRadioButton subtractiveButton = new JRadioButton("-");
261     subtractiveButton.setFont(GUI.getDefaultFontBold());
262     final ButtonGroup buttonGroup = new ButtonGroup();
263     buttonGroup.add(additiveButton);
264     buttonGroup.add(subtractiveButton);
265     JPanel radioGroupPanel = new JPanel(GUI.getStdBorderLayout());
266     radioGroupPanel.add(additiveButton, BorderLayout.CENTER);
267     radioGroupPanel.add(subtractiveButton, BorderLayout.EAST);
268 
269     final JTextField subPopName = new JTextField("Sub-Population Name");
270     final JCheckBox hasDescendantGenerations = new JCheckBox(
271         "Distinct Descendant Generations");
272     final JCheckBox jumpOffPop = new JCheckBox("Jump-Off Population");
273     final JButton addButton = new JButton("Add");
274     addButton.addActionListener(new ActionListener() {
275       @Override
276       public void actionPerformed(ActionEvent e) {
277         addSubPopulation(new SubPopulation(subPopName.getText(), jumpOffPop
278             .isSelected(), additiveButton.isSelected(),
279             hasDescendantGenerations.isSelected()));
280       }
281     });
282 
283     return createAddSubPopPanelLayout(radioGroupPanel, subPopName,
284         hasDescendantGenerations, jumpOffPop, addButton);
285   }
286 
287   private JPanel createAddSubPopPanelLayout(JPanel radioGroupPanel,
288       final JTextField subPopName, final JCheckBox hasDescendantGenerations,
289       JCheckBox jumpOffPop, final JButton addButton) {
290     JPanel generalPanel = new JPanel(GUI.getStdBorderLayout());
291     JPanel addButtonPanel = new JPanel(GUI.getStdBorderLayout());
292 
293     JPanel optionPanel = new JPanel();
294     optionPanel.add(hasDescendantGenerations);
295     optionPanel.add(jumpOffPop);
296 
297     addButtonPanel.add(radioGroupPanel, BorderLayout.WEST);
298     addButtonPanel.add(subPopName, BorderLayout.CENTER);
299     addButtonPanel.add(optionPanel, BorderLayout.EAST);
300 
301     generalPanel.add(addButtonPanel, BorderLayout.CENTER);
302     generalPanel.add(addButton, BorderLayout.EAST);
303     return generalPanel;
304   }
305 
306   private JPanel createSubPopulationDisplayPanel(SubPopulation subPop, int index) {
307     JPanel subPopDisplayPanel = new JPanel(GUI.getStdBorderLayout());
308     subPopDisplayPanel.add(new JPanel(), BorderLayout.NORTH);
309 
310     subPopDisplayPanel.add(createAdditiveButton(subPop, index),
311         BorderLayout.WEST);
312     subPopDisplayPanel.add(createSubPopulationDescription(subPop),
313         BorderLayout.CENTER);
314     subPopDisplayPanel
315         .add(editingAllowed ? createControlButtonPanel(subPop, index)
316             : new JPanel(), BorderLayout.EAST);
317     subPopDisplayPanel.add(new JPanel(), BorderLayout.SOUTH);
318     subPopDisplayPanel.setMaximumSize(new Dimension(
319         (int) (0.8 * SubPopulationModelEditDialog.DIALOG_WIDTH), 60));
320     return subPopDisplayPanel;
321   }
322 
323   private JLabel createSubPopulationDescription(SubPopulation subPop) {
324     JLabel subPopDesc = new JLabel(subPop.getName()
325         + (subPop.isJumpOffPopulation() ? " [Jump-Off]" : "")
326         + (subPop.isConsistingOfDescendantGenerations() ? " (& descendants)"
327             : ""));
328     subPopDesc.setFont(GUI.getDefaultFontBold());
329     return subPopDesc;
330   }
331 
332   private JPanel createControlButtonPanel(final SubPopulation subPop,
333       final int index) {
334     JPanel buttonPanel = new JPanel();
335     buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
336     JButton deleteButton = GUI.createIconButton("delete_obj.gif", "Delete");
337     deleteButton.addActionListener(new ActionListener() {
338       @Override
339       public void actionPerformed(ActionEvent e) {
340         removeSubPopulation(subPop);
341       }
342     });
343     JButton upButton = GUI.createIconButton("step_current_up.gif", "up");
344     upButton.addActionListener(new ActionListener() {
345       @Override
346       public void actionPerformed(ActionEvent e) {
347         moveSubPopulationUp(subPop);
348       }
349     });
350     JButton downButton = GUI.createIconButton("step_current_down.gif", "down");
351     downButton.addActionListener(new ActionListener() {
352       @Override
353       public void actionPerformed(ActionEvent e) {
354         moveSubPopulationDown(subPop);
355       }
356     });
357 
358     buttonPanel.add(upButton);
359     buttonPanel.add(new JPanel());
360     buttonPanel.add(downButton);
361     buttonPanel.add(new JPanel());
362     buttonPanel.add(deleteButton);
363     return buttonPanel;
364   }
365 
366   private JButton createAdditiveButton(final SubPopulation subPopulation,
367       final int subPopIndex) {
368     final JButton additivityButton = new JButton();
369     additivityButton.setEnabled(editingAllowed);
370     setButtonIcon(additivityButton, subPopulation.isAdditive());
371     additivityButton.addActionListener(new ActionListener() {
372       @Override
373       public void actionPerformed(ActionEvent e) {
374         subPopulation.setAdditive(!subPopulation.isAdditive());
375         setButtonIcon(additivityButton, subPopulation.isAdditive());
376       }
377     });
378     return additivityButton;
379   }
380 
381   private static void setButtonIcon(JButton additivityButton, boolean isAdditive) {
382     GUI.decorateButtonWithIconOrText(additivityButton, GUI
383         .retrieveIcon(isAdditive ? "add_correction.png"
384             : "remove_correction.png"), isAdditive ? "+" : "-");
385     additivityButton.setToolTipText(isAdditive ? "Adds to overall population"
386         : "Subtracts from overall population");
387   }
388 }