1 package com.explosion.datastream.exql.gui.table;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import java.awt.Color;
24 import java.awt.Font;
25 import java.awt.Toolkit;
26 import java.awt.datatransfer.Clipboard;
27 import java.awt.datatransfer.ClipboardOwner;
28 import java.awt.datatransfer.DataFlavor;
29 import java.awt.datatransfer.Transferable;
30 import java.awt.datatransfer.UnsupportedFlavorException;
31 import java.awt.event.MouseEvent;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.StringTokenizer;
40 import java.util.Vector;
41
42 import javax.swing.JOptionPane;
43 import javax.swing.JTable;
44 import javax.swing.ListSelectionModel;
45 import javax.swing.event.ListSelectionEvent;
46 import javax.swing.event.ListSelectionListener;
47 import javax.swing.event.TableModelEvent;
48 import javax.swing.event.TableModelListener;
49 import javax.swing.table.DefaultTableModel;
50 import javax.swing.table.TableCellEditor;
51 import javax.swing.table.TableCellRenderer;
52 import javax.swing.table.TableModel;
53 import javax.swing.text.JTextComponent;
54
55 import org.apache.log4j.LogManager;
56 import org.apache.log4j.Logger;
57
58 import com.explosion.datastream.exql.EXQLConstants;
59 import com.explosion.datastream.exql.EXQLModuleManager;
60 import com.explosion.datastream.exql.gui.EXQLBaseTool;
61 import com.explosion.datastream.exql.gui.table.editandrender.DeletedValueCellRenderer;
62 import com.explosion.datastream.exql.gui.table.editandrender.DisplayValueCellRenderer;
63 import com.explosion.datastream.exql.gui.table.editandrender.EditorAndRenderFactory;
64 import com.explosion.datastream.exql.gui.table.editandrender.NonEditableValueCellRenderer;
65 import com.explosion.datastream.exql.gui.table.editandrender.TextBasedTableCellEditor;
66 import com.explosion.datastream.exql.gui.table.editandrender.UpdatedValueCellRenderer;
67 import com.explosion.expf.Application;
68 import com.explosion.expf.ExpConstants;
69 import com.explosion.expf.ExpFrame;
70 import com.explosion.expf.menusandtools.menu.MenuHelper;
71 import com.explosion.expf.menusandtools.menu.popup.ExpPopupIntercepter;
72 import com.explosion.expf.menusandtools.menu.popup.ExpPopupableMenu;
73 import com.explosion.expf.menusandtools.menu.segmented.ExpMenuSegment;
74 import com.explosion.expfmodules.rdbmsconn.dbom.DBEntity;
75 import com.explosion.expfmodules.rdbmsconn.dbom.DBEntityColumn;
76 import com.explosion.expfmodules.rdbmsconn.dbom.utils.InvalidDataException;
77 import com.explosion.expfmodules.rdbmsconn.dbom.utils.SQLEngine;
78 import com.explosion.expfmodules.texteditor.Editable;
79 import com.explosion.utilities.exception.ExceptionManagerFactory;
80
81 /***
82 * Displays data. Edited values go red. Deleted values are struck through
83 *
84 * @author Stephen Created on Apr 30, 2004
85 */
86 public class DsProTable extends JTable implements ExpPopupIntercepter, Editable, ClipboardOwner
87 {
88
89 private Map insertedRows = new HashMap();
90 private Map deletedRows = new HashMap();
91 private Map updatedRows = new HashMap();
92 private static Logger log = LogManager.getLogger(DsProTable.class);
93 private EXQLBaseTool tool;
94 private Object cachedPreChangeValue;
95 private boolean isEditable = true;
96 private DBEntity entity = null;
97 private ExpPopupableMenu menu = null;
98 private Clipboard system;
99
100 public DsProTable(EXQLBaseTool tool, boolean isEditable) {
101 this.tool = tool;
102 this.isEditable = isEditable;
103 setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
104 setRowSelectionAllowed(true);
105 setColumnSelectionAllowed(false);
106 setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
107 SelectionListener listener = new SelectionListener(this, tool);
108 getSelectionModel().addListSelectionListener(listener);
109
110
111 setDragEnabled(true);
112
113 system = Toolkit.getDefaultToolkit().getSystemClipboard();
114
115 DsProTableLocalListener localListener = new DsProTableLocalListener(this, tool);
116
117 }
118
119 /***
120 * Applies the properties for this tool;
121 */
122 public void applyPreferences() {
123
124 setGridColor((Color) EXQLModuleManager.instance().getPreference(
125 EXQLConstants.TABLE_GRID_COLOR).getValue());
126 setForeground((Color) EXQLModuleManager.instance().getPreference(
127 EXQLConstants.TABLE_COLORS_FOREGROUND).getValue());
128 setBackground((Color) EXQLModuleManager.instance().getPreference(
129 EXQLConstants.TABLE_COLORS_BACKGROUND).getValue());
130 setSelectionForeground((Color) EXQLModuleManager.instance()
131 .getPreference(EXQLConstants.TABLE_COLOR_SELECTEDFORGROUND)
132 .getValue());
133 setSelectionBackground((Color) EXQLModuleManager.instance()
134 .getPreference(EXQLConstants.TABLE_COLOR_SELECTEDBACKGROUND)
135 .getValue());
136
137 setShowHorizontalLines(((Boolean) EXQLModuleManager.instance()
138 .getPreference(
139 EXQLConstants.RDBMS_OPTION_SHOW_VERTICAL_GRID_LINES)
140 .getValue()).booleanValue());
141 setShowVerticalLines(((Boolean) EXQLModuleManager.instance()
142 .getPreference(
143 EXQLConstants.RDBMS_OPTION_SHOW_HORIZONTAL_GRID_LINES)
144 .getValue()).booleanValue());
145 setShowGrid(((Boolean) EXQLModuleManager.instance().getPreference(
146 EXQLConstants.RDBMS_OPTION_SHOW_GRID).getValue())
147 .booleanValue());
148
149
150
151 setFont((Font) EXQLModuleManager.instance().getPreference(
152 EXQLConstants.TABLE_FONT).getValue());
153 }
154
155 /***
156 * Returns whether or not this scell is editable.
157 *
158 * @param arg0
159 * @param arg1
160 * @return
161 */
162 public boolean isCellEditable(int row, int column) {
163 try {
164
165 DBEntityColumn col = getDBEntityColumn(column);
166 if (col.isAutoIncrement())
167 {
168 return false;
169 }
170 else if (!SQLEngine.isSupportedEditableType(col.getType()))
171 {
172 return false;
173 }
174 else if (deletedRows.get(new Integer(row)) != null)
175 {
176 return false;
177 }
178 } catch (Exception e) {
179 return true;
180 }
181
182 return true;
183 }
184
185 /***
186 * Inserts a row into the table
187 *
188 * @param row
189 * @param v
190 */
191 public int insertNewRow() {
192 if (!isEditable)
193 return -1;
194
195 int selectedRow = getSelectedRow();
196
197 if (selectedRow < 0) {
198 insertedRows = reindex(insertedRows, 0);
199 deletedRows = reindex(deletedRows, 0);
200 updatedRows = reindex(updatedRows, 0);
201
202 ((DefaultTableModel) getModel()).insertRow(0, new Vector());
203 insertedRows.put(new Integer(0), new Integer(0));
204 } else {
205 insertedRows = reindex(insertedRows, selectedRow);
206 deletedRows = reindex(deletedRows, selectedRow);
207 updatedRows = reindex(updatedRows, selectedRow);
208
209 insertedRows
210 .put(new Integer(selectedRow), new Integer(selectedRow));
211 ((DefaultTableModel) getModel()).insertRow(selectedRow,
212 new Vector());
213 }
214
215 checkDirty();
216 tool.repaint();
217 return selectedRow;
218 }
219
220 /***
221 * Removes a row from the table
222 */
223 public void deleteSelectedRows() {
224 if (!isEditable)
225 return;
226
227 int selectedRows[] = this.getSelectedRows();
228 Arrays.sort(selectedRows);
229 for (int i = 0; i < selectedRows.length; i++) {
230 deletedRows.put(new Integer(selectedRows[i]), new Integer(
231 selectedRows[i]));
232 }
233
234 checkDirty();
235
236 this.clearSelection();
237
238 }
239
240 /***
241 * Overrides the default behaviour because it has to reset the insertedRows
242 * HashMap. I.E Presume that the new data is all fresh
243 *
244 * @see javax.swing.JTable#setModel(javax.swing.table.TableModel)
245 * @param model
246 */
247 public void setModel(TableModel model) {
248 insertedRows = new HashMap();
249 deletedRows = new HashMap();
250 updatedRows = new HashMap();
251 checkDirty();
252 Application.ensureLocalCookie(EXQLConstants.MENU_EXECUTE_CHANGES, 0,tool);
253 super.setModel(model);
254 if (isEditable) {
255
256 Application.ensureLocalCookie(EXQLConstants.MENU_INSERT_ROW, 1, tool);
257
258 model.addTableModelListener(new TableModelListener() {
259
260 public void tableChanged(TableModelEvent event) {
261 int first = event.getFirstRow();
262 int last = event.getLastRow();
263 int column = event.getColumn();
264 int type = event.getType();
265 if (type == TableModelEvent.UPDATE) {
266 if (first == last) {
267 boolean changed = true;
268
269
270 Map row = (Map) updatedRows.get(new Integer(first));
271
272 if (row == null)
273 {
274
275 row = new HashMap();
276
277 row.put(new Integer(column), cachedPreChangeValue);
278
279 updatedRows.put(new Integer(first), row);
280
281 checkDirty();
282 }
283 else
284 {
285
286
287 Object originalValue = row.get(new Integer(column));
288 if (originalValue == null)
289 {
290
291 row.put(new Integer(column), cachedPreChangeValue);
292
293 checkDirty();
294 }
295 else
296 {
297
298 Object newValue = getModel().getValueAt(first, column);
299 if (originalValue.equals(newValue))
300 {
301
302 row.remove(new Integer(column));
303 if (row.size() < 1)
304 {
305 updatedRows.remove(new Integer(first));
306 checkDirty();
307 }
308 }
309 else
310 {
311
312 checkDirty();
313 }
314 }
315 }
316
317
318
319
320
321 if (cachedPreChangeValue != null) {
322 Object originalValue = getOriginalValueAt(first, column);
323 changed = !cachedPreChangeValue.equals(originalValue);
324 } else {
325 changed = getModel().getValueAt(first, column) != null;
326 }
327
328 if (changed) {
329
330
331 if (row == null) {
332
333 }
334
335
336
337
338
339
340
341 if (row.get(new Integer(column)) == null)
342 {
343 row.put(new Integer(column), getModel().getValueAt(first,
344 column));
345 Application.ensureLocalCookie(EXQLConstants.MENU_EXECUTE_CHANGES, 1,tool);
346 }
347 }
348 cachedPreChangeValue = null;
349 }
350 }
351 }
352 });
353 }
354 else
355 {
356 Application.ensureLocalCookie(EXQLConstants.MENU_INSERT_ROW, 0, tool);
357 Application.ensureLocalCookie(EXQLConstants.MENU_DELETE_ROW, 0, tool);
358 Application.ensureLocalCookie(EXQLConstants.MENU_EXECUTE_CHANGES, 0, tool);
359 }
360 }
361
362 /***
363 * This method returns the original value at the specified location.
364 * (This will be the same as was originally populated in the table)
365 * If the value hasn;t changed, this will have the same effect as calling getValueAt
366 * @param row
367 * @param column
368 * @return
369 */
370 public Object getOriginalValueAt(int row, int column)
371 {
372 Map update = (Map) updatedRows.get(new Integer(row));
373 if (update != null) {
374 Object value = update.get(new Integer(column));
375 if (value != null)
376 return value;
377 }
378
379 return getValueAt(row, column);
380 }
381
382 /***
383 * Reindex
384 *
385 * @param insertedRowIndex
386 */
387 private Map reindex(Map mapToReIndex, int insertedRowIndex) {
388 Map newHashMap = new HashMap();
389 for (Iterator it = mapToReIndex.keySet().iterator(); it.hasNext();) {
390 int oldValue = ((Integer) it.next()).intValue();
391 if (oldValue >= insertedRowIndex) {
392 int newValue = oldValue + 1;
393 newHashMap.put(new Integer(newValue), mapToReIndex
394 .get(new Integer(oldValue)));
395 } else {
396 newHashMap.put(new Integer(oldValue), mapToReIndex
397 .get(new Integer(oldValue)));
398 }
399 }
400 return newHashMap;
401
402 }
403
404 /***
405 * Overrides TableInterface getCellRenederer. Bases it's decision on what
406 * renderer to return by looking at the property type for the Preference
407 * in the given row
408 *
409 * @param row
410 * @param column
411 * @return
412 */
413 public TableCellRenderer getCellRenderer(int row, int column) {
414 DBEntityColumn col = getDBEntityColumn(column);
415
416 if (!isCellEditable(row, column)) {
417
418
419
420
421 if (deletedRows.get(new Integer(row)) != null) {
422 return new DeletedValueCellRenderer(col, this);
423 }
424
425 return new NonEditableValueCellRenderer(col, this);
426 }
427
428
429 if (insertedRows.get(new Integer(row)) != null) {
430 return new UpdatedValueCellRenderer(col, this);
431 }
432
433
434 Map update = (Map) updatedRows.get(new Integer(row));
435 if (update != null && update.get(new Integer(column)) != null)
436 return new UpdatedValueCellRenderer(col, this);
437
438 return new DisplayValueCellRenderer(col, this);
439
440 }
441
442 /***
443 * @param column
444 * @return
445 */
446 public DBEntityColumn getDBEntityColumn(int column)
447 {
448 if (tool != null)
449 {
450 if (tool.getCurrentEntityMetaData() != null)
451 {
452 entity = tool.getCurrentEntityMetaData().getDbEntity();
453 }
454 }
455
456 DBEntityColumn col = null;
457 if (entity != null)
458 {
459 int modelColumn = convertColumnIndexToModel(column);
460 col = entity.getColumn(getColumnName(column));
461 }
462 return col;
463 }
464
465 /***
466 * @see javax.swing.JTable#getCellEditor(int, int)
467 * @param row
468 * @param column
469 * @return
470 */
471 public TableCellEditor getCellEditor(int row, int column) {
472 cachedPreChangeValue = getModel().getValueAt(row, column);
473 DBEntityColumn col = getDBEntityColumn(column);
474 return new TextBasedTableCellEditor(tool, this, col);
475 }
476
477 /***
478 * This method checks whether or not this table should be dirty or not
479 * and sets and resets the application cookie for accepting commit actions.
480 * if it should.
481 *
482 */
483 private void checkDirty()
484 {
485 if (updatedRows.size() > 0 || deletedRows.size() > 0 || insertedRows.size() > 0 )
486 Application.ensureLocalCookie(EXQLConstants.MENU_EXECUTE_CHANGES, 1,tool);
487 else
488 Application.ensureLocalCookie(EXQLConstants.MENU_EXECUTE_CHANGES, 0,tool);
489 }
490
491 /***
492 * @return Returns the dirty.
493 */
494 public boolean isDirty() {
495 return updatedRows.size() > 0 || deletedRows.size() > 0 || insertedRows.size() > 0;
496 }
497
498 /***
499 * @return Returns the isEditable.
500 */
501 public boolean isEditable() {
502 return isEditable;
503 }
504 /***
505 * @param isEditable
506 * The isEditable to set.
507 */
508 public void setEditable(boolean isEditable) {
509 this.isEditable = isEditable;
510 }
511
512 /***
513 * Returns a map of the indexes of the deleted rows in the table
514 * @return
515 */
516 public Map getDeletedRows()
517 {
518 return deletedRows;
519 }
520
521 /***
522 * Returns a map of the indexes of the inserted rows in the table
523 * @return
524 */
525 public Map getInsertedRows()
526 {
527 return insertedRows;
528 }
529
530 /***
531 * Returns a map of the indexes of the changed rows in the table
532 * @return
533 */
534 public Map getUpdatedRows()
535 {
536 return updatedRows;
537 }
538
539 /***
540 * Undo the last command executed by the user
541 */
542 public void undo() throws Exception
543 {
544 }
545
546 /***
547 * Undo the last n commands executed by the user
548 */
549 public void undo(int numberOfSteps) throws Exception
550 {
551
552 }
553
554 /***
555 * Allows redoable edits to be redone
556 * @throws Exception
557 */
558 public void redo() throws Exception
559 {
560 }
561
562 /***
563 * Allows Cut to be performed
564 * @throws Exception
565 */
566 public void cut() throws Exception
567 {
568 this.deleteSelectedRows();
569 }
570
571 /***
572 * Allows sopy to be performed
573 * @throws Exception
574 */
575 public void copy() throws Exception
576 {
577 Transferable t = new RowsTransferable(this);
578 system.setContents(t,this);
579 }
580
581 public void lostOwnership(Clipboard clipboard, Transferable contents)
582 {
583 }
584
585 /***
586 * Allows paste to be performed
587 * @throws Exception
588 */
589 public void paste() throws Exception
590 {
591 Transferable data = system.getContents(null);
592 if (data.isDataFlavorSupported(DsProTableDataFlavor.dsproTableDataFlavor()))
593 {
594 pasteInFromTransferableRows(data);
595 }
596 else if (data.isDataFlavorSupported(DataFlavor.stringFlavor))
597 {
598 pasteInFromText(data);
599 }
600 else
601 {
602 JOptionPane.showMessageDialog(this, "Cannot paste: Incompatible clipboard data");
603 }
604 }
605
606 /***
607 * Uses an existing Transferrable rows object to paste into the database
608 * @param data
609 * @throws IOException
610 * @throws UnsupportedFlavorException
611 */
612 private void pasteInFromTransferableRows(Transferable data) throws UnsupportedFlavorException, IOException
613 {
614 RowsTransferable transferable = (RowsTransferable) data.getTransferData(DsProTableDataFlavor.dsproTableDataFlavor());
615 List rows = transferable.getRows();
616 if (this.getColumnCount() != transferable.getColumnCount())
617 {
618 JOptionPane.showMessageDialog(this, "Cannot paste rows as the number of columns does not match");
619 return;
620 }
621
622 for (int i=0;i<rows.size();i++)
623 {
624 int rowId = insertNewRow();
625 List values = (ArrayList) rows.get(i);
626 for (int t=0;t<values.size();t++)
627 {
628 Object object = values.get(t);
629 DBEntityColumn entityColumn = (DBEntityColumn) transferable.getColumn(t);
630 if (isCellEditable(rowId, t))
631 {
632 this.setValueAt(object, rowId, t);
633 }
634 }
635 }
636 }
637
638 /***
639 * Parses out delimited text into rows and columns and pastes it in if possible
640 * @param data
641 * @throws UnsupportedFlavorException
642 * @throws IOException
643 * @throws InvalidDataException
644 */
645 private void pasteInFromText(Transferable data) throws UnsupportedFlavorException, IOException, InvalidDataException {
646 String text = (String) data.getTransferData(DataFlavor.stringFlavor);
647 log.debug(text);
648 StringTokenizer lineTokenizer = new StringTokenizer(text, System.getProperty("line.separator"));
649 while(lineTokenizer.hasMoreTokens())
650 {
651 StringTokenizer fieldTokenizer = new StringTokenizer(lineTokenizer.nextToken(), "\t");
652 int column = 0;
653 int rowId = insertNewRow();
654 int numberOfTokens = fieldTokenizer.countTokens();
655 if ( numberOfTokens != this.getColumnCount())
656 {
657 JOptionPane.showMessageDialog(this, "Cannot paste rows as the number of columns does not match");
658 }
659 else
660 {
661 while(fieldTokenizer.hasMoreTokens())
662 {
663 if (column < this.getColumnCount())
664 {
665 Object value = EditorAndRenderFactory.convertIntoCorrectTypeForColumn(fieldTokenizer.nextToken(),getDBEntityColumn(column) );
666 if (isCellEditable(rowId, column))
667 {
668 this.setValueAt(value, rowId, column);
669 }
670 column++;
671 }
672 else
673 {
674 log.debug(fieldTokenizer.nextToken());
675 }
676 }
677 }
678 }
679 }
680
681 /***
682 * Returns the textComponent associated with this object
683 * @return
684 */
685 public JTextComponent getTextComponent()
686 {
687 return null;
688 }
689
690 /***
691 * @see com.explosion.expfmodules.texteditor.Editable#clear()
692 */
693 public void clear() throws Exception {
694 }
695
696 /***
697 * @see com.explosion.expfmodules.texteditor.Editable#select(int, int)
698 */
699 public void select(int startSelection, int endSelection) {
700 this.getSelectionModel().setAnchorSelectionIndex(startSelection);
701 this.getSelectionModel().setLeadSelectionIndex(endSelection);
702 }
703
704 /***
705 * Pops up a popup menu
706 * @param event
707 */
708 public void popupEvent(MouseEvent event) {
709 try
710 {
711 menu = new ExpPopupableMenu();
712 MenuHelper menuHelper = new MenuHelper(((ExpFrame)Application.getApplicationFrame()).getListener());
713
714 ExpMenuSegment actionsSegment = menu.createNewSegment(ExpMenuSegment.ANY_POSITION);
715 ExpMenuSegment cutcopy = menu.createNewSegment(ExpMenuSegment.ANY_POSITION);
716 ExpMenuSegment select = menu.createNewSegment(ExpMenuSegment.ANY_POSITION);
717
718
719
720
721 if (tool.getCurrentEntityMetaData() != null && tool.getCurrentEntityMetaData().getDbEntity() != null && tool.getCurrentEntityMetaData().getDbEntity().getEntityName() != null)
722 {
723 actionsSegment.addElement(menuHelper.createMenuItem("Commit changes", 'e', EXQLConstants.MENU_EXECUTE_CHANGES, null, 1));
724 cutcopy.addElement(menuHelper.createMenuItem("Insert empty row", 'i', EXQLConstants.MENU_INSERT_ROW, null, 1));
725 cutcopy.addElement(menuHelper.createMenuItem("Delete row", 'd', EXQLConstants.MENU_DELETE_ROW, null, 1));
726 }
727
728 actionsSegment.addElement(menuHelper.createMenuItem("Export results", 'x', EXQLConstants.MENU_EXPORT, null, 1));
729 cutcopy.addElement(menuHelper.createMenuItem("Copy row", 'c', ExpConstants.MENU_COPY, null, 1));
730 cutcopy.addElement(menuHelper.createMenuItem("Paste copied row", 'p', ExpConstants.MENU_PASTE, null, 1));
731
732 select.addElement(menuHelper.createMenuItem("Select All", 'a', ExpConstants.MENU_SELECTALL, null, 1));
733
734 if (this.getSelectedRowCount() < 1)
735 {
736 Application.ensureLocalCookie(EXQLConstants.MENU_DELETE_ROW, 0,tool);
737 Application.ensureLocalCookie(ExpConstants.MENU_COPY, 0,tool);
738 }
739 else
740 {
741 Application.ensureLocalCookie(EXQLConstants.MENU_DELETE_ROW, 1,tool);
742 Application.ensureLocalCookie(ExpConstants.MENU_COPY, 1,tool);
743 }
744
745 Transferable data = system.getContents(null);
746 if (data.isDataFlavorSupported(DsProTableDataFlavor.dsproTableDataFlavor()))
747 {
748 Application.ensureLocalCookie(ExpConstants.MENU_PASTE, 1,tool);
749 }
750 else if (data.isDataFlavorSupported(DataFlavor.stringFlavor))
751 {
752 Application.ensureLocalCookie(ExpConstants.MENU_PASTE, 1,tool);
753 }
754 else
755 {
756 Application.ensureLocalCookie(ExpConstants.MENU_PASTE, 0,tool);
757 }
758
759
760
761
762
763
764
765
766
767
768
769 menuHelper.checkEnabled();
770 menu.show(event);
771 }
772 catch (Exception e)
773 {
774 ExceptionManagerFactory.getExceptionManager().manageException(e,"Exception caught while handling popupEvent");
775 }
776 }
777
778 }
779
780 /***
781 * Listens for selection events on this table
782 *
783 * @author Stephen Created on May 9, 2004
784 */
785
786 class SelectionListener implements ListSelectionListener {
787
788 private DsProTable table;
789 private EXQLBaseTool tool;
790
791
792
793 SelectionListener(DsProTable table, EXQLBaseTool tool) {
794 this.table = table;
795 this.tool = tool;
796 }
797
798 public void valueChanged(ListSelectionEvent e) {
799
800 if (e.getValueIsAdjusting())
801 return;
802
803 ListSelectionModel lsm = (ListSelectionModel) e.getSource();
804 if (!table.isEditable() || lsm.isSelectionEmpty()) {
805 Application.ensureLocalCookie(EXQLConstants.MENU_DELETE_ROW, 0,
806 tool);
807 } else {
808 Application.ensureLocalCookie(EXQLConstants.MENU_DELETE_ROW, 1,
809 tool);
810 }
811 }
812
813 }