Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.fmap.control / src / main / java / org / gvsig / fmap / mapcontrol / dal / feature / swing / FeatureTable.java @ 47436

History | View | Annotate | Download (12.8 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.mapcontrol.dal.feature.swing;
25

    
26
import java.awt.Color;
27
import java.awt.Component;
28
import java.awt.event.KeyAdapter;
29
import java.awt.event.KeyEvent;
30
import java.math.BigDecimal;
31
import java.sql.Time;
32
import java.sql.Timestamp;
33
import java.util.Date;
34
import java.util.Objects;
35

    
36
import javax.swing.JTable;
37
import javax.swing.event.ChangeEvent;
38
import javax.swing.event.TableModelEvent;
39
import javax.swing.table.TableCellRenderer;
40
import javax.swing.table.TableColumn;
41
import javax.swing.table.TableColumnModel;
42
import javax.swing.table.TableModel;
43
import org.apache.commons.lang3.StringUtils;
44

    
45
import org.gvsig.fmap.dal.exception.DataException;
46
import org.gvsig.fmap.dal.feature.Feature;
47
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
48
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
49
import org.gvsig.fmap.geom.Geometry;
50
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.BooleanCellRenderer;
51
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.ConfigurableFeatureTableModel;
52
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.FeatureAttributeCellRenderer;
53
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.FeatureCellRenderer;
54
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.FeatureTableModel;
55
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.FormattedCellEditor;
56
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.FormattedCellRenderer;
57
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.GeometryWKTCellEditor;
58
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.GeometryWKTCellRenderer;
59
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.JToggleButtonHeaderCellRenderer;
60
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.notification.ColumnHeaderSelectionChangeNotification;
61
import org.gvsig.tools.observer.Observable;
62
import org.gvsig.tools.observer.Observer;
63
import org.gvsig.tools.swing.api.ToolsSwingLocator;
64
import org.slf4j.Logger;
65
import org.slf4j.LoggerFactory;
66

    
67
/**
68
 * Table extension to show Feature values.
69
 *
70
 * It's based on the usage of a FeatureTableModel, and adds renderers for
71
 * Geometry and Feature cell values.
72
 *
73
 * Observers are notified about column header selection changes, with a
74
 * {@link ColumnHeaderSelectionChangeNotification}.
75
 *
76
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
77
 */
78
@SuppressWarnings("UseSpecificCatch")
79
public class FeatureTable extends JTable implements Observer, Observable {
80

    
81
    private static final Logger LOGGER = LoggerFactory.getLogger(FeatureTable.class);
82
    /**
83
     * Generated Serial UID
84
     */
85
    private static final long serialVersionUID = -6139395189283163964L;
86

    
87
    
88
    private final FeatureTableModel featureTableModel;
89
    private JToggleButtonHeaderCellRenderer headerCellRenderer;
90

    
91
        private static final int COLUMN_HEADER_MARGIN = 8;
92

    
93
        private static final int COLUMN_HEADER_MIN_WIDTH = 50;
94

    
95
    /**
96
     * Creates a new FeatureTable with a {@link FeatureTableModel}.
97
     *
98
     * @param featureTableModel
99
     *            the table model to get data to be shown on the table
100
     * @throws DataException
101
     *             if there is an error while loading the Features to show
102
     * @see JTable#JTable(TableModel)
103
     */
104
    public FeatureTable(FeatureTableModel featureTableModel)
105
            throws DataException {
106
        super(featureTableModel);
107
        this.featureTableModel = featureTableModel;
108
        init();
109
    }
110

    
111
    /**
112
     * Creates a new FeatureTable with a {@link FeatureTableModel}.
113
     *
114
     * @param featureTableModel
115
     *            the table model to get data to be shown on the table
116
     * @param cm
117
     *            the table column model to use
118
     * @throws DataException
119
     *             if there is an error while loading the Features to show
120
     * @see JTable#JTable(TableModel, TableColumnModel)
121
     */
122
    public FeatureTable(FeatureTableModel featureTableModel, TableColumnModel cm)
123
            throws DataException {
124
        super(featureTableModel, cm);
125
        this.featureTableModel = featureTableModel;
126
        init();
127
    }
128

    
129
    @Override
130
    public void update(Observable observable, Object notification) {
131
        if (notification instanceof FeatureStoreNotification) {
132
            FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
133
            String type = fsNotification.getType();
134
            // If the selection has changed, repaint the table to show the new
135
            // selected rows
136
            if (FeatureStoreNotification.SELECTION_CHANGE.equals(type)) {
137
                repaint();
138
            }
139

    
140
            /*
141
             * This is necessary to let Swing know
142
             * that editing (in terms of Swing, not gvsig editing)
143
             * must be cancelled because the deleted row
144
             * is perhaps the row that was being edited
145
             */
146
            if (FeatureStoreNotification.BEFORE_DELETE.equals(type)) {
147
                if (this.isEditing()) {
148
                    ChangeEvent che = new ChangeEvent(this);
149
                    this.editingCanceled(che);
150
                }
151
            }
152
        }
153
    }
154

    
155
    /**
156
     * Returns the FeatureAttributeDescriptor related to the selected columns.
157
     *
158
     * @return an array of FeatureAttributeDescriptor
159
     *
160
     * @see org.gvsig.fmap.mapcontrol.dal.feature.swing.table.JToggleButtonHeaderCellRenderer#getSelectedColumns()
161
     */
162
    public FeatureAttributeDescriptor[] getSelectedColumnsAttributeDescriptor() {
163
        int[] columns = headerCellRenderer.getSelectedColumns();
164
        FeatureAttributeDescriptor[] descriptors = new FeatureAttributeDescriptor[columns.length];
165

    
166
        for (int i = 0; i < descriptors.length; i++) {
167
            descriptors[i] = featureTableModel
168
                    .getDescriptorForColumn(columns[i]);
169
        }
170

    
171
        return descriptors;
172
    }
173

    
174
    @Override
175
    public void addObserver(Observer observer) {
176
        headerCellRenderer.addObserver(observer);
177
    }
178

    
179
    @Override
180
    public void deleteObserver(Observer observer) {
181
        headerCellRenderer.deleteObserver(observer);
182
    }
183

    
184
    @Override
185
    public void deleteObservers() {
186
        headerCellRenderer.deleteObservers();
187
    }
188

    
189
        /**
190
         * Sets that the selected Features to be viewed first.
191
     * @param selectionUp
192
         */
193
        public void setSelectionUp(boolean selectionUp) {
194
                ((FeatureTableModel) getModel()).setSelectionUp(selectionUp);
195
                scrollRectToVisible(getCellRect(0, 0, true));
196
        }
197

    
198
    // @Override
199
    // public void tableChanged(TableModelEvent e) {
200
    // super.tableChanged(e);
201
    // if (headerCellRenderer != null) {
202
    // headerCellRenderer.deselectAll();
203
    // }
204
    // }
205

    
206
    @Override
207
    protected void initializeLocalVars() {
208
        super.initializeLocalVars();
209
        // Add a cell renderer for Geometries and Features
210
        setDefaultRenderer(Geometry.class, new GeometryWKTCellRenderer());
211
        setDefaultEditor(Geometry.class, new GeometryWKTCellEditor());
212

    
213
        setDefaultRenderer(Feature.class, new FeatureCellRenderer());
214
        setDefaultRenderer(BigDecimal.class, new FormattedCellRenderer());
215
        setDefaultRenderer(Double.class, new FormattedCellRenderer());
216
        setDefaultRenderer(Float.class, new FormattedCellRenderer());
217
        setDefaultRenderer(Integer.class, new FormattedCellRenderer());
218
        setDefaultRenderer(Long.class, new FormattedCellRenderer());
219
        setDefaultRenderer(Date.class, new FormattedCellRenderer());
220
        setDefaultRenderer(Boolean.class, new BooleanCellRenderer());
221
        setDefaultRenderer(String.class, new FeatureAttributeCellRenderer());
222
        setDefaultRenderer(Byte.class, new FeatureAttributeCellRenderer());
223

    
224
        if( this.getModel() instanceof ConfigurableFeatureTableModel ) {
225
            ConfigurableFeatureTableModel model = (ConfigurableFeatureTableModel)this.getModel();
226
            setDefaultEditor(Double.class, new FormattedCellEditor(model));
227
            setDefaultEditor(Float.class, new FormattedCellEditor(model));
228
            setDefaultEditor(Integer.class, new FormattedCellEditor(model));
229
            setDefaultEditor(Long.class, new FormattedCellEditor(model));
230
            setDefaultEditor(Date.class, new FormattedCellEditor(model));
231
        }
232

    
233
        // Set the selected row colors
234
        setSelectionForeground(Color.blue);
235
        setSelectionBackground(Color.yellow);
236
    }
237

    
238
    /**
239
     * Initializes the table GUI.
240
     */
241
    private void init() throws DataException {
242
      setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
243

    
244
      featureTableModel.getFeatureStore().addObserver(this);
245
      // Change the selection model to link with the FeatureStore selection
246
      // through the FeatureTableModel
247
      setRowSelectionAllowed(true);
248
      setColumnSelectionAllowed(false);
249
      setSelectionModel(new FeatureSelectionModel(featureTableModel));
250

    
251
      headerCellRenderer = new JToggleButtonHeaderCellRenderer(this);
252
      getTableHeader().setDefaultRenderer(headerCellRenderer);
253

    
254
      TableColumnModel tcmodel = getColumnModel();
255
      for (int i = 0; i < tcmodel.getColumnCount(); i++) {
256
        TableColumn col = tcmodel.getColumn(i);
257
        // Get width of column header
258
        TableCellRenderer renderer = col.getHeaderRenderer();
259
        if (renderer == null) {
260
          renderer = getTableHeader().getDefaultRenderer();
261
        }
262
        Component comp
263
                = renderer.getTableCellRendererComponent(this,
264
                        col.getHeaderValue(), false, false, 0, i);
265
        int width = comp.getPreferredSize().width;
266
        width
267
                = width < COLUMN_HEADER_MIN_WIDTH ? COLUMN_HEADER_MIN_WIDTH
268
                        : width;
269
        col.setPreferredWidth(width + 2 * COLUMN_HEADER_MARGIN);
270
      }
271
      try {
272
        if( !this.featureTableModel.getFeatureStore().getFeatureSelection().isAvailable() ) {
273
          this.setSelectionBackground(Color.PINK);
274
        }
275
      } catch(Exception ex) {
276
        LOGGER.warn("Can't check if selecction is available.", ex);
277
      }
278
        this.addKeyListener(new KeyAdapter() {
279
            @Override
280
            public void keyPressed(KeyEvent e) {
281
                if( e.getKeyCode()==KeyEvent.VK_F4 ) {
282
                    doShowCellInDialog();
283
                }
284
            }
285
        });
286
      
287
    }
288
    private void doShowCellInDialog() {
289
        int row = this.getSelectedRow();
290
        if( row < 0 ) {
291
            return;
292
        }
293
        int col = this.getSelectedColumn();
294
        if( col < 0 ) {
295
            return;
296
        }
297
        Object v = this.getValueAt(row, col);
298
        String s;
299
        if( v instanceof Geometry ) {
300
            s = ((Geometry)v).convertToWKTQuietly();
301
        } else {
302
            s = Objects.toString(this.getValueAt(row, col),null);
303
        }
304
        if( StringUtils.isBlank(s) ) {
305
            return;
306
        }
307
        ToolsSwingLocator.getToolsSwingManager().showZoomDialog(
308
            this, 
309
            this.getColumnName(col), 
310
            s,
311
            false
312
        );
313
    }
314

    
315

    
316
    /**
317
     * Returns the number of selected columns.
318
     *
319
     * @return the number of selected columns, 0 if no columns are selected
320
     */
321
    @Override
322
    public int getSelectedColumnCount() {
323
        return headerCellRenderer.getSelectedColumns().length;
324
    }
325

    
326
    @Override
327
    public void tableChanged(TableModelEvent e) {
328
        // Clear the header selection
329
        if (e != null && e.getFirstRow() == TableModelEvent.HEADER_ROW
330
            && headerCellRenderer != null) {
331
            headerCellRenderer.deselectAll();
332
        }
333

    
334
        super.tableChanged(e);
335
    }
336

    
337
    @Override
338
    public Class<?> getColumnClass(int column) {
339
        Class resp = super.getColumnClass(column);
340
        if (Timestamp.class.isAssignableFrom(resp)) {
341
            return Object.class;
342
        } else {
343
            return resp;
344
        }
345
    }
346

    
347
    @Override
348
    public int getSelectedRowCount() {
349
        try {
350
            return (int) this.featureTableModel.getFeatureStore().getFeatureSelection().getSelectedCount();
351
        } catch (DataException ex) {
352
            LOGGER.error("Can't calculate selected rows in table.", ex);
353
            return 0;
354
        }
355
    }
356

    
357

    
358
}