001: package jimm.datavision.gui;
002:
003: import jimm.datavision.*;
004: import jimm.datavision.field.*;
005: import jimm.datavision.gui.cmd.FormatCommand;
006: import jimm.util.I18N;
007: import java.awt.*;
008: import java.awt.event.*;
009: import java.util.ArrayList;
010: import javax.swing.*;
011:
012: /**
013: * A field format editing dialog box. There are tabs for text format and
014: * borders. The initial format and border values are retrieved from one of
015: * two places: either a field passed in to the constructor or the first
016: * selected field in the desing window. The format and border are applied to
017: * either a single field or all selected fields.
018: * <p>
019: * <i>Warning</i>: this code depends upon the fact that the strings in
020: * <code>edgeCountChoices</code> equals the integer value of the string
021: * (zero, one, two, etc.) and that the strings in
022: * <code>edgeStyleChoices</code> correspond to the numeric values of the
023: * <code>BorderEdge.STYLE_*</code> constants.
024: *
025: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
026: */
027: public class FormatWin extends EditWin implements FieldWalker {
028:
029: // ================================================================
030: /**
031: * Holds a border edge and the widgets used for editing it.
032: */
033: static class EdgeWidgets {
034:
035: protected BorderEdge edge;
036: protected String name;
037: protected JComboBox numberComboBox;
038: protected JComboBox styleComboBox;
039: protected JTextField thicknessText;
040:
041: EdgeWidgets(BorderEdge e, String name) {
042: edge = e;
043: this .name = name;
044: }
045:
046: String getName() {
047: return name;
048: }
049:
050: }
051:
052: // ================================================================
053:
054: /**
055: * List of pre-approved font size choices. Don't use this directly; instead
056: * call {@link #sizeChoices}.
057: */
058: protected static Integer[] SIZE_CHOICES;
059:
060: protected static final int FORMAT_TEXT_FIELD_COLS = 20;
061: protected static final int THICKNESS_COLS = 8;
062:
063: protected static final int TOP = 0;
064: protected static final int LEFT = 1;
065: protected static final int RIGHT = 2;
066: protected static final int BOTTOM = 3;
067:
068: protected static String[] fontFamilyNames;
069:
070: protected Field field;
071: protected Format format;
072: protected Border border;
073: protected Format origFormat;
074: protected Border origBorder;
075: protected boolean saveRevertInfo;
076: protected JComboBox fontFamily;
077: protected JComboBox size;
078: protected JCheckBox bold;
079: protected JCheckBox italic;
080: protected JCheckBox underline;
081: protected JCheckBox wrap;
082: protected JComboBox align;
083: protected JTextField formatText;
084: protected JLabel fieldColorLabel;
085: protected JLabel borderColorLabel;
086: protected EdgeWidgets[] edgeWidgets;
087:
088: /**
089: * This method loads all the font family names in a separate thread. It is
090: * called each time a design window is created, though it only does its
091: * thang the first time it is called.
092: */
093: public static void loadFontChoices() {
094: if (fontFamilyNames == null) {
095: new Thread(new Runnable() {
096: public void run() {
097: fontFamilyNames = GraphicsEnvironment
098: .getLocalGraphicsEnvironment()
099: .getAvailableFontFamilyNames();
100: }
101: }).start();
102: }
103: }
104:
105: /**
106: * Constructor.
107: *
108: * @param designer the window to which this dialog belongs
109: * @param f a field from which we will take the format and border
110: * @param whichTab the index of the tab to display when opened
111: */
112: public FormatWin(Designer designer, Field f, int whichTab) {
113: super (designer, I18N.get("FormatWin.title")
114: + (f.designLabel().startsWith("{") ? " " : " (")
115: + f.designLabel()
116: + (designer.countSelectedFields() > 1 ? " +" : "")
117: + (f.designLabel().startsWith("{") ? " " : " )"),
118: "FormatCommand.name");
119:
120: field = f;
121:
122: origFormat = field.getFormat();
123: if (origFormat != null)
124: origFormat = (Format) origFormat.clone();
125: origBorder = field.getBorder();
126: if (origBorder != null)
127: origBorder = (Border) origBorder.clone();
128:
129: copyFormatAndBorder(field.getFormat(), field.getBorder());
130:
131: buildWindow(whichTab);
132: pack();
133: setVisible(true);
134: }
135:
136: /**
137: * Saves copies of format and border into the objects that we really edit.
138: * Either may be <code>null</code>. Called from constructor and
139: * {@link #doRevert}.
140: *
141: * @param origFormat the format we are copying; not necessarily that of
142: * the field
143: * @param origBorder the border we are copying; not necessarily that of
144: * the field
145: */
146: protected void copyFormatAndBorder(Format origFormat,
147: Border origBorder) {
148: format = (Format) origFormat.clone();
149:
150: if (origBorder == null) {
151: border = (Border) field.getReport().getDefaultField()
152: .getBorder().clone();
153: border.setField(field);
154: } else
155: border = (Border) origBorder.clone();
156:
157: if (border.getTop() == null)
158: border.setTop(new BorderEdge(BorderEdge.STYLE_LINE, 1, 0));
159: if (border.getBottom() == null)
160: border
161: .setBottom(new BorderEdge(BorderEdge.STYLE_LINE, 1,
162: 0));
163: if (border.getLeft() == null)
164: border.setLeft(new BorderEdge(BorderEdge.STYLE_LINE, 1, 0));
165: if (border.getRight() == null)
166: border
167: .setRight(new BorderEdge(BorderEdge.STYLE_LINE, 1,
168: 0));
169: }
170:
171: /**
172: * Builds the window contents.
173: *
174: * @param whichTab the index of the tab to display when opened
175: */
176: protected void buildWindow(int whichTab) {
177: JTabbedPane tabbedPane = new JTabbedPane();
178: tabbedPane.addTab(I18N.get("FormatWin.format_tab"),
179: buildFormatTab());
180: tabbedPane.addTab(I18N.get("FormatWin.border_tab"),
181: buildBorderTab());
182: tabbedPane.setSelectedIndex(whichTab);
183:
184: // Ok, Apply, Revert, and Cancel Buttons
185: JPanel buttonPanel = closeButtonPanel();
186:
187: // Add edit panes and buttons to window
188: getContentPane().add(tabbedPane, BorderLayout.CENTER);
189: getContentPane().add(buttonPanel, BorderLayout.SOUTH);
190:
191: fillFormatTab();
192: fillBorderTab();
193: }
194:
195: /**
196: * Builds the format tab contents.
197: */
198: protected java.awt.Container buildFormatTab() {
199: EditFieldLayout efl = new EditFieldLayout();
200:
201: fontFamily = efl.addComboBox(I18N.get("FormatWin.font"),
202: fontChoices(), true);
203: size = efl.addComboBox(I18N.get("FormatWin.size"),
204: sizeChoices(), true);
205: bold = efl.addCheckBox(I18N.get("FormatWin.bold"),
206: KeyEvent.VK_B);
207: italic = efl.addCheckBox(I18N.get("FormatWin.italic"),
208: KeyEvent.VK_I);
209: underline = efl.addCheckBox(I18N.get("FormatWin.underline"),
210: KeyEvent.VK_U);
211: wrap = efl.addCheckBox(I18N.get("FormatWin.wrap"),
212: KeyEvent.VK_W);
213: align = efl.addComboBox(I18N.get("FormatWin.align"),
214: alignChoices());
215: formatText = efl.addTextField(I18N.get("FormatWin.format"),
216: FORMAT_TEXT_FIELD_COLS);
217: efl.add(I18N.get("FormatWin.color"), buildFieldColorWidgets());
218:
219: // Put a wrapper around that panel so everything is centered in the tab.
220: Box outerBox = Box.createVerticalBox();
221: outerBox.add(Box.createGlue());
222: outerBox.add(efl.getPanel());
223: outerBox.add(Box.createGlue());
224:
225: return outerBox;
226: }
227:
228: protected Box buildFieldColorWidgets() {
229: Box box = Box.createHorizontalBox();
230: fieldColorLabel = new JLabel(I18N.get("FormatWin.sample_text"));
231: box.add(fieldColorLabel);
232: setFieldExampleColor();
233: box.add(Box.createHorizontalStrut(16));
234: box.add(createFieldColorChooserButton());
235: return box;
236: }
237:
238: protected Box buildBorderColorWidgets() {
239: Box box = Box.createHorizontalBox();
240: borderColorLabel = new JLabel(I18N.get("FormatWin.sample_text"));
241: box.add(borderColorLabel);
242: setBorderExampleColor();
243: box.add(Box.createHorizontalStrut(16));
244: box.add(createBorderColorChooserButton());
245:
246: Box widgetBox = Box.createVerticalBox();
247: widgetBox.add(Box.createVerticalGlue());
248: widgetBox.add(new JLabel(I18N.get("FormatWin.color")));
249: widgetBox.add(Box.createVerticalStrut(6));
250: widgetBox.add(box);
251: widgetBox.add(Box.createVerticalGlue());
252:
253: return widgetBox;
254: }
255:
256: protected void setFieldExampleColor() {
257: fieldColorLabel.setForeground(format.getColor());
258: }
259:
260: protected void setBorderExampleColor() {
261: borderColorLabel.setForeground(border.getColor());
262: }
263:
264: protected JButton createFieldColorChooserButton() {
265: JButton b = new JButton(I18N.get("FormatWin.choose"));
266: b.addActionListener(new ActionListener() {
267: public void actionPerformed(ActionEvent e) {
268: Color c = JColorChooser.showDialog(FormatWin.this , I18N
269: .get("FormatWin.field_color_title"), format
270: .getColor());
271: if (c != null) {
272: format.setColor(c);
273: setFieldExampleColor();
274: }
275: }
276: });
277: return b;
278: }
279:
280: protected JButton createBorderColorChooserButton() {
281: JButton b = new JButton(I18N.get("FormatWin.choose"));
282: b.addActionListener(new ActionListener() {
283: public void actionPerformed(ActionEvent e) {
284: Color c = JColorChooser.showDialog(FormatWin.this , I18N
285: .get("FormatWin.border_color_title"), border
286: .getColor());
287: if (c != null) {
288: border.setColor(c);
289: setBorderExampleColor();
290: }
291: }
292: });
293: return b;
294: }
295:
296: /**
297: * Returns the list of font size choices, lazily instantiating it if
298: * necessary.
299: *
300: * @return an array of <code>Integer</code> size choices
301: */
302: protected Integer[] sizeChoices() {
303: if (SIZE_CHOICES == null) {
304: ArrayList list = new ArrayList();
305:
306: for (int size = 6; size <= 12; ++size)
307: list.add(new Integer(size));
308: for (int size = 14; size <= 36; size += 2)
309: list.add(new Integer(size));
310:
311: SIZE_CHOICES = new Integer[list.size()];
312: list.toArray(SIZE_CHOICES);
313: }
314: return SIZE_CHOICES;
315: }
316:
317: /**
318: * Returns the index of the specified size value in the SIZE_CHOICES list.
319: * Returns -1 if not found.
320: *
321: * @return an index, or -1 if not found
322: */
323: protected int sizeIndexOf(int size) {
324: Integer[] sizeChoices = sizeChoices();
325: for (int i = 0; i < sizeChoices.length; ++i)
326: if (sizeChoices[i].intValue() == size)
327: return i;
328: return -1;
329: }
330:
331: /**
332: * Returns the list of font choices.
333: *
334: * @return an array of font family names
335: */
336: protected String[] fontChoices() {
337: return fontFamilyNames;
338: }
339:
340: /**
341: * Returns the index of the specified font family name. Returns -1 if
342: * not found.
343: *
344: * @return an index, or -1 if not found
345: */
346: protected int fontIndexOf(String fontFamilyName) {
347: if (fontFamilyName == null || fontFamilyName.length() == 0)
348: return -1;
349:
350: String[] names = fontChoices();
351: for (int i = 0; i < names.length; ++i)
352: if (names[i].equals(fontFamilyName))
353: return i;
354:
355: return -1;
356: }
357:
358: protected String[] alignChoices() {
359: String[] choices = new String[3];
360: int i = 0;
361: choices[i++] = I18N.get("FormatWin.align_left");
362: choices[i++] = I18N.get("FormatWin.align_center");
363: choices[i++] = I18N.get("FormatWin.align_right");
364: return choices;
365: }
366:
367: protected String[] edgeCountChoices() {
368: String[] choices = new String[4];
369: for (int i = 0; i < 4; ++i)
370: choices[i] = I18N.get("FormatWin.edge_count_" + i);
371: return choices;
372: }
373:
374: protected String[] edgeStyleChoices() {
375: String[] choices = new String[3];
376: int i = 0;
377: choices[i++] = I18N.get("FormatWin.edge_style_line");
378: choices[i++] = I18N.get("FormatWin.edge_style_dashed");
379: choices[i++] = I18N.get("FormatWin.edge_style_dotted");
380: return choices;
381: }
382:
383: /**
384: * Builds the border tab contents.
385: */
386: protected Box buildBorderTab() {
387: edgeWidgets = new EdgeWidgets[4];
388:
389: Box vertBox = Box.createVerticalBox();
390:
391: Box horizBox = Box.createHorizontalBox();
392: horizBox.add(Box.createHorizontalGlue());
393: horizBox.add(buildBorderEdge(TOP, I18N
394: .get("FormatWin.edge_top"), border.getTop()));
395: horizBox.add(Box.createHorizontalGlue());
396: vertBox.add(horizBox);
397:
398: horizBox = Box.createHorizontalBox();
399: horizBox.add(buildBorderEdge(LEFT, I18N
400: .get("FormatWin.edge_left"), border.getLeft()));
401: horizBox.add(buildBorderColorWidgets());
402: horizBox.add(buildBorderEdge(RIGHT, I18N
403: .get("FormatWin.edge_right"), border.getRight()));
404: vertBox.add(horizBox);
405:
406: horizBox = Box.createHorizontalBox();
407: horizBox.add(Box.createHorizontalGlue());
408: horizBox.add(buildBorderEdge(BOTTOM, I18N
409: .get("FormatWin.edge_bottom"), border.getBottom()));
410: horizBox.add(Box.createHorizontalGlue());
411: vertBox.add(horizBox);
412:
413: return vertBox;
414: }
415:
416: /**
417: * Builds one of the edges of the border.
418: * <p>
419: * <i>Warning</i>: this code depends upon the fact that the strings in
420: * <code>edgeCountChoices</code> equals the integer value of the string
421: * (zero, one, two, etc.) and that the strings in
422: * <code>edgeStyleChoices</code> correspond to the numeric values of the
423: * <code>BorderEdge.STYLE_*</code> constants.
424: *
425: * @param edgeIndex one of <code>TOP</code>, <code>LEFT</code>, etc.
426: * @param edgeName the text name of the widget
427: * @param edge the edge we are representing visually
428: * @return one box to rule them all, one box to bind them
429: */
430: protected Box buildBorderEdge(int edgeIndex, String edgeName,
431: final BorderEdge edge) {
432: // Create container for all widgets associated with this edge
433: EdgeWidgets ew = new EdgeWidgets(edge, edgeName);
434: edgeWidgets[edgeIndex] = ew;
435:
436: EditFieldLayout efl = new EditFieldLayout();
437: ew.numberComboBox = efl.addComboBox(
438: I18N.get("FormatWin.count"), edgeCountChoices());
439: ew.styleComboBox = efl.addComboBox(I18N.get("FormatWin.style"),
440: edgeStyleChoices());
441: ew.thicknessText = efl.addTextField(I18N
442: .get("FormatWin.thickness"), THICKNESS_COLS);
443:
444: // Behavior for line count combo box
445: final JComboBox edgeBox = ew.numberComboBox;
446: edgeBox.addActionListener(new ActionListener() {
447: public void actionPerformed(ActionEvent e) {
448: String countStr = (String) edgeBox.getSelectedItem();
449: String[] choices = edgeCountChoices();
450: for (int i = 0; i < choices.length; ++i)
451: if (choices[i].equals(countStr)) {
452: edge.setNumber(i);
453: break;
454: }
455: }
456: });
457:
458: // Behavior for line style combo box
459: final JComboBox styleBox = ew.styleComboBox;
460: styleBox.addActionListener(new ActionListener() {
461: public void actionPerformed(ActionEvent e) {
462: String countStr = (String) styleBox.getSelectedItem();
463: String[] choices = edgeStyleChoices();
464: for (int i = 0; i < choices.length; ++i)
465: if (choices[i].equals(countStr)) {
466: edge.setStyle(i);
467: break;
468: }
469: }
470: });
471:
472: // Parent panel
473: Box box = Box.createVerticalBox();
474: box.add(new JLabel(ew.getName()));
475: box.add(Box.createVerticalStrut(8));
476: box.add(efl.getPanel());
477:
478: return box;
479: }
480:
481: /**
482: * Fills the format tab edit fields with values of format.
483: */
484: protected void fillFormatTab() {
485: String fontFamilyName = format.getFontFamilyName();
486: if (fontFamilyName == null)
487: fontFamilyName = "";
488: int index = fontIndexOf(fontFamilyName);
489: if (index == -1) {
490: fontFamily.setSelectedItem(null);
491: fontFamily.configureEditor(fontFamily.getEditor(),
492: fontFamilyName);
493: } else
494: fontFamily.setSelectedIndex(index);
495:
496: int sizeVal = (int) format.getSize();
497: index = sizeIndexOf(sizeVal);
498: if (index == -1) {
499: size.setSelectedItem(null);
500: size.configureEditor(size.getEditor(), "" + sizeVal);
501: } else
502: size.setSelectedIndex(index);
503:
504: bold.setSelected(format.isBold());
505: italic.setSelected(format.isItalic());
506: underline.setSelected(format.isUnderline());
507: wrap.setSelected(format.isWrap());
508:
509: // Assumes format align value == index in alignChoices()
510: align.setSelectedIndex(format.getAlign());
511:
512: formatText.setText(format.getFormat());
513: }
514:
515: /**
516: * Fills the border tab edit fields with values of border.
517: */
518: protected void fillBorderTab() {
519: for (int i = 0; i < 4; ++i) {
520: EdgeWidgets ew = edgeWidgets[i];
521:
522: // Fill count. Assumes edge number (line count) value == index in
523: // edgeCountChoices().
524: ew.numberComboBox.setSelectedIndex(ew.edge.getNumber());
525:
526: // Fill styles. Assumes edge style value == index in
527: // edgeStyleChoices().
528: ew.styleComboBox.setSelectedIndex(ew.edge.getStyle());
529:
530: // Fill thicknesses.
531: ew.thicknessText.setText("" + ew.edge.getThickness());
532: }
533: }
534:
535: protected void doSave() {
536: Object selectedFont = fontFamily.getSelectedItem();
537: if (selectedFont == null
538: || selectedFont.toString().length() == 0)
539: format.setFontFamilyName(null);
540: else
541: format.setFontFamilyName(selectedFont.toString());
542:
543: double fontSize = Double.parseDouble(size.getSelectedItem()
544: .toString());
545: format.setSize(fontSize);
546: format.setBold(bold.isSelected());
547:
548: format.setItalic(italic.isSelected());
549: format.setUnderline(underline.isSelected());
550: format.setWrap(wrap.isSelected());
551:
552: String alignText = (String) align.getSelectedItem();
553: if (alignText.equals(I18N.get("FormatWin.align_left")))
554: format.setAlign(Format.ALIGN_LEFT);
555: else if (alignText.equals(I18N.get("FormatWin.align_center")))
556: format.setAlign(Format.ALIGN_CENTER);
557: else
558: // Right
559: format.setAlign(Format.ALIGN_RIGHT);
560:
561: format.setFormat(formatText.getText());
562:
563: // The border count (number) and style save themselves, but we need to
564: // read and set the thickness.
565: for (int i = 0; i < 4; ++i) {
566: EdgeWidgets ew = edgeWidgets[i];
567: ew.edge.setThickness(Double.parseDouble(ew.thicknessText
568: .getText()));
569: }
570:
571: if (designer.countSelectedFields() == 0
572: || field == field.getReport().getDefaultField()) // "==", not "equals"
573: step(field);
574: else
575: // Call step() for all selected fields
576: designer.withSelectedFieldsDo(this );
577: }
578:
579: /**
580: * Creates and performs a command that gives the format and borders to the
581: * specified field.
582: *
583: * @param f the field
584: */
585: public void step(Field f) {
586: FormatCommand cmd = new FormatCommand(f, format, border);
587: cmd.perform();
588: commands.add(cmd);
589: }
590:
591: protected void doRevert() {
592: fillFormatTab();
593: fillBorderTab();
594: }
595:
596: }
|