001: package jimm.datavision.gui;
002:
003: import jimm.datavision.*;
004: import jimm.datavision.field.Field;
005: import jimm.datavision.field.AggregateField;
006: import jimm.datavision.gui.cmd.NewAggregateCommand;
007: import jimm.datavision.gui.cmd.EditAggregateFuncCommand;
008: import jimm.datavision.gui.cmd.DeleteAggregateCommand;
009: import jimm.util.I18N;
010: import java.awt.BorderLayout;
011: import java.awt.GridLayout;
012: import java.awt.event.*;
013: import java.util.*;
014: import javax.swing.*;
015:
016: /**
017: * An aggregates editor for a single field that lets the user add and delete
018: * aggregates at each level (group footers, report footer). Never called
019: * when multiple fields are selected.
020: *
021: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
022: */
023: public class AggregatesWin extends EditWin {
024:
025: static class Slot {
026:
027: FieldWidget aggregate;
028: boolean existedAlready;
029: JCheckBox checkBox;
030: JComboBox functionMenu;
031:
032: Slot(FieldWidget fw, JCheckBox cb, JComboBox menu) {
033: existedAlready = (fw != null);
034: aggregate = fw;
035: checkBox = cb;
036: functionMenu = menu;
037: }
038: }
039:
040: protected static final int TEXT_FIELD_COLS = 8;
041:
042: protected Report report;
043: protected FieldWidget fieldWidget;
044: protected HashMap slots;
045:
046: /**
047: * Constructor.
048: *
049: * @param designer the window to which this dialog belongs
050: * @param fw a field widget
051: */
052: public AggregatesWin(Designer designer, FieldWidget fw) {
053: super (designer, I18N.get("AggregatesWin.title_prefix") + ' '
054: + fw, "AggregatesWin.command_name");
055:
056: fieldWidget = fw;
057: report = designer.report;
058:
059: // If we have been handed a aggregate field's widget, find the widget of
060: // the original field.
061: if (fieldWidget.getField() instanceof AggregateField) {
062: final Field fieldToAggregate = getAggregateField()
063: .getField();
064: final FieldWidget[] list = new FieldWidget[1];
065:
066: designer.withWidgetsDo(new FieldWidgetWalker() {
067: public void step(FieldWidget fw) {
068: Field f = fw.getField();
069: if (f == fieldToAggregate)
070: list[0] = fw;
071: }
072: });
073: fieldWidget = list[0];
074: // Reset window title
075: setTitle(I18N.get("AggregatesWin.title_prefix") + ' '
076: + fieldWidget);
077: }
078:
079: // Create a hash that maps either a group or a report footer section to
080: // the associated existing aggregate widget.
081: final HashMap aggregates = new HashMap();
082: final AbstractList subs = designer.report
083: .getAggregateFieldsFor(fieldWidget.getField());
084: designer.withWidgetsDo(new FieldWidgetWalker() {
085: public void step(FieldWidget fw) {
086: Field f = fw.getField();
087: if (subs.contains(f)) {
088: Object key = ((AggregateField) f).getGroup();
089: if (key == null)
090: key = fw.getField().getSection();
091: aggregates.put(key, fw);
092: }
093: }
094: });
095:
096: buildWindow(aggregates);
097: pack();
098: setVisible(true);
099: }
100:
101: /**
102: * Builds the window contents.
103: *
104: * @param aggregates a hash that maps either a group or a report footer
105: * section to the associated existing aggregate widget
106: */
107: protected void buildWindow(HashMap aggregates) {
108: JPanel editorPanel = buildAggregatesEditor(aggregates);
109:
110: // OK, Apply, Revert, and Cancel Buttons
111: JPanel buttonPanel = closeButtonPanel();
112:
113: // Add title, values, and buttons to window
114: getContentPane().add(
115: new JLabel(I18N.get("AggregatesWin.title_prefix") + ' '
116: + fieldWidget), BorderLayout.NORTH);
117: getContentPane().add(editorPanel, BorderLayout.CENTER);
118: getContentPane().add(buttonPanel, BorderLayout.SOUTH);
119: }
120:
121: /**
122: * Builds the editor widget panel.
123: *
124: * @param aggregates a hash that maps either a group or a report footer
125: * section to the associated existing aggregate widget
126: */
127: protected JPanel buildAggregatesEditor(HashMap aggregates) {
128: Object[] functionNames = AggregateField.functionNameArray();
129: JPanel panel = new JPanel();
130: panel.setLayout(new GridLayout(0, 1));
131: panel
132: .setBorder(BorderFactory.createEmptyBorder(20, 20, 20,
133: 20));
134: JCheckBox cb = null;
135: JComboBox menu = null;
136: slots = new HashMap();
137:
138: // For each footer in the report, create a checkbox and a slot.
139:
140: // Group footer sections
141: int i = report.countGroups();
142: for (Iterator iter = report.groupsReversed(); iter.hasNext(); --i) {
143: Group g = (Group) iter.next();
144: FieldWidget fw = (FieldWidget) aggregates.get(g);
145:
146: cb = new JCheckBox(I18N.get("AggregatesWin.group") + " #"
147: + i + " (" + g.getSelectableName() + ")");
148: panel.add(cb);
149: menu = new JComboBox(functionNames);
150: panel.add(menu);
151: if (fw != null) {
152: cb.setSelected(true);
153: menu.setSelectedItem(getAggregateField(fw)
154: .getFunction());
155: }
156: slots.put(g, new Slot(fw, cb, menu));
157: }
158:
159: // Report footer sections
160: cb = new JCheckBox(I18N.get("AggregatesWin.grand_total"));
161: panel.add(cb);
162: menu = new JComboBox(functionNames);
163: panel.add(menu);
164: boolean addedSlot = false;
165: for (Iterator iter = report.footers().iterator(); iter
166: .hasNext()
167: && !addedSlot;) {
168: Section s = (Section) iter.next();
169: FieldWidget fw = (FieldWidget) aggregates.get(s);
170: if (fw != null) {
171: cb.setSelected(true);
172: menu.setSelectedItem(getAggregateField(fw)
173: .getFunction());
174: slots.put(null, new Slot(fw, cb, menu));
175: addedSlot = true;
176: }
177: }
178: if (!addedSlot)
179: slots.put(null, new Slot(null, cb, menu));
180:
181: // Add an "all" button
182: JButton all = new JButton(I18N.get("GUI.all"));
183: all.addActionListener(new ActionListener() {
184: public void actionPerformed(ActionEvent e) {
185: for (Iterator iter = slots.values().iterator(); iter
186: .hasNext();)
187: ((Slot) iter.next()).checkBox.setSelected(true);
188: }
189: });
190: JPanel buttonPanel = new JPanel();
191: buttonPanel.add(all);
192: panel.add(buttonPanel);
193:
194: return panel;
195: }
196:
197: /**
198: * Return the <code>AggregateField</code> associated with the ivar
199: * <var>fieldWidget</var>. Convenience method.
200: */
201: protected AggregateField getAggregateField() {
202: return getAggregateField(fieldWidget);
203: }
204:
205: /**
206: * Return the <code>AggregateField</code> associated with <var>fw</var>.
207: * Convenience method.
208: */
209: protected AggregateField getAggregateField(FieldWidget fw) {
210: return (AggregateField) fw.getField();
211: }
212:
213: protected void doSave() {
214: // Hide existing aggregates that are unchecked and show existing
215: // aggregates that are checked. For checked aggregates, modify function
216: // (no harm done if function is not changed).
217: for (Iterator iter = slots.keySet().iterator(); iter.hasNext();) {
218: Object key = iter.next();
219: Slot slot = (Slot) slots.get(key);
220: if (slot.checkBox.isSelected()) {
221: String functionName = slot.functionMenu
222: .getSelectedItem().toString();
223: if (slot.aggregate == null) {
224: NewAggregateCommand cmd = new NewAggregateCommand(
225: report, fieldWidget, (Group) key,
226: functionName);
227: cmd.perform();
228: commands.add(cmd);
229:
230: slot.aggregate = cmd.getAggregateWidget();
231: } else { // Already have one; change to selected func
232: EditAggregateFuncCommand cmd = new EditAggregateFuncCommand(
233: getAggregateField(slot.aggregate),
234: functionName);
235: cmd.perform();
236: commands.add(cmd);
237: getAggregateField(slot.aggregate).setFunction(
238: functionName);
239: }
240: } else { // We don't want an aggregate for this slot
241: if (slot.aggregate != null) {
242: String functionName = getAggregateField(
243: slot.aggregate).getFunction();
244: DeleteAggregateCommand cmd = new DeleteAggregateCommand(
245: report, fieldWidget, slot.aggregate,
246: functionName, (Group) key);
247: cmd.perform();
248: commands.add(cmd);
249: slot.aggregate = null;
250: }
251: }
252: }
253: }
254:
255: protected void doRevert() {
256: for (Iterator iter = slots.keySet().iterator(); iter.hasNext();) {
257: Slot slot = (Slot) slots.get(iter.next());
258: if (slot.existedAlready) {
259: slot.aggregate.getComponent().setVisible(true);
260: slot.checkBox.setSelected(true);
261: } else {
262: if (slot.aggregate != null) {
263: slot.aggregate.doDelete();
264: slot.aggregate = null;
265: }
266: slot.checkBox.setSelected(false);
267: }
268: // For some reason we need to force a repaint of the section. Can't
269: //just call invalidate.
270: if (slot.aggregate != null)
271: slot.aggregate.getComponent().getParent().repaint();
272: }
273: }
274:
275: }
|