001: package net.sourceforge.squirrel_sql.fw.datasetviewer;
002:
003: /*
004: * Copyright (C) 2001 Colin Bell
005: * colbell@users.sourceforge.net
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library 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 GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.awt.BorderLayout;
022: import java.awt.Component;
023: import java.awt.Dimension;
024: import java.awt.Point;
025: import java.awt.Rectangle;
026: import java.awt.event.ActionEvent;
027: import java.awt.event.ActionListener;
028: import java.awt.event.KeyEvent;
029: import java.awt.event.MouseEvent;
030:
031: import javax.swing.AbstractAction;
032: import javax.swing.CellEditor;
033: import javax.swing.JButton;
034: import javax.swing.JComponent;
035: import javax.swing.JInternalFrame;
036: import javax.swing.JLayeredPane;
037: import javax.swing.JOptionPane;
038: import javax.swing.JPanel;
039: import javax.swing.JTable;
040: import javax.swing.KeyStroke;
041: import javax.swing.SwingUtilities;
042:
043: //import net.sourceforge.squirrel_sql.client.gui.AboutBoxDialog;
044: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.CellComponentFactory;
045: import net.sourceforge.squirrel_sql.fw.util.StringManager;
046: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
047:
048: /**
049: * Generate a popup window to display and manipulate the
050: * complete contents of a cell.
051: */
052: public class CellDataPopup {
053:
054: private static final StringManager s_stringMgr = StringManagerFactory
055: .getStringManager(CellDataPopup.class);
056:
057: /**
058: * function to create the popup display when called from JTable
059: */
060: public static void showDialog(JTable table,
061: ColumnDisplayDefinition colDef, MouseEvent evt,
062: boolean isModelEditable) {
063: CellDataPopup popup = new CellDataPopup();
064: popup.createAndShowDialog(table, evt, colDef, isModelEditable);
065: }
066:
067: private void createAndShowDialog(JTable table, MouseEvent evt,
068: ColumnDisplayDefinition colDef, boolean isModelEditable) {
069: Point pt = evt.getPoint();
070: int row = table.rowAtPoint(pt);
071: int col = table.columnAtPoint(pt);
072:
073: Object obj = table.getValueAt(row, col);
074:
075: // since user is now using popup, stop editing
076: // using the in-cell editor, if any
077: CellEditor editor = table.getCellEditor(row, col);
078: if (editor != null)
079: editor.cancelCellEditing();
080:
081: Component comp = SwingUtilities.getRoot(table);
082: Component newComp = null;
083:
084: if (false == comp instanceof IMainFrame) {
085: // Fixes ClassCastException, see below.
086: return;
087: }
088:
089: // The following only works if SwingUtilities.getRoot(table) returns
090: // and instanceof BaseMDIParentFrame.
091: // If SwingTUilities.getRoot(table) returns and instance of Dialog or
092: // Frame, then other code must be used.
093: TextAreaInternalFrame taif = new TextAreaInternalFrame(table
094: .getColumnName(col), colDef, obj, row, col,
095: isModelEditable, table);
096: ((IMainFrame) comp).addInternalFrame(taif, false);
097: taif.setLayer(JLayeredPane.POPUP_LAYER);
098: taif.pack();
099: newComp = taif;
100:
101: Dimension dim = newComp.getSize();
102: boolean dimChanged = false;
103: if (dim.width < 300) {
104: dim.width = 300;
105: dimChanged = true;
106: }
107: if (dim.height < 300) {
108: dim.height = 300;
109: dimChanged = true;
110: }
111: if (dim.width > 600) {
112: dim.width = 600;
113: dimChanged = true;
114: }
115: if (dim.height > 500) {
116: dim.height = 500;
117: dimChanged = true;
118: }
119: if (dimChanged) {
120: newComp.setSize(dim);
121: }
122: if (comp instanceof IMainFrame) {
123: pt = SwingUtilities.convertPoint((Component) evt
124: .getSource(), pt, comp);
125: pt.y -= dim.height;
126: } else {
127: // getRoot() doesn't appear to return the deepest Window, but the first one.
128: // If you have a dialog owned by a window you get the dialog, not the window.
129: Component parent = SwingUtilities.windowForComponent(comp);
130: while ((parent != null) && !(parent instanceof IMainFrame)
131: && !(parent.equals(comp))) {
132: comp = parent;
133: parent = SwingUtilities.windowForComponent(comp);
134: }
135: comp = (parent != null) ? parent : comp;
136: pt = SwingUtilities.convertPoint((Component) evt
137: .getSource(), pt, comp);
138: }
139:
140: if (pt.y < 0) {
141: pt.y = 0; // fudge for larger inset windows
142: }
143:
144: // Determine the position to place the new internal frame. Ensure that the right end
145: // of the internal frame doesn't exend past the right end the parent frame. Use a
146: // fudge factor as the dim.width doesn't appear to get the final width of the internal
147: // frame (e.g. where pt.x + dim.width == parentBounds.width, the new internal frame
148: // still extends past the right end of the parent frame).
149: int fudgeFactor = 100;
150: Rectangle parentBounds = comp.getBounds();
151: if (parentBounds.width <= (dim.width + fudgeFactor)) {
152: dim.width = parentBounds.width - fudgeFactor;
153: pt.x = fudgeFactor / 2;
154: newComp.setSize(dim);
155: } else {
156: if ((pt.x + dim.width + fudgeFactor) > (parentBounds.width)) {
157: pt.x -= (pt.x + dim.width + fudgeFactor)
158: - parentBounds.width;
159: }
160: }
161:
162: newComp.setLocation(pt);
163: newComp.setVisible(true);
164: }
165:
166: //
167: // inner class for the data display pane
168: //
169: private static class ColumnDataPopupPanel extends JPanel {
170:
171: private static final long serialVersionUID = 1L;
172: private final PopupEditableIOPanel ioPanel;
173: private JInternalFrame _parentFrame = null;
174: private int _row;
175: private int _col;
176: private JTable _table;
177:
178: ColumnDataPopupPanel(Object cellContents,
179: ColumnDisplayDefinition colDef, boolean tableIsEditable) {
180: super (new BorderLayout());
181:
182: if (tableIsEditable
183: && CellComponentFactory.isEditableInPopup(colDef,
184: cellContents)) {
185:
186: // data is editable in popup
187: ioPanel = new PopupEditableIOPanel(colDef,
188: cellContents, true);
189:
190: // Since data is editable, we need to add control panel
191: // to manage user requests for DB update, file IO, etc.
192: JPanel editingControls = createPopupEditingControls();
193: add(editingControls, BorderLayout.SOUTH);
194: } else {
195: // data is not editable in popup
196: ioPanel = new PopupEditableIOPanel(colDef,
197: cellContents, false);
198: }
199:
200: add(ioPanel, BorderLayout.CENTER);
201:
202: }
203:
204: /**
205: * Set up user controls to stop editing and update DB.
206: */
207: private JPanel createPopupEditingControls() {
208:
209: JPanel panel = new JPanel(new BorderLayout());
210:
211: // create update/cancel controls using default layout
212: JPanel updateControls = new JPanel();
213:
214: // set up Update button
215: // i18n[cellDataPopUp.updateData=Update Data]
216: JButton updateButton = new JButton(s_stringMgr
217: .getString("cellDataPopUp.updateData"));
218: updateButton.addActionListener(new ActionListener() {
219: public void actionPerformed(ActionEvent event) {
220:
221: // try to convert the text in the popup into a valid
222: // instance of type of data object being held in the table cell
223: StringBuffer messageBuffer = new StringBuffer();
224: Object newValue = ColumnDataPopupPanel.this .ioPanel
225: .getObject(messageBuffer);
226: if (messageBuffer.length() > 0) {
227: // handle an error in conversion of text to object
228:
229: // i18n[cellDataPopUp.cannnotBGeConverted=The given text cannot be converted into the internal object.\n
230: //Please change the data or cancel editing.\n
231: //The conversion error was:\n{0}]
232: String msg = s_stringMgr.getString(
233: "cellDataPopUp.cannnotBGeConverted",
234: messageBuffer);
235:
236: JOptionPane
237: .showMessageDialog(
238: ColumnDataPopupPanel.this ,
239: msg,
240: // i18n[cellDataPopUp.conversionError=Conversion Error]
241: s_stringMgr
242: .getString("cellDataPopUp.conversionError"),
243: JOptionPane.ERROR_MESSAGE);
244:
245: ColumnDataPopupPanel.this .ioPanel
246: .requestFocus();
247:
248: } else {
249: // no problem in conversion - proceed with update
250: // ((DataSetViewerTablePanel.MyJTable)_table).setConvertedValueAt(
251: // newValue, _row, _col);
252: _table.setValueAt(newValue, _row, _col);
253: ColumnDataPopupPanel.this ._parentFrame
254: .setVisible(false);
255: ColumnDataPopupPanel.this ._parentFrame
256: .dispose();
257: }
258: }
259: });
260:
261: // set up Cancel button
262: // i18n[cellDataPopup.cancel=Cancel]
263: JButton cancelButton = new JButton(s_stringMgr
264: .getString("cellDataPopup.cancel"));
265: cancelButton.addActionListener(new ActionListener() {
266: public void actionPerformed(ActionEvent event) {
267: ColumnDataPopupPanel.this ._parentFrame
268: .setVisible(false);
269: ColumnDataPopupPanel.this ._parentFrame.dispose();
270: }
271: });
272:
273: // add buttons to button panel
274: updateControls.add(updateButton);
275: updateControls.add(cancelButton);
276:
277: // add button panel to main panel
278: panel.add(updateControls, BorderLayout.SOUTH);
279:
280: return panel;
281: }
282:
283: /*
284: * Save various information which is needed to do Update & Cancel.
285: */
286: public void setUserActionInfo(JInternalFrame parent, int row,
287: int col, JTable table) {
288: _parentFrame = parent;
289: _row = row;
290: _col = col;
291: _table = table;
292: }
293:
294: }
295:
296: // The following is only useable for a root type of InternalFrame. If the
297: // root type is Dialog or Frame, then other code must be used.
298: class TextAreaInternalFrame extends JInternalFrame {
299: private static final long serialVersionUID = 1L;
300:
301: public TextAreaInternalFrame(String columnName,
302: ColumnDisplayDefinition colDef, Object value, int row,
303: int col, boolean isModelEditable, JTable table) {
304: // i18n[cellDataPopup.valueofColumn=Value of column {0}]
305: super (s_stringMgr.getString("cellDataPopup.valueofColumn",
306: columnName), true, true, true, true);
307: ColumnDataPopupPanel popup = new ColumnDataPopupPanel(
308: value, colDef, isModelEditable);
309: popup.setUserActionInfo(this , row, col, table);
310: setContentPane(popup);
311:
312: AbstractAction closeAction = new AbstractAction() {
313: private static final long serialVersionUID = 1L;
314:
315: public void actionPerformed(ActionEvent actionEvent) {
316: setVisible(false);
317: dispose();
318: }
319: };
320: KeyStroke escapeStroke = KeyStroke.getKeyStroke(
321: KeyEvent.VK_ESCAPE, 0);
322: getRootPane().getInputMap(
323: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
324: escapeStroke, "CloseAction");
325: getRootPane()
326: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
327: .put(escapeStroke, "CloseAction");
328: getRootPane().getInputMap(JComponent.WHEN_FOCUSED).put(
329: escapeStroke, "CloseAction");
330: getRootPane().getActionMap()
331: .put("CloseAction", closeAction);
332: }
333: }
334:
335: }
|