001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.workbench.ui;
034:
035: import java.awt.BorderLayout;
036: import java.awt.Color;
037: import java.awt.Component;
038: import java.awt.GridBagConstraints;
039: import java.awt.GridBagLayout;
040: import java.awt.Insets;
041: import java.awt.Point;
042: import java.awt.Rectangle;
043: import java.awt.event.ActionEvent;
044: import java.awt.event.ActionListener;
045: import java.awt.event.MouseAdapter;
046: import java.awt.event.MouseEvent;
047: import java.util.ArrayList;
048: import java.util.Collection;
049: import java.util.Iterator;
050:
051: import javax.swing.BorderFactory;
052: import javax.swing.DefaultCellEditor;
053: import javax.swing.Icon;
054: import javax.swing.JButton;
055: import javax.swing.JCheckBox;
056: import javax.swing.JComboBox;
057: import javax.swing.JComponent;
058: import javax.swing.JLabel;
059: import javax.swing.JList;
060: import javax.swing.JPanel;
061: import javax.swing.JPopupMenu;
062: import javax.swing.JScrollPane;
063: import javax.swing.JTable;
064: import javax.swing.JTextField;
065: import javax.swing.JToolBar;
066: import javax.swing.ListCellRenderer;
067: import javax.swing.SwingUtilities;
068: import javax.swing.border.Border;
069: import javax.swing.border.LineBorder;
070: import javax.swing.event.ListSelectionEvent;
071: import javax.swing.event.ListSelectionListener;
072: import javax.swing.event.TableModelEvent;
073: import javax.swing.event.TableModelListener;
074: import javax.swing.table.TableCellRenderer;
075: import javax.swing.table.TableColumn;
076:
077: import com.vividsolutions.jts.util.Assert;
078: import com.vividsolutions.jump.I18N;
079: import com.vividsolutions.jump.feature.AttributeType;
080: import com.vividsolutions.jump.util.StringUtil;
081: import com.vividsolutions.jump.workbench.WorkbenchContext;
082: import com.vividsolutions.jump.workbench.model.CategoryEvent;
083: import com.vividsolutions.jump.workbench.model.FeatureEvent;
084: import com.vividsolutions.jump.workbench.model.Layer;
085: import com.vividsolutions.jump.workbench.model.LayerEvent;
086: import com.vividsolutions.jump.workbench.model.LayerEventType;
087: import com.vividsolutions.jump.workbench.model.LayerListener;
088: import com.vividsolutions.jump.workbench.plugin.AbstractPlugIn;
089: import com.vividsolutions.jump.workbench.plugin.EnableCheck;
090: import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck;
091: import com.vividsolutions.jump.workbench.plugin.PlugIn;
092: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
093: import com.vividsolutions.jump.workbench.ui.addremove.AddRemovePanel;
094: import com.vividsolutions.jump.workbench.ui.cursortool.editing.EditingPlugIn;
095: import com.vividsolutions.jump.workbench.ui.images.IconLoader;
096: import com.vividsolutions.jump.workbench.ui.plugin.EditablePlugIn;
097: import com.vividsolutions.jump.workbench.ui.plugin.FeatureInstaller;
098:
099: public class SchemaPanel extends JPanel {
100: private JPanel jPanel3 = new JPanel();
101: private JPanel jPanel1 = new JPanel();
102: private GridBagLayout gridBagLayout1 = new GridBagLayout();
103: private JLabel statusLabel = new JLabel();
104: private Layer layer;
105: private Point currentClickPoint;
106: private JPopupMenu popupMenu = new JPopupMenu();
107: private GridBagLayout gridBagLayout2 = new GridBagLayout();
108: private JPanel buttonPanel = new JPanel();
109: private JButton applyButton = new JButton();
110: private JCheckBox forceInvalidConversionsToNullCheckBox = new JCheckBox();
111: private GridBagLayout gridBagLayout3 = new GridBagLayout();
112: private JPanel jPanel2 = new JPanel();
113: private Border border1;
114: private boolean modified = false;
115: private ArrayList listeners = new ArrayList();
116: private JButton revertButton = new JButton();
117: private BorderLayout borderLayout1 = new BorderLayout();
118: private WorkbenchToolBar toolBar = new WorkbenchToolBar(null) {
119: public JButton addPlugIn(Icon icon, PlugIn plugIn,
120: EnableCheck enableCheck,
121: WorkbenchContext workbenchContext) {
122: return super .addPlugIn(icon, addCleanUp(plugIn),
123: enableCheck, workbenchContext);
124: }
125:
126: };
127:
128: private void setModel(SchemaTableModel model) {
129: table.setModel(model);
130: table.getModel().addTableModelListener(
131: new TableModelListener() {
132: public void tableChanged(TableModelEvent e) {
133: updateComponents();
134: }
135: });
136: //Must init cell editors every time we init model. [Jon Aquino]
137: initCellEditors();
138: updateComponents();
139: }
140:
141: public SchemaPanel(final Layer layer, EditingPlugIn editingPlugIn,
142: WorkbenchContext context) {
143: editablePlugIn = new EditablePlugIn(editingPlugIn);
144: try {
145: table.getSelectionModel().addListSelectionListener(
146: new ListSelectionListener() {
147: public void valueChanged(ListSelectionEvent e) {
148: updateComponents();
149: }
150: });
151: this .layer = layer;
152: jbInit();
153: //Call #initPopupMenu and #initToolBar before #setModel, because #setModel
154: //calls #updateComponents. [Jon Aquino]
155: initPopupMenu(context);
156: initToolBar(context);
157: setModel(new SchemaTableModel(layer));
158: layer.getLayerManager().addLayerListener(
159: new LayerListener() {
160: public void categoryChanged(CategoryEvent e) {
161: }
162:
163: public void featuresChanged(FeatureEvent e) {
164: }
165:
166: public void layerChanged(LayerEvent e) {
167: if (e.getLayerable() != layer) {
168: return;
169: }
170:
171: if (e.getType() == LayerEventType.METADATA_CHANGED) {
172: //If layer becomes editable, apply row striping and remove gridlines,
173: //as recommended in Java Look and Feel Design Guidelines: Advanced Topics [Jon Aquino]
174: updateComponents();
175: repaint();
176: }
177: }
178: });
179: } catch (Exception ex) {
180: Assert.shouldNeverReachHere(ex.toString());
181: }
182: }
183:
184: private int[] rowsToActOn() {
185: if (table.getSelectedRowCount() > 0) {
186: return table.getSelectedRows();
187: }
188: if (getCurrentClickPoint() != null
189: && table.rowAtPoint(getCurrentClickPoint()) != -1) {
190: return new int[] { table.rowAtPoint(getCurrentClickPoint()) };
191: }
192: return new int[] {};
193: }
194:
195: private EnableCheck basicEnableCheck = new EnableCheck() {
196: public String check(JComponent component) {
197: if (!layer.isEditable()) {
198: return I18N
199: .get("ui.SchemaPanel.layer-must-be-editable");
200: }
201: if (rowsToActOn().length == 0) {
202: return I18N
203: .get("ui.SchemaPanel.at-least-1-row-must-be-selected");
204: }
205: return null;
206: }
207: };
208:
209: private abstract class MyPlugIn extends AbstractPlugIn {
210: public MultiEnableCheck createEnableCheck() {
211: return new MultiEnableCheck().add(basicEnableCheck);
212: }
213:
214: public abstract Icon getIcon();
215: }
216:
217: private MyPlugIn insertPlugIn = new MyPlugIn() {
218: public String getName() {
219: return I18N.get("ui.SchemaPanel.insert");
220: }
221:
222: public Icon getIcon() {
223: return GUIUtil.toSmallIcon(IconLoader.icon("Plus.gif"));
224: }
225:
226: public boolean execute(PlugInContext context) throws Exception {
227: markAsModified();
228: getModel().insertBlankRow(rowsToActOn()[0]);
229: return true;
230: }
231: };
232:
233: private MyPlugIn deletePlugIn = new MyPlugIn() {
234: public String getName() {
235: return I18N.get("ui.SchemaPanel.delete");
236: }
237:
238: public Icon getIcon() {
239: return GUIUtil.toSmallIcon(IconLoader.icon("Delete.gif"));
240: }
241:
242: public boolean execute(PlugInContext context) throws Exception {
243: markAsModified();
244: getModel().removeFields(rowsToActOn());
245: return true;
246: }
247: };
248:
249: private MyPlugIn moveUpPlugIn = new MyPlugIn() {
250: public Icon getIcon() {
251: return GUIUtil.toSmallIcon(IconLoader.icon("VCRUp.gif"));
252: }
253:
254: public String getName() {
255: return I18N.get("ui.SchemaPanel.move-field-up");
256: }
257:
258: public boolean execute(PlugInContext context) throws Exception {
259: markAsModified();
260: move(AddRemovePanel.itemsToMoveUp(getModel().getFields(),
261: toFields(rowsToActOn())), -1);
262: return true;
263: }
264:
265: public MultiEnableCheck createEnableCheck() {
266: return super .createEnableCheck().add(new EnableCheck() {
267: public String check(JComponent component) {
268: return min(rowsToActOn()) == 0 ? I18N
269: .get("ui.SchemaPanel.field-is-already-at-the-top")
270: : null;
271: //No corresponding check in moveDownPlugIn because there is no
272: //bottom! (We keep adding rows as necessary) [Jon Aquino]
273: }
274: });
275: }
276: };
277: private EditablePlugIn editablePlugIn;
278:
279: private MyPlugIn moveDownPlugIn = new MyPlugIn() {
280: public Icon getIcon() {
281: return GUIUtil.toSmallIcon(IconLoader.icon("VCRDown.gif"));
282: }
283:
284: public String getName() {
285: return I18N.get("ui.SchemaPanel.move-field-down");
286: }
287:
288: public boolean execute(PlugInContext context) throws Exception {
289: markAsModified();
290: move(AddRemovePanel.itemsToMoveDown(getModel().getFields(),
291: toFields(rowsToActOn())), 1);
292: return true;
293: }
294: };
295: private JScrollPane jScrollPane1 = new JScrollPane();
296: private JTable table = new JTable();
297:
298: private void initToolBar(WorkbenchContext context) {
299: toolBar.addPlugIn(insertPlugIn.getIcon(), insertPlugIn,
300: insertPlugIn.createEnableCheck(), context);
301: toolBar.addPlugIn(deletePlugIn.getIcon(), deletePlugIn,
302: deletePlugIn.createEnableCheck(), context);
303: toolBar.addPlugIn(moveUpPlugIn.getIcon(), moveUpPlugIn,
304: moveUpPlugIn.createEnableCheck(), context);
305: toolBar.addPlugIn(moveDownPlugIn.getIcon(), moveDownPlugIn,
306: moveDownPlugIn.createEnableCheck(), context);
307: }
308:
309: private void initPopupMenu(WorkbenchContext context) {
310: table.addMouseListener(new MouseAdapter() {
311: public void mouseReleased(MouseEvent e) {
312: setCurrentClickPoint(e.getPoint());
313:
314: if (SwingUtilities.isRightMouseButton(e)) {
315: popupMenu
316: .show(e.getComponent(), e.getX(), e.getY());
317: }
318:
319: }
320: });
321: addPopupMenuItem(editablePlugIn, true, null, editablePlugIn
322: .createEnableCheck(context), context);
323: popupMenu.addSeparator();
324: addPopupMenuItem(insertPlugIn, false, insertPlugIn.getIcon(),
325: insertPlugIn.createEnableCheck(), context);
326: addPopupMenuItem(deletePlugIn, false, deletePlugIn.getIcon(),
327: deletePlugIn.createEnableCheck(), context);
328: popupMenu.addSeparator();
329: addPopupMenuItem(moveUpPlugIn, false, moveUpPlugIn.getIcon(),
330: moveUpPlugIn.createEnableCheck(), context);
331: addPopupMenuItem(moveDownPlugIn, false, moveDownPlugIn
332: .getIcon(), moveDownPlugIn.createEnableCheck(), context);
333: }
334:
335: private void addPopupMenuItem(PlugIn plugIn, boolean checkBox,
336: Icon icon, EnableCheck enableCheck, WorkbenchContext context) {
337: FeatureInstaller installer = new FeatureInstaller(context);
338: installer.addPopupMenuItem(popupMenu, addCleanUp(plugIn),
339: plugIn.getName(), checkBox, icon, enableCheck);
340: }
341:
342: private PlugIn addCleanUp(final PlugIn plugIn) {
343: return new PlugIn() {
344: public String toString() {
345: return plugIn.toString();
346: }
347:
348: public boolean execute(PlugInContext context)
349: throws Exception {
350: try {
351: return plugIn.execute(context);
352: } finally {
353: setCurrentClickPoint(null);
354: updateComponents();
355: }
356: }
357:
358: public void initialize(PlugInContext context)
359: throws Exception {
360: plugIn.initialize(context);
361: }
362:
363: public String getName() {
364: return plugIn.getName();
365: }
366: };
367: }
368:
369: public boolean isModified() {
370: return modified;
371: }
372:
373: private Collection toFields(int[] rows) {
374: ArrayList fields = new ArrayList();
375:
376: for (int i = 0; i < rows.length; i++) {
377: fields.add(getModel().get(rows[i]));
378: }
379:
380: return fields;
381: }
382:
383: private void updateComponents() {
384: table.setShowGrid(layer.isEditable());
385: applyButton.setEnabled(layer.isEditable());
386: revertButton.setEnabled(layer.isEditable());
387: forceInvalidConversionsToNullCheckBox.setEnabled(layer
388: .isEditable());
389: reportError(validateInput());
390: toolBar.updateEnabledState();
391: }
392:
393: public SchemaTableModel getModel() {
394: return (SchemaTableModel) table.getModel();
395: }
396:
397: private TableColumn fieldNameColumn() {
398: return table.getColumnModel().getColumn(
399: getModel().indexOfColumn(
400: SchemaTableModel.FIELD_NAME_COLUMN_NAME));
401: }
402:
403: private TableColumn dataTypeColumn() {
404: return table.getColumnModel().getColumn(
405: getModel().indexOfColumn(
406: SchemaTableModel.DATA_TYPE_COLUMN_NAME));
407: }
408:
409: private void initCellEditors() {
410: fieldNameColumn().setCellEditor(new MyFieldNameEditor());
411: dataTypeColumn()
412: .setCellEditor(
413: new MyDataTypeEditor(AttributeType.allTypes()
414: .toArray()));
415: fieldNameColumn().setCellRenderer(
416: new StripingRenderer(table
417: .getDefaultRenderer(String.class)));
418: dataTypeColumn().setCellRenderer(
419: new StripingRenderer(new TableCellRenderer() {
420: public Component getTableCellRendererComponent(
421: JTable table, Object value,
422: boolean isSelected, boolean hasFocus,
423: int row, int column) {
424: return table
425: .getDefaultRenderer(String.class)
426: .getTableCellRendererComponent(
427: table,
428: (value != null) ? capitalizeFirstLetter(value
429: .toString())
430: : null, isSelected,
431: hasFocus, row, column);
432: }
433: }));
434: table.getModel().addTableModelListener(
435: new TableModelListener() {
436: public void tableChanged(TableModelEvent e) {
437: for (int i = 0; i < table.getColumnCount(); i++) {
438: ((MyEditor) table.getColumnModel()
439: .getColumn(i).getCellEditor())
440: .cancelCellEditing();
441: }
442: }
443: });
444: }
445:
446: private String capitalizeFirstLetter(String string) {
447: return string.toUpperCase().charAt(0)
448: + string.toLowerCase().substring(1);
449: }
450:
451: void jbInit() throws Exception {
452: toolBar.setOrientation(JToolBar.VERTICAL);
453: border1 = BorderFactory.createEtchedBorder(Color.white,
454: new Color(148, 145, 140));
455: this .setLayout(gridBagLayout2);
456: jPanel1.setLayout(gridBagLayout1);
457: statusLabel.setBorder(BorderFactory.createLoweredBevelBorder());
458: statusLabel.setText(" ");
459: applyButton.setText(I18N.get("ui.SchemaPanel.apply-changes"));
460: applyButton
461: .addActionListener(new java.awt.event.ActionListener() {
462: public void actionPerformed(ActionEvent e) {
463: applyButton_actionPerformed(e);
464: }
465: });
466: forceInvalidConversionsToNullCheckBox
467: .setToolTipText(I18N
468: .get("ui.SchemaPanel.leave-unchecked-if-you-want-to-be-notified-of-conversion-errors"));
469: forceInvalidConversionsToNullCheckBox
470: .setText(I18N
471: .get("ui.SchemaPanel.force-invalid-conversions-to-null"));
472: buttonPanel.setLayout(gridBagLayout3);
473: buttonPanel.setBorder(BorderFactory.createLoweredBevelBorder());
474: revertButton.setText(I18N.get("ui.SchemaPanel.revert-changes"));
475: revertButton
476: .addActionListener(new java.awt.event.ActionListener() {
477: public void actionPerformed(ActionEvent e) {
478: revertButton_actionPerformed(e);
479: }
480: });
481: jPanel3.setLayout(borderLayout1);
482: this .add(jPanel3, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
483: GridBagConstraints.CENTER, GridBagConstraints.BOTH,
484: new Insets(0, 0, 0, 0), 0, 0));
485: jPanel3.add(toolBar, BorderLayout.WEST);
486: jPanel3.add(jScrollPane1, BorderLayout.CENTER);
487: jScrollPane1.getViewport().add(table, null);
488: this .add(jPanel1, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0,
489: GridBagConstraints.CENTER,
490: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
491: 0, 0));
492: jPanel1.add(statusLabel, new GridBagConstraints(0, 0, 1, 1,
493: 1.0, 0.0, GridBagConstraints.CENTER,
494: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
495: 0, 0));
496: this .add(buttonPanel, new GridBagConstraints(0, 2, 1, 1, 1.0,
497: 0.0, GridBagConstraints.CENTER,
498: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
499: 0, 0));
500: buttonPanel.add(applyButton, new GridBagConstraints(1, 1, 1, 1,
501: 0.0, 0.0, GridBagConstraints.CENTER,
502: GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0));
503: buttonPanel.add(forceInvalidConversionsToNullCheckBox,
504: new GridBagConstraints(4, 1, 1, 1, 0.0, 0.0,
505: GridBagConstraints.CENTER,
506: GridBagConstraints.NONE,
507: new Insets(0, 0, 0, 0), 0, 0));
508: buttonPanel.add(jPanel2, new GridBagConstraints(3, 1, 1, 1,
509: 1.0, 0.0, GridBagConstraints.CENTER,
510: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
511: 0, 0));
512: buttonPanel.add(revertButton, new GridBagConstraints(2, 1, 1,
513: 1, 0.0, 0.0, GridBagConstraints.CENTER,
514: GridBagConstraints.NONE, new Insets(0, 0, 0, 4), 0, 0));
515: }
516:
517: private void reportError(String message) {
518: if (message != null) {
519: statusLabel.setText(message);
520: statusLabel.setIcon(GUIUtil.toSmallIcon(IconLoader
521: .icon("Delete.gif")));
522: } else {
523: statusLabel.setText(" ");
524: statusLabel.setIcon(null);
525: }
526: }
527:
528: private int geometryCount() {
529: int geometryCount = 0;
530:
531: for (int i = 0; i < getModel().getRowCount(); i++) {
532: if (getModel().get(i).getType() == AttributeType.GEOMETRY) {
533: geometryCount++;
534: }
535: }
536:
537: return geometryCount;
538: }
539:
540: public String validateInput() {
541: for (int i = 0; i < table.getColumnCount(); i++) {
542: String error = ((MyEditor) table.getColumnModel()
543: .getColumn(i).getCellEditor())
544: .getCurrentErrorMessage();
545:
546: if (error != null) {
547: return error;
548: }
549: }
550:
551: if (geometryCount() > 1) {
552: return I18N
553: .get("ui.SchemaPanel.only-one-geometry-field-is-allowed");
554: }
555:
556: if (geometryCount() == 0) {
557: return I18N
558: .get("ui.SchemaPanel.a-geometry-field-must-be-defined");
559: }
560:
561: return null;
562: }
563:
564: private String validate(int row, AttributeType type) {
565: if (type == AttributeType.GEOMETRY) {
566: for (int i = 0; i < getModel().getRowCount(); i++) {
567: if (i == row) {
568: continue;
569: }
570:
571: if (getModel().get(i).getType() == null) {
572: //One of the blank rows. [Jon Aquino]
573: continue;
574: }
575: }
576: }
577:
578: return null;
579: }
580:
581: private String validate(int row, String name) {
582: if (name.trim().length() == 0) {
583: return I18N
584: .get("ui.SchemaPanel.field-name-cannot-be-blank");
585: }
586:
587: //Existing fields are already trimmed. [Jon Aquino]
588: for (int i = 0; i < getModel().getRowCount(); i++) {
589: if (i == row) {
590: continue;
591: }
592:
593: if (getModel().get(i).getName() == null) {
594: //One of the blank rows. [Jon Aquino]
595: continue;
596: }
597:
598: if (getModel().get(i).getName().equalsIgnoreCase(
599: name.trim())) {
600: return I18N
601: .get("ui.SchemaPanel.field-name-already-exists")
602: + ": " + name;
603: }
604: }
605:
606: return null;
607: }
608:
609: private void markAsModified() {
610: modified = true;
611: }
612:
613: public void markAsUnmodified() {
614: modified = false;
615: }
616:
617: public JTable getTable() {
618: return table;
619: }
620:
621: void applyButton_actionPerformed(ActionEvent e) {
622: fireActionPerformed();
623: }
624:
625: public void add(ActionListener l) {
626: listeners.add(l);
627: }
628:
629: private void fireActionPerformed() {
630: for (Iterator i = listeners.iterator(); i.hasNext();) {
631: ActionListener l = (ActionListener) i.next();
632: l.actionPerformed(null);
633: }
634: }
635:
636: public boolean isForcingInvalidConversionsToNull() {
637: return forceInvalidConversionsToNullCheckBox.isSelected();
638: }
639:
640: public void move(Collection fieldsToMove, int displacement) {
641: //Use rows-to-act-on, not selected row, because no rows may be selected
642: //i.e. we might just be operating on the row the user right-clicked on. [Jon Aquino]
643: int guaranteedVisibleRow = displacement > 0 ? max(rowsToActOn())
644: : min(rowsToActOn());
645: guaranteedVisibleRow += displacement;
646: //Compute guaranteedVisibleRow before doing the move, because after the
647: //move the selection would have moved, or if a row were merely clicked and not
648: //selected, its click point would *not* have moved -- would tricky to compute it
649: //after the move! [Jon Aquino]
650:
651: ArrayList selectedFields = new ArrayList();
652: int[] selectedRows = table.getSelectedRows();
653:
654: for (int i = 0; i < selectedRows.length; i++) {
655: selectedFields.add(getModel().get(selectedRows[i]));
656: }
657:
658: getModel().move(fieldsToMove, displacement);
659: table.clearSelection();
660:
661: for (Iterator i = selectedFields.iterator(); i.hasNext();) {
662: SchemaTableModel.Field field = (SchemaTableModel.Field) i
663: .next();
664: table.addRowSelectionInterval(getModel().indexOf(field),
665: getModel().indexOf(field));
666: }
667: Rectangle r = table.getCellRect(guaranteedVisibleRow, 0, true);
668: table.scrollRectToVisible(r);
669: }
670:
671: private int min(int[] ints) {
672: int min = ints[0];
673: for (int i = 0; i < ints.length; i++) {
674: min = Math.min(min, ints[i]);
675: }
676: return min;
677: }
678:
679: private int max(int[] ints) {
680: int max = ints[0];
681: for (int i = 0; i < ints.length; i++) {
682: max = Math.max(max, ints[i]);
683: }
684: return max;
685: }
686:
687: private class StripingRenderer implements TableCellRenderer {
688: private TableCellRenderer originalRenderer;
689:
690: //Row-stripe colour recommended in
691: //Java Look and Feel Design Guidelines: Advanced Topics [Jon Aquino]
692: private final Color LIGHT_GRAY = new Color(230, 230, 230);
693:
694: public StripingRenderer(TableCellRenderer originalRenderer) {
695: this .originalRenderer = originalRenderer;
696: }
697:
698: public Component getTableCellRendererComponent(JTable table,
699: Object value, boolean isSelected, boolean hasFocus,
700: int row, int column) {
701: JComponent component = (JComponent) originalRenderer
702: .getTableCellRendererComponent(table, value,
703: isSelected, hasFocus, row, column);
704:
705: //If not editable, use row striping, as recommended in
706: //Java Look and Feel Design Guidelines: Advanced Topics [Jon Aquino]
707: component.setOpaque(true);
708:
709: if (!isSelected) {
710: component.setForeground(Color.black);
711: component
712: .setBackground((layer.isEditable() || ((row % 2) == 0)) ? Color.white
713: : LIGHT_GRAY);
714: }
715:
716: return component;
717: }
718: }
719:
720: public class MyDataTypeEditor extends MyEditor {
721: private AttributeType originalType;
722:
723: public MyDataTypeEditor(Object[] items) {
724: super (new JComboBox(items));
725:
726: final ListCellRenderer originalRenderer = comboBox()
727: .getRenderer();
728: comboBox().setRenderer(new ListCellRenderer() {
729: public Component getListCellRendererComponent(
730: JList list, Object value, int index,
731: boolean isSelected, boolean cellHasFocus) {
732: return originalRenderer
733: .getListCellRendererComponent(
734: list,
735: (value != null) ? capitalizeFirstLetter(value
736: .toString())
737: : null, index, isSelected,
738: cellHasFocus);
739: }
740: });
741: }
742:
743: public Component getTableCellEditorComponent(JTable table,
744: Object value, boolean isSelected, int row, int column) {
745: originalType = (AttributeType) value;
746: return super .getTableCellEditorComponent(table, value,
747: isSelected, row, column);
748: }
749:
750: private JComboBox comboBox() {
751: return (JComboBox) getComponent();
752: }
753:
754: public boolean stopCellEditing() {
755: if (originalType != comboBox().getSelectedItem()) {
756: markAsModified();
757: }
758: return super .stopCellEditing();
759: }
760:
761: protected String validate() {
762: return SchemaPanel.this .validate(row,
763: (AttributeType) comboBox().getSelectedItem());
764: }
765: }
766:
767: public abstract class MyEditor extends DefaultCellEditor {
768: protected int row;
769: private String currentErrorMessage = null;
770:
771: public MyEditor(JComboBox comboBox) {
772: super (comboBox);
773: }
774:
775: public MyEditor(JTextField textField) {
776: super (textField);
777: }
778:
779: public Component getTableCellEditorComponent(JTable table,
780: Object value, boolean isSelected, int row, int column) {
781: this .row = row;
782: ((JComponent) getComponent()).setBorder(new LineBorder(
783: Color.black));
784:
785: return super .getTableCellEditorComponent(table, value,
786: isSelected, row, column);
787: }
788:
789: public void cancelCellEditing() {
790: currentErrorMessage = null;
791: updateComponents();
792: super .cancelCellEditing();
793: }
794:
795: public boolean stopCellEditing() {
796: try {
797: if (validate() != null) {
798: ((JComponent) getComponent())
799: .setBorder(new LineBorder(Color.red));
800:
801: return false;
802: }
803:
804: return super .stopCellEditing();
805: } finally {
806: //Can't just call #validate at the top of this method, because other validations
807: //apply to when the edit is finished (e.g. checking the number of geometries). [Jon Aquino]
808: currentErrorMessage = validate();
809: updateComponents();
810: }
811: }
812:
813: protected abstract String validate();
814:
815: public String getCurrentErrorMessage() {
816: return currentErrorMessage;
817: }
818: }
819:
820: public class MyFieldNameEditor extends MyEditor {
821: public MyFieldNameEditor() {
822: super (new JTextField());
823: }
824:
825: public boolean stopCellEditing() {
826: if (!textField().getText().equals(originalText)) {
827: markAsModified();
828: }
829: return super .stopCellEditing();
830: }
831:
832: private String originalText;
833:
834: public Component getTableCellEditorComponent(JTable table,
835: Object value, boolean isSelected, int row, int column) {
836: originalText = (String) value;
837: return super .getTableCellEditorComponent(table, value,
838: isSelected, row, column);
839: }
840:
841: private JTextField textField() {
842: return (JTextField) getComponent();
843: }
844:
845: protected String validate() {
846: return SchemaPanel.this
847: .validate(row, textField().getText());
848: }
849: }
850:
851: void revertButton_actionPerformed(ActionEvent e) {
852: setModel(new SchemaTableModel(layer));
853: }
854:
855: private void setCurrentClickPoint(Point currentClickPoint) {
856: this .currentClickPoint = currentClickPoint;
857: }
858:
859: private Point getCurrentClickPoint() {
860: return currentClickPoint;
861: }
862: }
|