View Javadoc
1   package fr.ifremer.quadrige3.ui.swing.table.action;
2   
3   /*
4    * #%L
5    * Reef DB :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import fr.ifremer.quadrige3.core.dao.technical.Assert;
27  import fr.ifremer.quadrige3.ui.swing.table.*;
28  import fr.ifremer.quadrige3.ui.swing.table.editor.AbstractCellEditor;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  
32  import javax.swing.AbstractAction;
33  import javax.swing.SwingUtilities;
34  import javax.swing.table.TableCellEditor;
35  import java.awt.Component;
36  import java.awt.event.ActionEvent;
37  
38  /**
39   * Abstract Action performed on a table responsive on key bindings
40   * <p/>
41   * Created by Ludovic on 01/06/2015.
42   */
43  public abstract class AbstractCellSelectionAction extends AbstractAction {
44  
45      private static final Log LOG = LogFactory.getLog(AbstractCellSelectionAction.class);
46      private SwingTable currentTable;
47      private final AbstractTableUIHandler handler;
48      private final boolean forceStopEditingBeforeAction;
49      private boolean previousTerminateEditOnFocusLost;
50  
51      /**
52       * <p>Constructor for AbstractCellSelectionAction.</p>
53       *  @param name a {@link String} object.
54       * @param handler a {@link AbstractTableUIHandler} object.
55       * @param forceStopEditingBeforeAction
56       */
57      public AbstractCellSelectionAction(String name, AbstractTableUIHandler handler, boolean forceStopEditingBeforeAction) {
58          super(name);
59          this.handler = handler;
60          this.forceStopEditingBeforeAction = forceStopEditingBeforeAction;
61      }
62  
63      private AbstractTableModel getTableModel() {
64          return handler.getTableModel();
65      }
66  
67      public SwingTable getCurrentTable() {
68          return currentTable;
69      }
70  
71      public void setCurrentTable(SwingTable currentTable) {
72          this.currentTable = currentTable;
73      }
74  
75      private SwingTable getMainTable() {
76          return handler.getTable();
77      }
78  
79      private SwingTable getLeftTable() {
80          return handler.getFixedTable();
81      }
82  
83      protected boolean onLeftTable() {
84          return getCurrentTable() == getLeftTable();
85      }
86  
87      protected boolean onMainTable() {
88          return getCurrentTable() == getMainTable();
89      }
90  
91      protected void switchToLeftTable() {
92          if (onMainTable() && getLeftTable() != null) {
93              stopActiveEdition();
94              getMainTable().clearSelection();
95              setCurrentTable(getLeftTable());
96          }
97      }
98  
99      protected void switchToMainTable() {
100         if (onLeftTable()) {
101             stopActiveEdition();
102             getLeftTable().clearSelection();
103             setCurrentTable(getMainTable());
104         }
105     }
106 
107     @Override
108     public void actionPerformed(ActionEvent e) {
109         Assert.isInstanceOf(SwingTable.class, e.getSource(), "This action must be performed on a SwingTable");
110 
111         setCurrentTable((SwingTable) e.getSource());
112 
113         beforeAction();
114         actionPerformed();
115         SwingUtilities.invokeLater(this::afterAction);
116     }
117 
118     private void beforeAction() {
119         previousTerminateEditOnFocusLost = getMainTable().isTerminateEditOnFocusLost();
120         // disable terminate action on focus lost to prevent focus problem switching both tables
121         getMainTable().setTerminateEditOnFocusLost(false);
122         if (getLeftTable() != null)
123             getLeftTable().setTerminateEditOnFocusLost(false);
124     }
125 
126     private void afterAction() {
127         getMainTable().setTerminateEditOnFocusLost(previousTerminateEditOnFocusLost);
128         if (getLeftTable() != null)
129             getLeftTable().setTerminateEditOnFocusLost(previousTerminateEditOnFocusLost);
130     }
131 
132     protected abstract void actionPerformed();
133 
134     /**
135      * <p>isTableEditing.</p>
136      *
137      * @return a boolean.
138      */
139     protected boolean isTableEditing() {
140         return getCurrentTable().isEditing();
141     }
142 
143     protected boolean isForceStopEditingBeforeAction() {
144         return forceStopEditingBeforeAction;
145     }
146 
147     /**
148      * <p>isCreateNewRow.</p>
149      *
150      * @return a boolean.
151      */
152     protected boolean isCreateNewRow() {
153         return getTableModel().isCreateNewRow();
154     }
155 
156     /**
157      * <p>addNewRow.</p>
158      */
159     protected void addNewRow() {
160         // stop previous cell editing first
161         if (stopActiveEdition()) {
162             // add a new row in model
163             getTableModel().addNewRow();
164         }
165     }
166 
167     /**
168      * <p>getRowCount.</p>
169      *
170      * @return a int.
171      */
172     protected int getRowCount() {
173         return getCurrentTable().getRowCount();
174     }
175 
176     /**
177      * <p>getColumnCount.</p>
178      *
179      * @return a int.
180      */
181     protected int getColumnCount() {
182         return getCurrentTable().getColumnCount();
183     }
184 
185     /**
186      * <p>getSelectedRow.</p>
187      *
188      * @return a int.
189      */
190     protected int getSelectedRow() {
191         return getCurrentTable().getSelectedRow();
192     }
193 
194     /**
195      * <p>getSelectedColumn.</p>
196      *
197      * @return a int.
198      */
199     protected int getSelectedColumn() {
200         return getCurrentTable().getSelectedColumn();
201     }
202 
203     /**
204      * <p>getNextColumn.</p>
205      *
206      * @param column a int.
207      * @return a int.
208      */
209     protected int getNextColumn(int column) {
210         int nextColumn = column;
211         do {
212             nextColumn++;
213         } while (nextColumn < getColumnCount() && getCurrentTable().getColumn(nextColumn) instanceof FixedColumn);
214 
215         if (nextColumn >= getColumnCount() && onLeftTable()) {
216             // switch to main table
217             switchToMainTable();
218             return getNextColumn(-1);
219         }
220 
221         return nextColumn;
222     }
223 
224     /**
225      * <p>getPreviousColumn.</p>
226      *
227      * @param column a int.
228      * @return a int.
229      */
230     protected int getPreviousColumn(int column) {
231         int nextColumn = column;
232         do {
233             nextColumn--;
234         } while (nextColumn >= 0 && getCurrentTable().getColumn(nextColumn) instanceof FixedColumn);
235 
236         if (nextColumn < 0 && onMainTable() && getLeftTable() != null) {
237             // switch to left table
238             switchToLeftTable();
239             return getPreviousColumn(getColumnCount());
240         }
241 
242         return nextColumn;
243     }
244 
245     /**
246      * <p>isCellValid.</p>
247      *
248      * @param row a int.
249      * @param column a int.
250      * @return a boolean.
251      */
252     protected boolean isCellValid(int row, int column) {
253         return row > -1 && column > -1 && row < getRowCount() && column < getColumnCount();
254     }
255 
256     /**
257      * <p>isCellEditable.</p>
258      *
259      * @param row a int.
260      * @param column a int.
261      * @return a boolean.
262      */
263     protected boolean isCellEditable(int row, int column) {
264         return isCellValid(row, column)
265             && getCurrentTable().isCellEditable(row, column)
266             && !(getCurrentTable().getColumn(column) instanceof FixedColumn);
267     }
268 
269     /**
270      * <p>selectCell.</p>
271      *
272      * @param row a int.
273      * @param column a int.
274      * @return a boolean.
275      */
276     protected boolean selectCell(int row, int column) {
277 
278         if (!isCellValid(row, column))
279             return false;
280 
281         // stop previous cell editing first
282         if (!stopActiveEdition())
283             return false;
284 
285         getCurrentTable().setColumnSelectionInterval(column, column);
286         getCurrentTable().setRowSelectionInterval(row, row);
287         getCurrentTable().scrollCellToVisible(row, column);
288         return true;
289     }
290 
291     /**
292      * <p>editCell.</p>
293      *
294      * @param row a int.
295      * @param column a int.
296      */
297     protected void editCell(int row, int column) {
298         if (selectCell(row, column)) {
299             getCurrentTable().editCellAt(row, column);
300             TableCellEditor editor = getCurrentTable().getCellEditor();
301             if (editor instanceof AbstractCellEditor)
302                 ((AbstractCellEditor) editor).getTextField().requestFocus();
303         }
304     }
305 
306     /**
307      * <p>stopActiveEdition.</p>
308      *
309      * @return a boolean.
310      */
311     protected boolean stopActiveEdition() {
312 
313         TableCellEditor editor = getMainTable().getCellEditor();
314         boolean stopped = editor == null || editor.stopCellEditing();
315 
316         if (getLeftTable() != null) {
317             editor = getLeftTable().getCellEditor();
318             stopped &= editor == null || editor.stopCellEditing();
319         }
320 
321         return stopped;
322     }
323 
324     /**
325      * <p>selectNextComponent.</p>
326      */
327     protected void selectNextComponent() {
328         Component next = handler.getNextComponentToFocus();
329         if (next != null) {
330             afterAction();
331             SwingUtilities.invokeLater(next::requestFocusInWindow);
332         }
333     }
334 
335     /**
336      * <p>selectPreviousComponent.</p>
337      */
338     protected void selectPreviousComponent() {
339         Component previous = handler.getPreviousComponentToFocus();
340         if (previous != null) {
341             afterAction();
342             SwingUtilities.invokeLater(previous::requestFocusInWindow);
343         }
344     }
345 
346     public enum Direction {
347         NEXT_CELL, PREVIOUS_CELL, NEXT_ROW, PREVIOUS_ROW
348     }
349 }
350