001: package net.sourceforge.squirrel_sql.fw.datasetviewer;
002:
003: /*
004: * Copyright (C) 2001-2002 Colin Bell
005: * colbell@users.sourceforge.net
006: * Modifications copyright (C) 2001-2002 Johan Compagner
007: * jcompagner@j-com.nl
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: */
023:
024: import java.awt.BorderLayout;
025: import java.awt.Container;
026: import java.awt.event.ActionListener;
027: import java.awt.event.ActionEvent;
028: import java.awt.Component;
029: import java.awt.Color;
030: import java.awt.event.MouseAdapter;
031: import java.awt.event.MouseEvent;
032: import java.awt.Point;
033: import java.awt.Dimension;
034:
035: import javax.swing.JInternalFrame;
036: import javax.swing.JButton;
037: import javax.swing.JTextArea;
038: import javax.swing.JPanel;
039: import javax.swing.WindowConstants;
040: import javax.swing.JScrollPane;
041: import javax.swing.JOptionPane;
042: import javax.swing.BorderFactory;
043: import javax.swing.JTable;
044: import javax.swing.table.DefaultTableModel;
045: import javax.swing.table.TableCellRenderer;
046: import javax.swing.table.TableColumnModel;
047: import javax.swing.table.DefaultTableColumnModel;
048: import javax.swing.table.TableColumn;
049:
050: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.CellComponentFactory;
051: import net.sourceforge.squirrel_sql.fw.sql.JDBCTypeMapper;
052: import net.sourceforge.squirrel_sql.fw.util.StringManager;
053: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
054:
055: /**
056: * @author gwg
057: *
058: * This is the frame that gets data from the user for creating
059: * a new row in a table.
060: */
061: public class RowDataInputFrame extends JInternalFrame implements
062: ActionListener {
063:
064: private static final StringManager s_stringMgr = StringManagerFactory
065: .getStringManager(RowDataInputFrame.class);
066:
067: // object that called us and that we want to return data to when done
068: DataSetViewerEditableTablePanel _caller;
069:
070: // the table containing the user's input
071: RowDataJTable table;
072:
073: /**
074: * ctor.
075: */
076: public RowDataInputFrame(ColumnDisplayDefinition[] colDefs,
077: Object[] initialValues,
078: DataSetViewerEditableTablePanel caller) {
079:
080: // i18n[rowDataInputFrame.propName=Input New Row Data]
081: super (s_stringMgr.getString("rowDataInputFrame.propName"),
082: true, true, false, true);
083:
084: // get the ConentPane into a variable for convenience
085: Container pane = getContentPane();
086:
087: // save data passed in to us
088: _caller = caller;
089:
090: // set layout
091: pane.setLayout(new BorderLayout());
092:
093: // create the JTable for input and put in the top of window
094: table = new RowDataJTable(colDefs, initialValues);
095: // tell scrollpane to use table size with the height adjusted to leave
096: // room for the scrollbar at the bottom if needed
097: Dimension tableDim = table.getPreferredSize();
098: tableDim
099: .setSize(tableDim.getWidth(), tableDim.getHeight() + 15);
100: table.setPreferredScrollableViewportSize(tableDim);
101: JScrollPane scrollPane = new JScrollPane(table);
102:
103: // add row headers to help user understand what the second row is
104: JPanel rowHeaderPanel = new JPanel();
105: rowHeaderPanel.setLayout(new BorderLayout());
106: // i18n[rowDataInputFrame.data=Data]
107: JTextArea r1 = new JTextArea(s_stringMgr
108: .getString("rowDataInputFrame.data"), 1, 10);
109: r1.setBackground(Color.lightGray);
110: r1.setBorder(BorderFactory.createLineBorder(Color.black));
111: r1.setEditable(false);
112: rowHeaderPanel.add(r1, BorderLayout.NORTH);
113: // i18n[rowDataInputFrame.colDescription=\nColumn\nDescription\n]
114: JTextArea r2 = new JTextArea(s_stringMgr
115: .getString("rowDataInputFrame.colDescription"), 4, 10);
116: r2.setBackground(Color.lightGray);
117: r2.setBorder(BorderFactory.createLineBorder(Color.black));
118: r2.setEditable(false);
119: rowHeaderPanel.add(r2, BorderLayout.CENTER);
120: scrollPane.setRowHeaderView(rowHeaderPanel);
121:
122: pane.add(scrollPane, BorderLayout.NORTH);
123:
124: // create the buttons for input done and cancel
125: JPanel buttonPanel = new JPanel();
126:
127: // i18n[rowDataInputFrame.insert=Insert]
128: JButton insertButton = new JButton(s_stringMgr
129: .getString("rowDataInputFrame.insert"));
130: buttonPanel.add(insertButton);
131: insertButton.setActionCommand("insert");
132: insertButton.addActionListener(this );
133:
134: // i18n[rowDataInputFrame.cancel=Cancel]
135: JButton cancelButton = new JButton(s_stringMgr
136: .getString("rowDataInputFrame.cancel"));
137: buttonPanel.add(cancelButton);
138: cancelButton.setActionCommand("cancel");
139: cancelButton.addActionListener(this );
140:
141: pane.add(buttonPanel, BorderLayout.SOUTH);
142:
143: // this frame should really go away when done
144: setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
145:
146: // display the frame
147: pack();
148: show();
149:
150: }
151:
152: /**
153: * Handle actions on the buttons
154: */
155: public void actionPerformed(ActionEvent e) {
156:
157: if (e.getActionCommand().equals("cancel")) {
158: setVisible(false);
159: dispose();
160: return;
161: } else if (!e.getActionCommand().equals("insert")) {
162: return; // do not recognize this button request
163: }
164:
165: // user said to insert, so collect all the data from the
166: // JTable and send it to the DataSetViewer for insertion
167: // into DB and on-screen tables
168:
169: // first make sure that user's last input has been included
170: // (It is too easy for user to enter data and forget to click
171: // on another field to force it to be set.)
172: if (table.isEditing()) {
173: int col = table.getEditingColumn();
174: table.getCellEditor(0, col).stopCellEditing();
175: }
176:
177: Object[] rowData = new Object[table.getModel().getColumnCount()];
178: for (int i = 0; i < table.getModel().getColumnCount(); i++) {
179: rowData[i] = table.getValueAt(0, i);
180: }
181:
182: // put the data into the DB and the on-screen JTable.
183: // If there was a failure, do not make this form
184: // go away since the user may be able to fix the problem
185: // by changing the data.
186: if (_caller.insertRow(rowData) == null) {
187: // the insert worked, so make this input form go away
188: setVisible(false);
189: dispose();
190: }
191: }
192:
193: /**
194: * JTable for use in creating data for insertion.
195: */
196: class RowDataJTable extends JTable {
197:
198: private ColumnDisplayDefinition[] _colDefs = null;
199:
200: /**
201: * constructor
202: */
203: protected RowDataJTable(ColumnDisplayDefinition[] colDefs,
204: Object[] initalValues) {
205:
206: super ();
207: setModel(new RowDataModel(colDefs, initalValues));
208:
209: // create column model
210:
211: final String data = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG";
212: final int _multiplier = getFontMetrics(getFont())
213: .stringWidth(data)
214: / data.length();
215:
216: TableColumnModel cm = new DefaultTableColumnModel();
217: for (int i = 0; i < colDefs.length; ++i) {
218: ColumnDisplayDefinition colDef = colDefs[i];
219: int colWidth = colDef.getDisplayWidth() * _multiplier;
220: if (colWidth > IDataSetViewer.MAX_COLUMN_WIDTH
221: * _multiplier) {
222: colWidth = IDataSetViewer.MAX_COLUMN_WIDTH
223: * _multiplier;
224: }
225:
226: TableColumn col = new TableColumn(i, colWidth,
227: CellComponentFactory
228: .getTableCellRenderer(colDefs[i]), null);
229: col.setHeaderValue(colDef.getLabel());
230: cm.addColumn(col);
231: }
232:
233: setColumnModel(cm);
234:
235: _colDefs = colDefs;
236:
237: // set up cell editors on first row
238: for (int i = 0; i < colDefs.length; i++) {
239: cm.getColumn(i).setCellEditor(
240: CellComponentFactory.getInCellEditor(this ,
241: _colDefs[i]));
242: }
243:
244: // the second row contains a multi-line description,
245: // so make that row high enough to display it
246: setRowHeight(1, 80);
247:
248: setRowSelectionAllowed(false);
249: setColumnSelectionAllowed(false);
250: setCellSelectionEnabled(true);
251: getTableHeader().setResizingAllowed(true);
252: getTableHeader().setReorderingAllowed(true);
253: setAutoCreateColumnsFromModel(false);
254: setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
255:
256: //?? Future: may want to create TablePopupMenu to allow cut/copy/paste operations
257:
258: // add mouse listener for Popup
259: MouseAdapter m = new MouseAdapter() {
260: public void mousePressed(MouseEvent evt) {
261: if (evt.isPopupTrigger()) {
262: // for now, ignore popup request
263: //RowDataJTable.this.displayPopupMenu(evt);
264: } else if (evt.getClickCount() == 2) {
265: // figure out which column the user clicked on
266: // so we can pass in the right column description
267:
268: Point pt = evt.getPoint();
269: int col = RowDataJTable.this .columnAtPoint(pt);
270: CellDataPopup.showDialog(RowDataJTable.this ,
271: _colDefs[col], evt, true);
272: }
273: }
274:
275: public void mouseReleased(MouseEvent evt) {
276: if (evt.isPopupTrigger()) {
277: // for now, ignore popup request
278: //RowDataJTable.this.displayPopupMenu(evt);
279: }
280: }
281: };
282: addMouseListener(m);
283: }
284:
285: public boolean isCellEditable(int row, int col) {
286: if (row > 0)
287: return false; // only the first row (containing data) is editable
288: return CellComponentFactory.isEditableInCell(_colDefs[col],
289: getValueAt(row, col));
290: }
291:
292: public TableCellRenderer getCellRenderer(int row, int column) {
293: if (row == 0)
294: return CellComponentFactory
295: .getTableCellRenderer(_colDefs[column]);
296: // for entries past the first one, use the default renderer
297: return new RowDataDescriptionRenderer();
298: }
299:
300: /*
301: * When user leaves a cell after editing it, the contents of
302: * that cell need to be converted from a string into an
303: * object of the appropriate type before updating the table.
304: * However, when the call comes from the Popup window, the data
305: * has already been converted and validated.
306: * We assume that a String being passed in here is a value from
307: * a text field that needs to be converted to an object, and
308: * a non-string object has already been validated and converted.
309: */
310: public void setValueAt(Object newValueString, int row, int col) {
311: if (!(newValueString instanceof java.lang.String)) {
312: // data is an object - assume already validated
313: super .setValueAt(newValueString, row, col);
314: return;
315: }
316:
317: // data is a String, so we need to convert to real object
318: StringBuffer messageBuffer = new StringBuffer();
319: ColumnDisplayDefinition colDef = _colDefs[col];
320: Object newValueObject = CellComponentFactory
321: .validateAndConvert(colDef, getValueAt(row, col),
322: (String) newValueString, messageBuffer);
323: if (messageBuffer.length() > 0) {
324: // display error message and do not update the table
325:
326: // i18n[rowInputDataFrame.conversionToInternErr=The given text cannot be converted into the internal object.\nThe database has not been changed.\nThe conversion error was:\n{0}]
327: String msg = s_stringMgr.getString(
328: "rowInputDataFrame.conversionToInternErr",
329: messageBuffer);
330: JOptionPane
331: .showMessageDialog(
332: this ,
333: msg,
334: // i18n[rowInputDataFrame.conversionErr=Conversion Error]
335: s_stringMgr
336: .getString("rowInputDataFrame.conversionErr"),
337: JOptionPane.ERROR_MESSAGE);
338: } else {
339: // data converted ok, so update the table
340: super .setValueAt(newValueObject, row, col);
341: }
342: }
343:
344: }
345:
346: /**
347: * Model for use by JTable in creating data for insertion.
348: */
349: class RowDataModel extends DefaultTableModel {
350:
351: /**
352: * ctor
353: */
354: protected RowDataModel(ColumnDisplayDefinition[] colDefs,
355: Object[] initalValues) {
356: super ();
357:
358: // set up the list of column names and the data for the rows
359: String[] colNames = new String[colDefs.length];
360: Object[][] rowData = new Object[2][colDefs.length];
361: for (int i = 0; i < colDefs.length; i++) {
362: colNames[i] = colDefs[i].getLabel(); // set column heading
363: rowData[0][i] = initalValues[i]; // set data in first row
364:
365: // put a description of the field in the following rows
366: rowData[1][i] = getColumnDescription(colDefs[i]);
367:
368: // colDefs[i].getSqlTypeName() + "\n" +
369: // ((colDefs[i].isNullable()) ? "nullable" : "not nullable") + "\n" +
370: // "precision="+ colDefs[i].getPrecision() + "\n" +
371: // "scale=" + colDefs[i].getScale();
372: }
373:
374: // put the data and header names into the model
375: setDataVector(rowData, colNames);
376: }
377:
378: /**
379: * Provides values for several column attributes (nullable, prec, scale)
380: * and in the event the column is auto-increment it displays that as a
381: * visual cue to the user that the field cannot be edited.
382: *
383: * @param def the ColumnDisplayDefinition that describes the column
384: * @return a string of column attributes separated by eol chars.
385: */
386: private String getColumnDescription(ColumnDisplayDefinition def) {
387: StringBuilder result = new StringBuilder();
388: result.append(def.getSqlTypeName());
389: result.append("\n");
390: if (def.isNullable()) {
391: result.append("nullable");
392: } else {
393: result.append("not nullable");
394: }
395: result.append("\n");
396: if (JDBCTypeMapper.isNumberType(def.getSqlType())) {
397: result.append("prec=");
398: result.append(def.getPrecision());
399: result.append("\n");
400: result.append("scale=");
401: result.append(def.getScale());
402: if (def.isAutoIncrement()) {
403: result.append("\n");
404: result.append("(auto-incr)");
405: }
406: } else {
407: result.append("length=");
408: result.append(def.getColumnSize());
409: }
410: return result.toString();
411: }
412: }
413:
414: /**
415: * renderer to display multiple lines in one table cell
416: */
417: class RowDataDescriptionRenderer implements TableCellRenderer {
418:
419: public Component getTableCellRendererComponent(JTable table,
420: Object value, boolean isSelected, boolean hasFocus,
421: int row, int column) {
422:
423: JTextArea ta = new JTextArea((String) value, 8, 20);
424: ta.setBackground(Color.lightGray);
425: return ta;
426: }
427: }
428:
429: }
|