001: /*
002: ** $Id: QueryView.java,v 1.10 2000/11/01 08:43:37 mrw Exp $
003: **
004: ** View for an SQL query. Contains a table to display the query
005: ** results and a text are to edit the query.
006: **
007: ** Mike Wilson, July 2000, mrw@whisperingwind.co.uk
008: **
009: ** (C) Copyright 2000, Mike Wilson, Reading, Berkshire, UK
010: **
011: ** This program is free software; you can redistribute it and/or modify
012: ** it under the terms of the GNU General Public License as published by
013: ** the Free Software Foundation; either version 2 of the License, or
014: ** (at your option) any later version.
015: **
016: ** This program is distributed in the hope that it will be useful,
017: ** but WITHOUT ANY WARRANTY; without even the implied warranty of
018: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019: ** GNU General Public License for more details.
020: **
021: ** You should have received a copy of the GNU Library General
022: ** Public License along with this library; if not, write to the
023: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
024: ** Boston, MA 02111-1307 USA.
025: */
026:
027: package uk.co.whisperingwind.vienna;
028:
029: import java.awt.BorderLayout;
030: import java.awt.Cursor;
031: import java.awt.Font;
032: import java.awt.FontMetrics;
033: import java.util.Observable;
034: import java.util.Observer;
035: import javax.swing.event.DocumentEvent;
036: import javax.swing.event.DocumentListener;
037: import javax.swing.JScrollPane;
038: import javax.swing.JSplitPane;
039: import javax.swing.JTable;
040: import javax.swing.JTextArea;
041: import javax.swing.SwingConstants;
042: import javax.swing.table.TableColumn;
043: import javax.swing.table.TableColumnModel;
044: import javax.swing.text.Document;
045: import javax.swing.event.UndoableEditListener;
046: import javax.swing.event.UndoableEditEvent;
047: import javax.swing.undo.UndoManager;
048: import javax.swing.undo.CannotRedoException;
049: import javax.swing.undo.CannotUndoException;
050: import uk.co.whisperingwind.framework.ModelEvent;
051: import uk.co.whisperingwind.framework.PanelView;
052: import uk.co.whisperingwind.framework.StatusBar;
053: import uk.co.whisperingwind.framework.VectorTableModel;
054:
055: class QueryView extends PanelView implements DocumentListener {
056: private static final int MAX_COLUMN_WIDTH = 200;
057:
058: private ConfigModel configModel = null;
059: private JSplitPane splitPane = null;
060: private JTextArea textArea = null;
061: private QueryTable resultTable = null;
062: private QueryModel queryModel = null;
063: private Cursor originalCursor = null;
064: private StatusBar statusBar = new StatusBar();
065: private UndoManager undoManager = new UndoManager();
066:
067: public QueryView(ConfigModel config, QueryModel model) {
068: configModel = config;
069: queryModel = model;
070: queryModel.addObserver(this );
071: initComponents(configModel);
072: configModel.addObserver(this );
073: }
074:
075: public void setTableModel(QueryModel model) {
076: queryModel = model;
077: resultTable.setModel(queryModel.getTableModel());
078: }
079:
080: /*
081: ** Something has changed in the model...
082: */
083:
084: protected void modelEvent(ModelEvent event) {
085: /*
086: ** A ModelEvent is fired when I call setSQL myself. I need to
087: ** make sure I don't get in an infinite loop. (Actually, it
088: ** won't loop but thow an exception if I try to mutate while
089: ** in a handler).
090: */
091:
092: if (event.getInitiator() != this ) {
093: if (event.getInitiator() == queryModel)
094: handleQueryEvent(event);
095: else if (event.getInitiator() == configModel)
096: handleConfigEvent(event);
097: }
098: }
099:
100: public void editAction(String action) {
101: if (action.equals("cut"))
102: textArea.cut();
103: else if (action.equals("copy"))
104: textArea.copy();
105: else if (action.equals("paste"))
106: textArea.paste();
107: else if (action.equals("clear"))
108: textArea.replaceSelection("");
109: }
110:
111: public void focusText() {
112: textArea.requestFocus();
113: }
114:
115: protected void cleanUp() {
116: queryModel.deleteObserver(this );
117: configModel.deleteObserver(this );
118: queryModel = null;
119: configModel = null;
120: }
121:
122: private void handleQueryEvent(ModelEvent event) {
123: String type = event.getField();
124: String value = (String) event.getValue();
125:
126: if (type.equals("query")) {
127: if (value.equals("1"))
128: setStatus("Query returned " + value + " row");
129: else
130: setStatus("Query returned " + value + " rows");
131:
132: enableGUI(false);
133: resultTable.refresh();
134: resizeTable();
135: }
136: if (type.equals("update")) {
137: String message = "Insert, update or delete affected ";
138:
139: if (value.equals("1"))
140: setStatus(message + "1 row");
141: else
142: setStatus(message + value + " rows");
143:
144: enableGUI(false);
145: } else if (type.equals("sql")) {
146: textArea.setText(queryModel.getSQL());
147: undoManager.discardAllEdits();
148: } else if (type.equals("execute")) {
149: if (value.equals("begin")) {
150: enableGUI(true);
151: } else {
152: fireEvent("execute", "end");
153: enableGUI(false);
154: }
155: } else if (type.equals("status")) {
156: setStatus(value);
157: }
158: }
159:
160: private void handleConfigEvent(ModelEvent event) {
161: String field = event.getField();
162:
163: if (field.equals("tablefont"))
164: resultTable.setFont((Font) event.getValue());
165: else if (field.equals("textfont"))
166: textArea.setFont((Font) event.getValue());
167: }
168:
169: /**
170: ** DocumentEvent listener method. The text in the SQL query area
171: ** has been changed.
172: */
173:
174: public void changedUpdate(DocumentEvent event) {
175: queryModel.setSQL(textArea.getText(), this );
176: fireEvent("edited");
177: }
178:
179: /**
180: ** DocumentEvent listener method. Text has been inserted in the
181: ** SQL query text area.
182: */
183:
184: public void insertUpdate(DocumentEvent event) {
185: queryModel.setSQL(textArea.getText(), this );
186: fireEvent("edited");
187: }
188:
189: /**
190: ** DocumentEvent listener method. Text has been deleted from the
191: ** SQL query text area.
192: */
193:
194: public void removeUpdate(DocumentEvent event) {
195: queryModel.setSQL(textArea.getText(), this );
196: fireEvent("edited");
197: }
198:
199: public void setStatus(String status) {
200: statusBar.setText(status);
201: }
202:
203: public void clearStatus() {
204: statusBar.clear();
205: }
206:
207: public void enableGUI(boolean busy) {
208: busyCursor(busy);
209: textArea.setEnabled(!busy);
210: }
211:
212: /**
213: ** Return true if the text area has a selection. getSelectedText
214: ** can throw if I just typed over the selection.
215: */
216:
217: public boolean hasSelection() {
218: boolean has = false;
219:
220: try {
221: if (textArea.getSelectedText() != null)
222: has = true;
223: } catch (Exception ex) {
224: // Do nothing
225: }
226:
227: return has;
228: }
229:
230: public void undoEdit() {
231: try {
232: undoManager.undo();
233: } catch (CannotUndoException ex) {
234: // FIXME: Do something
235: }
236: }
237:
238: public void redoEdit() {
239: try {
240: undoManager.redo();
241: } catch (CannotRedoException ex) {
242: // FIXME: Do something
243: }
244: }
245:
246: public boolean canUndo() {
247: return undoManager.canUndo();
248: }
249:
250: public boolean canRedo() {
251: return undoManager.canRedo();
252: }
253:
254: private void initComponents(ConfigModel configModel) {
255: textArea = new JTextArea(8, 80);
256: textArea.setFont(configModel.getTextFont());
257: JScrollPane textScroller = new JScrollPane(textArea);
258:
259: resultTable = new QueryTable(queryModel.getTableModel());
260: resultTable.setFont(configModel.getTableFont());
261: resultTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
262: content.setLayout(new BorderLayout());
263:
264: JScrollPane tableScroller = new JScrollPane(resultTable);
265:
266: splitPane = new JSplitPane(SwingConstants.HORIZONTAL,
267: tableScroller, textScroller);
268:
269: content.add(splitPane, BorderLayout.CENTER);
270: content.add(statusBar, BorderLayout.SOUTH);
271:
272: Document document = textArea.getDocument();
273: document.addDocumentListener(this );
274: document
275: .addUndoableEditListener(new QueryUndoableEditListener());
276: }
277:
278: private void busyCursor(boolean busy) {
279: if (busy) {
280: originalCursor = content.getCursor();
281: content.setCursor(new Cursor(Cursor.WAIT_CURSOR));
282: } else {
283: content.setCursor(originalCursor);
284: }
285: }
286:
287: /*
288: ** Set some initial widths for the table columns. FIXME: This uses
289: ** the table's font, not the column header font.
290: */
291:
292: private void resizeTable() {
293: FontMetrics fontMetrics = resultTable
294: .getFontMetrics(resultTable.getFont());
295: int scale = fontMetrics.stringWidth("X");
296: VectorTableModel model = queryModel.getTableModel();
297: TableColumnModel columnModel = resultTable.getColumnModel();
298:
299: for (int i = 0; i < resultTable.getColumnCount(); i++) {
300: /*
301: ** Set the width to the larger of the with of the column
302: ** if it contains all "X" and the width of its name.
303: */
304:
305: int width = scale * model.getColumnWidth(i);
306:
307: /*
308: ** Get the width of the name (the column title). Add a bit
309: ** to make room for some padding. This won't work too well
310: ** for all fonts, but hey, the columns are resizable by
311: ** the user so this should be close enough.
312: */
313:
314: int width2 = fontMetrics
315: .stringWidth(model.getColumnName(i))
316: + scale + scale;
317:
318: if (width2 > width)
319: width = width2;
320:
321: if (width > MAX_COLUMN_WIDTH)
322: width = MAX_COLUMN_WIDTH;
323:
324: TableColumn column = columnModel.getColumn(i);
325: column.setPreferredWidth(width);
326: }
327: }
328:
329: private class QueryUndoableEditListener implements
330: UndoableEditListener {
331: public void undoableEditHappened(UndoableEditEvent event) {
332: undoManager.addEdit(event.getEdit());
333: }
334: }
335: }
|