001: /*
002: * TriggerListPanel.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.gui.dbobjects;
013:
014: import java.awt.BorderLayout;
015: import java.awt.Component;
016: import java.awt.Container;
017: import java.awt.Cursor;
018: import java.awt.EventQueue;
019: import java.util.ArrayList;
020:
021: import java.util.List;
022: import javax.swing.JPanel;
023: import javax.swing.JSplitPane;
024: import javax.swing.JTable;
025: import javax.swing.ListSelectionModel;
026: import javax.swing.border.EmptyBorder;
027: import javax.swing.event.ListSelectionEvent;
028: import javax.swing.event.ListSelectionListener;
029: import javax.swing.table.TableCellRenderer;
030:
031: import workbench.db.DbMetadata;
032: import workbench.db.ProcedureReader;
033: import workbench.db.WbConnection;
034: import workbench.gui.WbSwingUtilities;
035: import workbench.gui.actions.ReloadAction;
036: import workbench.gui.components.WbScrollPane;
037: import workbench.gui.components.WbSplitPane;
038: import workbench.gui.components.WbTable;
039: import workbench.gui.components.WbTraversalPolicy;
040: import workbench.gui.renderer.ProcStatusRenderer;
041: import workbench.interfaces.PropertyStorage;
042: import workbench.interfaces.Reloadable;
043: import workbench.log.LogMgr;
044: import workbench.resource.ResourceMgr;
045: import workbench.resource.Settings;
046: import javax.swing.JLabel;
047: import workbench.WbManager;
048: import workbench.db.DbObject;
049: import workbench.db.TableIdentifier;
050: import workbench.db.TriggerDefinition;
051: import workbench.gui.MainWindow;
052: import workbench.gui.actions.CompileDbObjectAction;
053: import workbench.gui.actions.DropDbObjectAction;
054: import workbench.gui.components.DataStoreTableModel;
055: import workbench.gui.components.QuickFilterPanel;
056: import workbench.interfaces.CriteriaPanel;
057: import workbench.storage.DataStore;
058: import workbench.util.ExceptionUtil;
059: import workbench.util.WbWorkspace;
060:
061: /**
062: *
063: * @author support@sql-workbench.net
064: *
065: */
066: public class TriggerListPanel extends JPanel implements
067: ListSelectionListener, Reloadable, DbObjectList {
068: private WbConnection dbConnection;
069: private JPanel listPanel;
070: private CriteriaPanel findPanel;
071: private WbTable triggerList;
072:
073: protected DbObjectSourcePanel source;
074: private WbSplitPane splitPane;
075: private String currentSchema;
076: private String currentCatalog;
077: private boolean shouldRetrieve;
078: private JLabel infoLabel;
079: private boolean isRetrieving;
080: protected ProcStatusRenderer statusRenderer;
081:
082: private CompileDbObjectAction compileAction;
083: private DropDbObjectAction dropAction;
084:
085: public TriggerListPanel(MainWindow parent) throws Exception {
086: Reloadable sourceReload = new Reloadable() {
087: public void reload() {
088: if (dbConnection.isBusy())
089: return;
090: try {
091: dbConnection.setBusy(true);
092: retrieveCurrentTrigger();
093: } finally {
094: dbConnection.setBusy(false);
095: }
096: }
097: };
098:
099: this .source = new DbObjectSourcePanel(parent, sourceReload);
100:
101: this .listPanel = new JPanel();
102: this .statusRenderer = new ProcStatusRenderer();
103: this .triggerList = new WbTable(true, false, false) {
104: public TableCellRenderer getCellRenderer(int row, int column) {
105: if (column == ProcedureReader.COLUMN_IDX_PROC_LIST_TYPE)
106: return statusRenderer;
107: return super .getCellRenderer(row, column);
108: }
109: };
110:
111: this .triggerList.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
112: this .triggerList.setCellSelectionEnabled(false);
113: this .triggerList.setColumnSelectionAllowed(false);
114: this .triggerList.setRowSelectionAllowed(true);
115: this .triggerList.getSelectionModel().addListSelectionListener(
116: this );
117: this .triggerList.getSelectionModel().setSelectionMode(
118: ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
119:
120: String[] cols = new String[] { "NAME", "TYPE", "EVENT" };
121: this .findPanel = new QuickFilterPanel(this .triggerList, cols,
122: false, "triggerlist");
123:
124: ReloadAction a = new ReloadAction(this );
125:
126: this .findPanel.addToToolbar(a, true, false);
127: a.getToolbarButton().setToolTipText(
128: ResourceMgr.getString("TxtRefreshTriggerList"));
129: this .listPanel.setLayout(new BorderLayout());
130: this .listPanel.add((JPanel) findPanel, BorderLayout.NORTH);
131:
132: this .splitPane = new WbSplitPane(JSplitPane.HORIZONTAL_SPLIT);
133: this .splitPane.setOneTouchExpandable(true);
134: this .splitPane.setDividerSize(6);
135: WbScrollPane scroll = new WbScrollPane(this .triggerList);
136:
137: this .listPanel.add(scroll, BorderLayout.CENTER);
138:
139: this .infoLabel = new JLabel("");
140: EmptyBorder b = new EmptyBorder(1, 3, 0, 0);
141: this .infoLabel.setBorder(b);
142: this .listPanel.add(this .infoLabel, BorderLayout.SOUTH);
143:
144: this .splitPane.setLeftComponent(this .listPanel);
145: this .splitPane.setRightComponent(source);
146: this .splitPane.setDividerBorder(WbSwingUtilities.EMPTY_BORDER);
147: this .setLayout(new BorderLayout());
148: this .add(splitPane, BorderLayout.CENTER);
149:
150: WbTraversalPolicy pol = new WbTraversalPolicy();
151: pol.setDefaultComponent((JPanel) findPanel);
152: pol.addComponent((JPanel) findPanel);
153: pol.addComponent(this .triggerList);
154: this .setFocusTraversalPolicy(pol);
155: this .reset();
156:
157: this .dropAction = new DropDbObjectAction(this , triggerList
158: .getSelectionModel(), this );
159: triggerList.addPopupAction(dropAction, true);
160: this .compileAction = new CompileDbObjectAction(this ,
161: this .triggerList.getSelectionModel());
162: triggerList.addPopupAction(compileAction, false);
163: }
164:
165: public void disconnect() {
166: this .dbConnection = null;
167: this .reset();
168: }
169:
170: public void reset() {
171: this .triggerList.reset();
172: this .source.setText("");
173: }
174:
175: public void setConnection(WbConnection aConnection) {
176: this .dbConnection = aConnection;
177: this .source.setDatabaseConnection(aConnection);
178: this .compileAction.setConnection(aConnection);
179: this .reset();
180: }
181:
182: public void setCatalogAndSchema(String aCatalog, String aSchema,
183: boolean retrieve) throws Exception {
184: this .currentSchema = aSchema;
185: this .currentCatalog = aCatalog;
186: if (this .isVisible() && retrieve) {
187: this .retrieve();
188: } else {
189: this .reset();
190: this .shouldRetrieve = true;
191: }
192: }
193:
194: public void panelSelected() {
195: if (this .shouldRetrieve)
196: this .retrieve();
197: }
198:
199: public void retrieve() {
200: if (this .isRetrieving)
201: return;
202: if (!WbSwingUtilities.checkConnection(this , this .dbConnection))
203: return;
204:
205: try {
206: this .reset();
207: this .dbConnection.setBusy(true);
208: this .isRetrieving = true;
209: DbMetadata meta = dbConnection.getMetadata();
210: WbSwingUtilities.showWaitCursorOnWindow(this );
211:
212: DataStore ds = meta.getTriggers(currentCatalog,
213: currentSchema);
214: final DataStoreTableModel model = new DataStoreTableModel(
215: ds);
216:
217: WbSwingUtilities.invoke(new Runnable() {
218: public void run() {
219: int rows = model.getRowCount();
220: infoLabel.setText(rows
221: + " "
222: + ResourceMgr
223: .getString("TxtTableListObjects"));
224: triggerList.setModel(model, true);
225: triggerList.adjustOrOptimizeColumns();
226: }
227: });
228: shouldRetrieve = false;
229: } catch (OutOfMemoryError mem) {
230: WbManager.getInstance().showOutOfMemoryError();
231: } catch (Throwable e) {
232: LogMgr.logError("ProcedureListPanel.retrieve() thread",
233: "Could not retrieve trigger list", e);
234: } finally {
235: this .isRetrieving = false;
236: this .dbConnection.setBusy(false);
237: WbSwingUtilities.showDefaultCursorOnWindow(this );
238: }
239:
240: }
241:
242: public void setVisible(boolean aFlag) {
243: super .setVisible(aFlag);
244: if (aFlag && this .shouldRetrieve)
245: this .retrieve();
246: }
247:
248: private String getWorkspacePrefix(int index) {
249: return "dbexplorer" + index + ".triggerlist.";
250: }
251:
252: public void saveSettings() {
253: storeSettings(Settings.getInstance(), this .getClass().getName()
254: + ".");
255: findPanel.saveSettings();
256: }
257:
258: public void saveToWorkspace(WbWorkspace w, int index) {
259: String prefix = getWorkspacePrefix(index);
260: storeSettings(w.getSettings(), prefix);
261: findPanel.saveSettings(w.getSettings(), prefix);
262: }
263:
264: private void storeSettings(PropertyStorage props, String prefix) {
265: props.setProperty(prefix + "divider", this .splitPane
266: .getDividerLocation());
267: }
268:
269: public void restoreSettings() {
270: readSettings(Settings.getInstance(), this .getClass().getName()
271: + ".");
272: findPanel.restoreSettings();
273: }
274:
275: public void readFromWorkspace(WbWorkspace w, int index) {
276: String prefix = getWorkspacePrefix(index);
277: readSettings(w.getSettings(), prefix);
278: this .findPanel.restoreSettings(w.getSettings(), prefix);
279: }
280:
281: private void readSettings(PropertyStorage props, String prefix) {
282: int loc = props.getIntProperty(prefix + "divider", 200);
283: this .splitPane.setDividerLocation(loc);
284: }
285:
286: public void valueChanged(ListSelectionEvent e) {
287: if (e.getSource() != this .triggerList.getSelectionModel())
288: return;
289: if (e.getValueIsAdjusting())
290: return;
291: retrieveCurrentTrigger();
292: }
293:
294: protected void retrieveCurrentTrigger() {
295: int row = this .triggerList.getSelectedRow();
296:
297: if (row < 0)
298: return;
299:
300: final String trigger = this .triggerList.getValueAsString(row,
301: DbMetadata.COLUMN_IDX_TABLE_TRIGGERLIST_TRG_NAME);
302: EventQueue.invokeLater(new Runnable() {
303: public void run() {
304: retrieveTriggerSource(trigger);
305: }
306: });
307: }
308:
309: protected void retrieveTriggerSource(String triggerName) {
310: if (this .dbConnection == null)
311: return;
312: if (!WbSwingUtilities.checkConnection(this , this .dbConnection))
313: return;
314:
315: DbMetadata meta = dbConnection.getMetadata();
316: Container parent = this .getParent();
317: parent
318: .setCursor(Cursor
319: .getPredefinedCursor(Cursor.WAIT_CURSOR));
320:
321: try {
322: dbConnection.setBusy(true);
323:
324: try {
325: String sql = meta.getTriggerSource(currentCatalog,
326: currentSchema, triggerName);
327: source.setText(sql == null ? "" : sql);
328: } catch (Throwable ex) {
329: LogMgr.logError(
330: "ProcedureListPanel.valueChanged() thread",
331: "Could not read procedure source", ex);
332: source.setText(ExceptionUtil.getDisplay(ex));
333: }
334: } finally {
335: parent.setCursor(Cursor
336: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
337: dbConnection.setBusy(false);
338: }
339:
340: EventQueue.invokeLater(new Runnable() {
341: public void run() {
342: source.setCaretPosition(0, false);
343: source.requestFocusInWindow();
344: }
345: });
346: }
347:
348: public TableIdentifier getObjectTable() {
349: return null;
350: }
351:
352: public Component getComponent() {
353: return this ;
354: }
355:
356: public WbConnection getConnection() {
357: return this .dbConnection;
358: }
359:
360: public List<DbObject> getSelectedObjects() {
361: if (this .triggerList.getSelectedRowCount() == 0)
362: return null;
363: int rows[] = this .triggerList.getSelectedRows();
364: int count = rows.length;
365: List<DbObject> objects = new ArrayList<DbObject>(count);
366: if (count == 0)
367: return objects;
368:
369: for (int i = 0; i < count; i++) {
370: String name = this .triggerList.getValueAsString(rows[i],
371: DbMetadata.COLUMN_IDX_TABLE_TRIGGERLIST_TRG_NAME);
372:
373: // MS SQL Server appends a semicolon at the end of the name...
374: int pos = name.indexOf(';');
375: if (pos > 0) {
376: name = name.substring(0, pos);
377: }
378:
379: // To build the correct schema, catalog and trigger name
380: // we use the functionality built into TableIdentifier
381: // The name of a trigger should follow the same rules as a table
382: // name. So it should be save to apply the same algorithm to
383: // build a correctly qualified name
384: TriggerDefinition trg = new TriggerDefinition(
385: currentCatalog, currentSchema, name);
386: objects.add(trg);
387: }
388: return objects;
389: }
390:
391: public void reload() {
392: this.reset();
393: this.retrieve();
394: }
395:
396: }
|