1 package fr.ifremer.quadrige3.ui.swing.table;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import com.google.common.collect.Lists;
27 import com.google.common.collect.Sets;
28 import fr.ifremer.quadrige3.core.dao.technical.AlphanumericComparator;
29 import fr.ifremer.quadrige3.core.dao.technical.Assert;
30 import fr.ifremer.quadrige3.core.dao.technical.decorator.Decorator;
31 import fr.ifremer.quadrige3.core.dao.technical.decorator.DecoratorComparator;
32 import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
33 import fr.ifremer.quadrige3.core.service.ClientServiceLocator;
34 import fr.ifremer.quadrige3.core.service.decorator.DecoratorService;
35 import fr.ifremer.quadrige3.ui.swing.AbstractUIHandler;
36 import fr.ifremer.quadrige3.ui.swing.ApplicationUI;
37 import fr.ifremer.quadrige3.ui.swing.ApplicationUIUtil;
38 import fr.ifremer.quadrige3.ui.swing.component.bean.ExtendedComboBox;
39 import fr.ifremer.quadrige3.ui.swing.component.coordinate.CoordinateEditor;
40 import fr.ifremer.quadrige3.ui.swing.model.BeanMonitor;
41 import fr.ifremer.quadrige3.ui.swing.table.action.*;
42 import fr.ifremer.quadrige3.ui.swing.table.comment.CommentCellEditor;
43 import fr.ifremer.quadrige3.ui.swing.table.comment.CommentCellRenderer;
44 import fr.ifremer.quadrige3.ui.swing.table.editor.*;
45 import fr.ifremer.quadrige3.ui.swing.table.renderer.DateCellRenderer;
46 import fr.ifremer.quadrige3.ui.swing.table.renderer.ExtendedDecoratorCellRenderer;
47 import fr.ifremer.quadrige3.ui.swing.table.renderer.LocalDateCellRenderer;
48 import fr.ifremer.quadrige3.ui.swing.table.renderer.NumberCellRenderer;
49 import jaxx.runtime.SwingUtil;
50 import jaxx.runtime.swing.editor.bean.BeanFilterableComboBox;
51 import jaxx.runtime.swing.editor.bean.BeanUIUtil;
52 import org.apache.commons.collections4.CollectionUtils;
53 import org.apache.commons.collections4.comparators.ComparableComparator;
54 import org.apache.commons.lang3.ArrayUtils;
55 import org.apache.commons.lang3.StringUtils;
56 import org.apache.commons.logging.Log;
57 import org.apache.commons.logging.LogFactory;
58 import org.jdesktop.swingx.JXTable;
59 import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
60 import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
61 import org.jdesktop.swingx.renderer.DefaultTableRenderer;
62 import org.jdesktop.swingx.renderer.StringValue;
63 import org.jdesktop.swingx.table.TableColumnExt;
64 import org.springframework.beans.BeanUtils;
65
66 import javax.swing.*;
67 import javax.swing.event.*;
68 import javax.swing.table.*;
69 import java.awt.Component;
70 import java.awt.Container;
71 import java.awt.Dimension;
72 import java.awt.Point;
73 import java.awt.event.MouseEvent;
74 import java.beans.IndexedPropertyChangeEvent;
75 import java.beans.PropertyChangeEvent;
76 import java.beans.PropertyChangeListener;
77 import java.math.BigDecimal;
78 import java.text.DecimalFormat;
79 import java.util.*;
80 import java.util.function.Predicate;
81
82 import static org.nuiton.i18n.I18n.t;
83
84
85
86
87
88
89
90
91
92 public abstract class AbstractTableUIHandler<R extends AbstractRowUIModel<?, ?>, M extends AbstractTableUIModel<?, R, M>, UI extends ApplicationUI<M, ?>>
93 extends AbstractUIHandler<M, UI> {
94
95
96
97
98 public static final int DEFAULT_ROW_HEIGHT = 24;
99
100
101
102 public static final int DEFAULT_MIN_COLUMN_WIDTH = 80;
103
104
105
106 private static final Log LOG = LogFactory.getLog(AbstractTableUIHandler.class);
107
108
109
110
111
112 private final BeanMonitor<R> rowMonitor;
113
114
115
116 private ListSelectionListener tableSelectionListener;
117 private ListSelectionListener tableScrollSelectionListener;
118 private TableColumnModelListener columnModelListener;
119 private RowSorterListener rowSorterListener;
120
121 private boolean initializing = false;
122 private boolean initialized = false;
123 private FixedSwingTable fixedTable;
124 private List<TableColumnExt> pendingFixedColumns = new ArrayList<>();
125
126
127
128
129
130
131 @SuppressWarnings("unchecked")
132 protected AbstractTableUIHandler(String... properties) {
133
134 rowMonitor = new BeanMonitor<>(properties);
135
136
137 rowMonitor.addPropertyChangeListener(BeanMonitor.PROPERTY_BEAN, new PropertyChangeListener() {
138
139 final Set<String> propertiesToSkip = Sets.newHashSet(getRowPropertiesToIgnore());
140
141 final PropertyChangeListener l = evt -> {
142 String propertyName = evt.getPropertyName();
143
144 R row = (R) evt.getSource();
145
146 Object oldValue = evt.getOldValue();
147 Object newValue = evt.getNewValue();
148
149 int rowIndex = getTableModel().getRowIndex(row);
150
151 if (AbstractRowUIModel.PROPERTY_VALID.equals(propertyName)) {
152 onRowValidStateChanged(rowIndex, row,
153 (Boolean) oldValue,
154 (Boolean) newValue);
155 } else if (AbstractRowUIModel.PROPERTY_MODIFY.equals(propertyName)) {
156 onRowModifyStateChanged(rowIndex, row,
157 (Boolean) oldValue,
158 (Boolean) newValue);
159 } else if (AbstractRowUIModel.PROPERTY_SELECTED.equals(propertyName)) {
160 onRowSelectedStateChanged(rowIndex, row,
161 (Boolean) oldValue,
162 (Boolean) newValue);
163 } else if (!propertiesToSkip.contains(propertyName)) {
164 if (LOG.isDebugEnabled()) {
165 LOG.debug(
166 "row [" + rowIndex + "] property " + propertyName + " changed from " + oldValue + " to " + newValue);
167 }
168
169
170 if (oldValue != null || newValue != null) {
171 onRowModified(rowIndex, row, propertyName,
172 evt instanceof IndexedPropertyChangeEvent ? ((IndexedPropertyChangeEvent) evt).getIndex() : null,
173 oldValue, newValue);
174 }
175 }
176 };
177
178 @Override
179 public void propertyChange(PropertyChangeEvent evt) {
180 R oldValue = (R) evt.getOldValue();
181 R newValue = (R) evt.getNewValue();
182 if (LOG.isDebugEnabled()) {
183 LOG.debug("Monitor row changed from " + oldValue + " to " + newValue);
184 }
185 if (oldValue != null) {
186 oldValue.removePropertyChangeListener(l);
187 }
188 if (newValue != null) {
189 newValue.addPropertyChangeListener(l);
190 }
191 }
192 });
193 }
194
195
196
197
198
199
200
201 public abstract AbstractTableModel<R> getTableModel();
202
203
204
205
206
207
208
209 public abstract SwingTable getTable();
210
211
212
213
214
215
216 public FixedSwingTable getFixedTable() {
217 return fixedTable;
218 }
219
220
221
222
223
224
225
226
227
228
229 protected void initTable(SwingTable table) {
230 initTable(table, false);
231 }
232
233
234
235
236
237
238
239 protected void initTable(SwingTable table, boolean forceSingleSelection) {
240 initTable(table, forceSingleSelection, false);
241 }
242
243
244
245
246
247
248
249
250 @SuppressWarnings("unchecked")
251 protected void initTable(SwingTable table, boolean singleSelection, boolean checkBoxSelection) {
252 Assert.isTrue(!(singleSelection && checkBoxSelection), "Could not have both 'singleSelection' and 'checkBoxSelection' to 'true'");
253
254 initializing = true;
255 initialized = false;
256
257 if (table.getTableHeader() == null) {
258
259 table.setTableHeader(new SwingTableHeader(getTable().getColumnModel()));
260 table.getTableHeader().setTable(table);
261 table.getTableHeader().updateUI();
262 }
263
264
265 getModel().setTableModel(getTableModel());
266
267 getTableModel().setTableUIModel(getModel());
268
269
270 table.setAutoResizeMode(JXTable.AUTO_RESIZE_OFF);
271
272 table.setRowHeight(DEFAULT_ROW_HEIGHT);
273 table.setHorizontalScrollEnabled(true);
274
275
276 table.getTableHeader().setReorderingAllowed(true);
277
278 initHighlighters(table);
279
280
281 getModel().addPropertyChangeListener(AbstractTableUIModel.PROPERTY_ROWS, evt -> onModelRowsChanged((List<R>) evt.getNewValue()));
282
283 getTableModel().setRows(getModel().getRows());
284
285
286 getModel().addPropertyChangeListener(AbstractTableUIModel.PROPERTY_ROWS_ADDED, evt -> onRowsAdded((List<R>) evt.getNewValue()));
287
288
289 uninstallSortController();
290
291 if (singleSelection) {
292
293 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
294
295 } else {
296
297
298 if (checkBoxSelection) {
299
300
301 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
302
303
304 addFixedColumn(CheckTableColumn.class);
305
306 } else {
307
308
309 getTable().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
310 }
311 }
312
313
314 if (!pendingFixedColumns.isEmpty()) {
315
316 initFixedTable();
317
318 pendingFixedColumns.forEach(pendingFixedColumn -> {
319
320
321 if (pendingFixedColumn instanceof HiddenColumn) {
322 HiddenColumn hiddenColumn = (HiddenColumn) pendingFixedColumn;
323 hiddenColumn.setVisible(false);
324 pendingFixedColumn = hiddenColumn.getDelegate();
325 }
326
327
328 getFixedTable().addColumn(pendingFixedColumn);
329
330
331 if (pendingFixedColumn.getIdentifier() instanceof ColumnIdentifier) {
332 ColumnIdentifier<R> identifier = (ColumnIdentifier<R>) pendingFixedColumn.getIdentifier();
333 if (getTableModel().getColumnIndex(identifier.getPropertyName()) == -1) {
334 getTableModel().getIdentifiers().add(pendingFixedColumn.getModelIndex(), identifier);
335 }
336 }
337
338 });
339
340
341 pendingFixedColumns.clear();
342 }
343
344
345 installSortController();
346
347
348 uninstallTableSelectionListener();
349
350
351 installTableSelectionListener();
352
353
354 addRowNumbersAction();
355
356
357 table.setColumnControl(new ColumnControlButton(table));
358 table.setColumnControlVisible(true);
359
360
361 table.setDefaultEditor(String.class, new StringCellEditor());
362
363
364 overrideTabKeyActions(table, false);
365
366 initializing = false;
367 initialized = true;
368 }
369
370 private void initHighlighters(SwingTable table) {
371
372
373 table.putClientProperty(JXTable.USE_DTCR_COLORMEMORY_HACK, Boolean.FALSE);
374
375
376 addHighlighters(table);
377
378 }
379
380
381
382
383
384
385
386 protected void overrideTabKeyActions(SwingTable table, boolean forceStopEditingBeforeAction) {
387
388 overrideKeyAction(table, "pressed TAB", AbstractCellSelectionAction.Direction.NEXT_CELL, forceStopEditingBeforeAction);
389 overrideKeyAction(table, "shift pressed TAB", AbstractCellSelectionAction.Direction.PREVIOUS_CELL, forceStopEditingBeforeAction);
390 overrideKeyAction(table, "pressed RIGHT", AbstractCellSelectionAction.Direction.NEXT_CELL, forceStopEditingBeforeAction);
391 overrideKeyAction(table, "pressed LEFT", AbstractCellSelectionAction.Direction.PREVIOUS_CELL, forceStopEditingBeforeAction);
392 overrideKeyAction(table, "pressed DOWN", AbstractCellSelectionAction.Direction.NEXT_ROW, forceStopEditingBeforeAction);
393 overrideKeyAction(table, "pressed ENTER", AbstractCellSelectionAction.Direction.NEXT_ROW, forceStopEditingBeforeAction);
394
395 }
396
397 private void overrideKeyAction(SwingTable table, String key, AbstractCellSelectionAction.Direction direction, boolean forceStopEditingBeforeAction) {
398
399 String actionName = "actionFor" + StringUtils.deleteWhitespace(StringUtils.capitalize(key));
400
401
402 AbstractCellSelectionAction action = null;
403 switch (direction) {
404
405 case NEXT_CELL:
406 action = new NextCellSelectionAction(actionName, this, forceStopEditingBeforeAction);
407 break;
408 case PREVIOUS_CELL:
409 action = new PreviousCellSelectionAction(actionName, this, forceStopEditingBeforeAction);
410 break;
411 case NEXT_ROW:
412 action = new NextRowSelectionAction(actionName, this, forceStopEditingBeforeAction);
413 break;
414 case PREVIOUS_ROW:
415 break;
416 }
417
418 if (action == null) {
419 return;
420 }
421
422
423 table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(key), actionName);
424
425
426 table.getActionMap().put(actionName, action);
427
428 }
429
430
431
432
433 protected void installTableSelectionListener() {
434
435 Assert.state(
436 tableSelectionListener == null,
437 "There is already a tableSelectionListener registered, remove it before invoking this method.");
438
439
440 tableSelectionListener = new ListSelectionListener() {
441
442 int lastMinIndex = -1;
443 int lastMaxIndex = -1;
444
445 @Override
446 public void valueChanged(ListSelectionEvent e) {
447
448 ListSelectionModel source = (ListSelectionModel) e.getSource();
449
450 if (LOG.isDebugEnabled()) {
451 LOG.debug("Selection changed: " + e);
452 }
453
454
455
456
457
458
459 int minIndex = source.getMinSelectionIndex();
460 int maxIndex = source.getMaxSelectionIndex();
461
462 if (source.isSelectionEmpty()) {
463 minIndex = -1;
464 maxIndex = -1;
465 }
466 if (minIndex == lastMinIndex && maxIndex == lastMaxIndex) {
467 return;
468 }
469 lastMinIndex = minIndex;
470 lastMaxIndex = maxIndex;
471
472
473 if (minIndex != maxIndex) {
474 getTable().editingStopped(null);
475 }
476
477
478
479 rowMonitor.setBean(null);
480
481
482 if (!hasCheckTableColumn()) {
483
484 getModel().getSelectedRows().forEach(row -> row.setSelected(false));
485
486
487 for (int index = minIndex; index <= maxIndex; index++) {
488 if (source.isSelectedIndex(index)) {
489 int modelIndex = getTable().convertRowIndexToModel(index);
490 R row = getTableModel().getEntry(modelIndex);
491 row.setSelected(true);
492 }
493 }
494 }
495
496 R newRow;
497
498
499 if (source.isSelectionEmpty() || source.getLeadSelectionIndex() < 0 || source.getLeadSelectionIndex() >= getTable().getRowCount()) {
500 newRow = null;
501 } else {
502 int rowIndex = source.getLeadSelectionIndex();
503 int modelIndex = getTable().convertRowIndexToModel(rowIndex);
504 newRow = getTableModel().getEntry(modelIndex);
505 if (LOG.isDebugEnabled()) {
506 LOG.debug("Will monitor entry: " + rowIndex);
507 }
508 }
509
510 rowMonitor.setBean(newRow);
511 getModel().setSingleSelectedRow(newRow);
512
513
514 if (newRow != null && source.getMinSelectionIndex() == source.getMaxSelectionIndex()) {
515 selectRow(newRow);
516 }
517
518
519 recalculateRowSelection(newRow);
520
521 }
522 };
523
524 if (LOG.isDebugEnabled()) {
525 LOG.debug("Install " + tableSelectionListener + " on tableModel " + getTableModel());
526 }
527
528 getTable().getSelectionModel().addListSelectionListener(tableSelectionListener);
529 }
530
531
532
533
534 protected void uninstallTableSelectionListener() {
535
536 if (tableSelectionListener != null) {
537
538 if (LOG.isDebugEnabled()) {
539 LOG.debug("Uninstall " + tableSelectionListener);
540 }
541
542
543 getTable().getSelectionModel().removeListSelectionListener(tableSelectionListener);
544 tableSelectionListener = null;
545 }
546 }
547
548
549
550
551 protected void installTableScrollSelectionListener() {
552 Assert.state(
553 tableScrollSelectionListener == null,
554 "There is already a tableScrollSelectionListener registered, remove it before invoking this method.");
555
556 tableScrollSelectionListener = e -> {
557 if (!e.getValueIsAdjusting()) {
558 int colIndex = getTable().getSelectedColumn();
559 int rowIndex = getTable().getSelectedRow();
560 if (rowIndex > -1 && colIndex > -1) {
561 getTable().scrollCellToVisible(rowIndex, colIndex);
562 }
563 }
564 };
565
566 getTable().getSelectionModel().addListSelectionListener(tableScrollSelectionListener);
567 getTable().getColumnModel().getSelectionModel().addListSelectionListener(tableScrollSelectionListener);
568 }
569
570
571
572
573 protected void uninstallTableScrollSelectionListener() {
574 if (tableScrollSelectionListener != null) {
575
576 if (LOG.isDebugEnabled()) {
577 LOG.debug("Uninstall " + tableScrollSelectionListener);
578 }
579
580
581 getTable().getSelectionModel().removeListSelectionListener(tableScrollSelectionListener);
582 getTable().getColumnModel().getSelectionModel().removeListSelectionListener(tableScrollSelectionListener);
583 tableScrollSelectionListener = null;
584 }
585 }
586
587
588 protected void installSaveTableStateListener() {
589 Assert.state(
590 columnModelListener == null,
591 "There is already a columnModelListener registered, remove it before invoking this method.");
592 Assert.state(
593 rowSorterListener == null,
594 "There is already a rowSorterListener registered, remove it before invoking this method.");
595
596 columnModelListener = new TableColumnModelListener() {
597 @Override
598 public void columnAdded(TableColumnModelEvent event) {
599 if (!getModel().isLoading()) {
600 if (LOG.isDebugEnabled()) LOG.debug(event);
601 saveTableState();
602 }
603 }
604
605 @Override
606 public void columnRemoved(TableColumnModelEvent event) {
607 if (!getModel().isLoading()) {
608 if (LOG.isDebugEnabled()) LOG.debug(event);
609 saveTableState();
610 }
611 }
612
613 @Override
614 public void columnMoved(TableColumnModelEvent event) {
615 if (!getModel().isLoading() && event.getFromIndex() != event.getToIndex()) {
616 if (LOG.isDebugEnabled()) LOG.debug(event);
617 saveTableState();
618 }
619 }
620
621 @Override
622 public void columnMarginChanged(ChangeEvent event) {
623 if (!getModel().isLoading()) {
624 if (LOG.isDebugEnabled()) LOG.debug(event);
625 saveTableState();
626 }
627 }
628
629 @Override
630 public void columnSelectionChanged(ListSelectionEvent event) {
631 }
632 };
633
634 getTable().getColumnModel().addColumnModelListener(columnModelListener);
635
636
637 if (getTable().getRowSorter() != null) {
638 rowSorterListener = event -> {
639 if (!getModel().isLoading() && event.getType().equals(RowSorterEvent.Type.SORT_ORDER_CHANGED)) {
640 if (LOG.isDebugEnabled()) LOG.debug(event);
641 saveTableState();
642 }
643 };
644
645 getTable().getRowSorter().addRowSorterListener(rowSorterListener);
646 }
647 }
648
649 protected void saveTableState() {
650 getContext().saveComponentInSwingSession(getTable(), getTableModel().getStateContext());
651 if (getFixedTable() != null) {
652 getContext().saveComponentInSwingSession(getFixedTable(), getTableModel().getStateContext());
653 }
654 }
655
656 protected void restoreTableState() {
657 getContext().restoreComponentFromSwingSession(getTable());
658 if (getFixedTable() != null) {
659 getContext().restoreComponentFromSwingSession(getFixedTable());
660 }
661 }
662
663 protected void uninstallSaveTableStateListener() {
664 if (columnModelListener != null) {
665 getTable().getColumnModel().removeColumnModelListener(columnModelListener);
666 columnModelListener = null;
667 }
668 if (rowSorterListener != null) {
669 if (getTable().getRowSorter() != null) {
670 getTable().getRowSorter().removeRowSorterListener(rowSorterListener);
671 }
672 rowSorterListener = null;
673 }
674 }
675
676 protected void copyParentTableState(SwingTable sourceTable) {
677
678 getTable().getTableModel().getIdentifiers();
679 getTableModel().getIdentifiers();
680 @SuppressWarnings({"unchecked", "rawtypes"}) List<ColumnIdentifier<R>> targetIdentifiers = new ArrayList<>(getTable().getTableModel().getIdentifiers());
681
682 Map<Integer, Integer> deferredMove = new HashMap<>();
683 int offset = 0;
684 for (ColumnIdentifier<?> targetIdentifier : targetIdentifiers) {
685
686 TableColumnExt sourceColumn = sourceTable.getColumnExt(targetIdentifier);
687 if (sourceColumn == null || sourceColumn instanceof FixedColumn) {
688
689 offset++;
690 continue;
691 }
692 boolean isHidden = sourceColumn instanceof HiddenColumn || !sourceColumn.isVisible();
693
694
695 TableColumnExt targetColumn = getTable().getColumnExt(targetIdentifier);
696 if (targetColumn == null) {
697
698 continue;
699 }
700
701 if (isHidden) {
702 targetColumn.setVisible(false);
703 } else {
704
705 int position = sourceTable.convertColumnIndexToView(sourceColumn.getModelIndex());
706 deferredMove.put(position + offset, targetColumn.getModelIndex());
707 offset = 0;
708 }
709
710 }
711
712
713 if (!deferredMove.isEmpty()) {
714
715 for (Integer newColumnViewIndex : deferredMove.keySet()) {
716 int columnViewIndex = getTable().convertColumnIndexToView(deferredMove.get(newColumnViewIndex));
717 if (columnViewIndex == -1)
718 continue;
719
720 if (newColumnViewIndex >= getTable().getColumnCount()) {
721 newColumnViewIndex = getTable().getColumnCount() - 1;
722 }
723
724
725 getTable().moveColumn(columnViewIndex, newColumnViewIndex);
726 }
727 }
728 }
729
730
731
732
733 @SuppressWarnings("unchecked")
734 protected void installSortController() {
735
736
737 uninstallSortController();
738
739 SwingTableSortController controller = new SwingTableSortController(getTableModel());
740
741 getTable().setRowSorter(controller);
742
743
744 DecoratorService decoratorService = ClientServiceLocator.instance().getDecoratorService();
745 getTable().getColumns(true).stream().map(TableColumnExt.class::cast).forEach(column -> {
746
747 if (!column.isSortable()) {
748
749 controller.setSortable(column.getModelIndex(), false);
750
751 } else {
752
753 ColumnIdentifier identifier = (ColumnIdentifier) column.getIdentifier();
754 Comparator comparator = null;
755
756
757 if (decoratorService != null) {
758 Decorator decorator = decoratorService.getDecoratorByType(identifier.getPropertyType(), identifier.getDecoratorName());
759 if (decorator != null) {
760
761 comparator = decorator.getCurrentComparator();
762
763
764 if (comparator instanceof DecoratorComparator && ((DecoratorComparator) comparator).getDecorator() == null) {
765 ((DecoratorComparator) comparator).setDecorator(decorator);
766 }
767 }
768 }
769
770
771 if (comparator == null) {
772 Class<?> columnClass = identifier.getPropertyType();
773 if (columnClass == String.class) {
774
775 comparator = AlphanumericComparator.instance();
776 } else if (Comparable.class.isAssignableFrom(columnClass)) {
777
778 comparator = ComparableComparator.comparableComparator();
779 }
780 }
781
782
783 controller.setComparator(column.getModelIndex(), comparator);
784 }
785 });
786
787 }
788
789
790
791
792 protected void uninstallSortController() {
793
794
795 getTable().setAutoCreateRowSorter(false);
796
797
798 getTable().setRowSorter(null);
799
800
801 if (getFixedTable() != null) {
802 getFixedTable().setAutoCreateRowSorter(false);
803 getFixedTable().setRowSorter(null);
804 }
805 }
806
807 protected SwingTableSortController getSortController() {
808 return (SwingTableSortController) getTable().getRowSorter();
809 }
810
811
812
813
814
815
816
817
818
819
820 protected void onModelRowsChanged(List<R> rows) {
821 if (LOG.isDebugEnabled()) {
822 LOG.debug("Will set " + (rows == null ? 0 : rows.size())
823 + " rows on model.");
824 }
825 cleanRowMonitor();
826 getTableModel().setRows(rows);
827 }
828
829
830
831
832
833
834 protected void onRowsAdded(List<R> addedRows) {
835 if (LOG.isDebugEnabled()) {
836 LOG.debug((addedRows == null ? 0 : addedRows.size())
837 + " rows added on model.");
838 }
839 }
840
841
842
843
844
845
846
847
848
849 protected void onRowModifyStateChanged(int rowIndex, R row, Boolean oldValue, Boolean newValue) {
850 if (LOG.isDebugEnabled()) {
851 LOG.debug("row [" + rowIndex + "] modify state changed from "
852 + oldValue + " to " + newValue);
853 }
854 }
855
856
857
858
859
860
861
862
863
864 protected void onRowValidStateChanged(int rowIndex, R row, Boolean oldValue, Boolean newValue) {
865 if (LOG.isDebugEnabled()) {
866 LOG.debug("row [" + rowIndex + "] valid state changed from "
867 + oldValue + " to " + newValue);
868 }
869 if (rowIndex > -1) {
870 getTableModel().fireTableRowsUpdated(rowIndex, rowIndex);
871 }
872 }
873
874
875
876
877
878
879
880
881
882 protected void onRowSelectedStateChanged(int rowIndex, R row, Boolean oldValue, Boolean newValue) {
883 if (LOG.isDebugEnabled()) {
884 LOG.debug("row [" + rowIndex + "] selected state changed from "
885 + oldValue + " to " + newValue);
886 }
887 recalculateRowSelection(row);
888 }
889
890
891
892
893
894
895
896
897
898
899
900
901 protected void onRowModified(int rowIndex, R row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
902 getModel().setModify(true);
903 getTable().repaint();
904 if (getFixedTable() != null) {
905 getFixedTable().repaint();
906 }
907 recomputeRowValidState(row);
908 }
909
910
911
912
913
914
915
916
917
918
919
920
921 protected boolean isRowValid(R row) {
922
923
924 getTableModel().getMandatoryIdentifiers().forEach(identifier -> {
925 Object value = identifier.getValue(row);
926 if (value == null || (value instanceof String && StringUtils.isBlank((String) value))) {
927 row.addInvalidMandatoryProperty(identifier);
928 } else {
929 row.removeInvalidMandatoryProperty(identifier);
930 }
931 });
932
933
934 return row.isMandatoryValid();
935 }
936
937
938
939
940
941
942 public final void recomputeRowValidState(R row) {
943
944
945 boolean valid = isRowValid(row);
946
947
948 row.setValid(valid);
949
950 if (valid) {
951 getModel().removeRowInError(row);
952 } else {
953 getModel().addRowInError(row);
954 }
955
956 }
957
958
959
960
961 public void recomputeRowsValidState() {
962 recomputeRowsValidState(true);
963 }
964
965
966
967
968
969
970 public void recomputeRowsValidState(boolean validIfNoRow) {
971 List<R> rows = getModel().getRows();
972 if (LOG.isDebugEnabled()) {
973 LOG.debug("Will valid " + (rows == null ? 0 : rows.size())
974 + " rows on model.");
975 }
976
977 if (CollectionUtils.isNotEmpty(rows)) {
978
979
980 Set<R> invalidRows = Sets.newHashSet();
981 rows.forEach(row -> {
982
983 row.setValid(isRowValid(row));
984
985 if (!row.isValid()) {
986 invalidRows.add(row);
987 }
988 });
989
990
991 getModel().setRowsInError(invalidRows);
992 } else {
993 getModel().setValid(validIfNoRow);
994 }
995
996 }
997
998
999
1000
1001
1002 private void recalculateRowSelection(R currentRow) {
1003
1004
1005 getModel().populateSelectedRows();
1006
1007
1008 if (currentRow == null) {
1009 return;
1010 }
1011
1012
1013 findColumnByPredicate(column -> column instanceof CheckTableColumn).ifPresent(column -> {
1014 CheckTableColumn checkTableColumn = (CheckTableColumn) column;
1015 boolean all = true;
1016 Boolean currentSelected = currentRow.isSelected();
1017 for (R row : getTableModel().getRows()) {
1018 if (!currentSelected.equals(row.isSelected())) {
1019 all = false;
1020 break;
1021 }
1022 }
1023 checkTableColumn.setAllEnabled(all);
1024 checkTableColumn.setAllSelected(!all || currentSelected);
1025
1026
1027 JTableHeader h = checkTableColumn.getTable().getTableHeader();
1028 h.repaint(h.getHeaderRect(checkTableColumn.getTable().convertColumnIndexToView(checkTableColumn.getModelIndex())));
1029 });
1030 }
1031
1032
1033
1034
1035 public void selectAllValidRows() {
1036 if (getModel().getRowCount() > 0) {
1037 getModel().getRows().stream().filter(R::isValid).forEach(row -> row.selected = true);
1038 recalculateRowSelection(getModel().getRows().get(0));
1039 }
1040 }
1041
1042
1043
1044
1045 public void selectAllEditableRows() {
1046 if (getModel().getRowCount() > 0) {
1047 getModel().getRows().stream().filter(row -> !row.isCalculated() && row.isEditable()).forEach(row -> row.selected = true);
1048 recalculateRowSelection(getModel().getRows().get(0));
1049 }
1050 }
1051
1052
1053
1054
1055 public void unselectAllRows() {
1056 getTable().clearSelection();
1057 }
1058
1059
1060
1061
1062
1063
1064 public void selectRow(R row) {
1065 int rowViewIndex = getRowViewIndex(row);
1066 if (rowViewIndex == -1) {
1067 return;
1068 }
1069 if (getTable().getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) {
1070 row.setSelected(true);
1071 }
1072 getTable().selectCell(rowViewIndex, null);
1073 }
1074
1075
1076
1077
1078
1079
1080 public void selectRows(List<R> rows) {
1081
1082 rows.forEach(row -> {
1083 int rowViewIndex = getRowViewIndex(row);
1084 if (rowViewIndex != -1) {
1085 row.setSelected(true);
1086 getTable().addRowSelectionInterval(rowViewIndex, rowViewIndex);
1087
1088 if (rows.indexOf(row) == rows.size() - 1) {
1089 getTable().scrollCellToVisible(rowViewIndex, 0);
1090 recalculateRowSelection(row);
1091 }
1092 }
1093 });
1094
1095 }
1096
1097
1098
1099
1100
1101
1102 public void setFocusOnCell(final R row) {
1103
1104
1105 getModel().setSingleSelectedRow(row);
1106 recomputeRowValidState(row);
1107
1108 SwingUtilities.invokeLater(() -> {
1109
1110
1111 int rowViewIndex = getRowViewIndex(row);
1112 if (rowViewIndex == -1) {
1113 return;
1114 }
1115 if (!row.isEditable()) {
1116 getTable().selectCell(rowViewIndex, null);
1117 return;
1118 }
1119
1120
1121 final ColumnIdentifier<?> columnIdentifier = getTableModel().getFirstColumnEditing();
1122 if (columnIdentifier != null) {
1123
1124
1125 SwingTable table = getTable();
1126 TableColumnExt column = table.getColumnExt(columnIdentifier);
1127 if (column instanceof HiddenColumn) {
1128
1129 table = getFixedTable();
1130 }
1131
1132 int columnViewIndex = table.convertColumnIndexToView(column.getModelIndex());
1133 if (columnViewIndex == -1) {
1134
1135 table.selectCell(rowViewIndex, null);
1136 return;
1137 }
1138
1139 int columnCount = table.getColumnCount(false);
1140
1141 while (columnViewIndex < columnCount && !table.isCellEditable(rowViewIndex, columnViewIndex)) {
1142 columnViewIndex++;
1143 }
1144
1145
1146 if (columnViewIndex >= columnCount) {
1147 table.selectCell(rowViewIndex, null);
1148 return;
1149 }
1150
1151
1152 table.selectCell(rowViewIndex, columnViewIndex);
1153
1154
1155 table.editCellAt(rowViewIndex, columnViewIndex);
1156 }
1157 });
1158 }
1159
1160
1161
1162
1163
1164
1165
1166
1167 @Deprecated
1168 public void selectCell(Integer rowIndex, Integer colIndex) {
1169 getTable().selectCell(rowIndex, colIndex);
1170 }
1171
1172
1173
1174
1175
1176
1177
1178 private int getRowViewIndex(R row) {
1179 if (row == null) {
1180 return -1;
1181 }
1182 int rowIndex = getTableModel().getRowIndex(row);
1183 if (rowIndex == -1) {
1184 return -1;
1185 }
1186 return getTable().convertRowIndexToView(rowIndex);
1187 }
1188
1189
1190
1191
1192
1193
1194
1195 @Override
1196 public void autoSelectRowInTable(MouseEvent e, JPopupMenu popup) {
1197 boolean rightClick = SwingUtilities.isRightMouseButton(e);
1198
1199 if (e.getSource() instanceof JXTable && (rightClick || SwingUtilities.isLeftMouseButton(e))) {
1200
1201
1202 Point p = e.getPoint();
1203
1204 JXTable source = (JXTable) e.getSource();
1205
1206 int[] selectedRows = source.getSelectedRows();
1207 int[] selectedColumns = source.getSelectedColumns();
1208
1209
1210 int rowIndex = source.rowAtPoint(p);
1211
1212
1213 int columnIndex = source.columnAtPoint(p);
1214
1215 if (LOG.isDebugEnabled()) {
1216 LOG.debug("At point [" + p + "] found Row " + rowIndex + ", Column " + columnIndex);
1217 }
1218
1219 boolean canContinue = true;
1220
1221 if (getTable().isEditing()) {
1222
1223
1224 boolean stopEdit = getTable().getCellEditor().stopCellEditing();
1225 if (!stopEdit) {
1226 if (LOG.isWarnEnabled()) {
1227 LOG.warn("Could not stop edit cell on main table...");
1228 }
1229 canContinue = false;
1230 }
1231 }
1232
1233 if (getFixedTable() != null && getFixedTable().isEditing()) {
1234
1235
1236 boolean stopEdit = getFixedTable().getCellEditor().stopCellEditing();
1237 if (!stopEdit) {
1238 if (LOG.isWarnEnabled()) {
1239 LOG.warn("Could not stop edit cell on fixed table...");
1240 }
1241 canContinue = false;
1242 }
1243 }
1244
1245 if (canContinue) {
1246
1247
1248 if (rowIndex == -1) {
1249 source.clearSelection();
1250 } else if (!ArrayUtils.contains(selectedRows, rowIndex)) {
1251 source.setRowSelectionInterval(rowIndex, rowIndex);
1252 }
1253
1254
1255 if (columnIndex == -1) {
1256 source.clearSelection();
1257 } else if (!ArrayUtils.contains(selectedColumns, columnIndex)) {
1258 source.setColumnSelectionInterval(columnIndex, columnIndex);
1259 }
1260
1261 if (rightClick) {
1262
1263
1264 int modelRowIndex = rowIndex == -1 ? -1 : source.convertRowIndexToModel(rowIndex);
1265 int modelColumnIndex = columnIndex == -1 ? -1 : source.convertColumnIndexToModel(columnIndex);
1266
1267 beforeOpenPopup(modelRowIndex, modelColumnIndex);
1268
1269
1270 popup.show(source, e.getX(), e.getY());
1271 }
1272 }
1273 }
1274
1275 }
1276
1277
1278
1279
1280
1281
1282
1283
1284 @Override
1285 @Deprecated
1286 protected <O> TableColumnExt addColumnToModel(
1287 TableColumnModel model,
1288 org.nuiton.jaxx.application.swing.table.ColumnIdentifier<O> identifier) {
1289
1290 return addColumnToModel(model, null, null, identifier);
1291 }
1292
1293
1294
1295
1296 @Override
1297 @Deprecated
1298 protected <O> TableColumnExt addColumnToModel(
1299 TableColumnModel model,
1300 TableCellEditor editor,
1301 TableCellRenderer renderer,
1302 org.nuiton.jaxx.application.swing.table.ColumnIdentifier<O> identifier) {
1303
1304 if (!(model instanceof SwingTableColumnModel)) {
1305 LOG.error("Wrong model type : " + model.getClass().getName());
1306 return null;
1307 }
1308
1309 if (!(identifier instanceof ColumnIdentifier)) {
1310 LOG.error("Wrong identifier type : " + identifier.getClass().getName());
1311 return null;
1312 }
1313
1314 return addColumn(editor, renderer, (ColumnIdentifier<R>) identifier);
1315 }
1316
1317 protected TableColumnExt addBooleanColumnToModel(ColumnIdentifier<R> identifier, JTable table) {
1318 return super.addBooleanColumnToModel(getTable().getColumnModel(), identifier, table);
1319 }
1320
1321 protected TableColumnExt addColumn(ColumnIdentifier<R> identifier) {
1322 return addColumn(null, null, identifier);
1323 }
1324
1325 protected TableColumnExt addColumn(TableCellEditor editor, TableCellRenderer renderer, ColumnIdentifier<R> identifier) {
1326 return addColumn(editor, renderer, identifier, false);
1327 }
1328
1329 private TableColumnExt addColumn(TableCellEditor editor, TableCellRenderer renderer, ColumnIdentifier<R> identifier, boolean hidden) {
1330
1331 TableColumnExt column = hidden ? createHiddenColumn(getTable()) : createColumn(getTable());
1332
1333 column.setIdentifier(identifier);
1334 setEditorAndRenderer(column, editor, renderer, identifier);
1335 return column;
1336 }
1337
1338 public TableColumnExt addFixedColumn(ColumnIdentifier<R> identifier) {
1339 return addFixedColumn(null, null, identifier);
1340 }
1341
1342 public TableColumnExt addFixedColumn(TableCellEditor editor, TableCellRenderer renderer, ColumnIdentifier<R> identifier) {
1343
1344 Assert.isTrue(!initialized, "Add a new fixed column can be made before table initialization");
1345
1346
1347 TableColumnExt column = addColumn(editor, renderer, identifier, true);
1348
1349
1350 pendingFixedColumns.add(column);
1351
1352 return column;
1353 }
1354
1355 private TableColumnExt createColumn(SwingTable table) {
1356 TableColumnExt column = new TableColumnExt(table.getColumnCount(true));
1357 table.getColumnModel().addColumn(column);
1358 return column;
1359 }
1360
1361 private TableColumnExt createHiddenColumn(SwingTable table) {
1362 HiddenColumn column = new HiddenColumn(new TableColumnExt(table.getColumnCount(true)));
1363 table.getColumnModel().addColumn(column);
1364 return column;
1365 }
1366
1367 protected void setEditorAndRenderer(TableColumnExt column, TableCellEditor editor, TableCellRenderer renderer, ColumnIdentifier<R> identifier) {
1368
1369
1370 if (renderer == null) {
1371 Decorator<?> decorator = getDecorator(identifier.getPropertyType(), identifier.getDecoratorName());
1372 if (decorator != null) {
1373 renderer = newTableCellRender(decorator);
1374 } else {
1375 renderer = (table, value, isSelected, hasFocus, row, column1) -> {
1376 TableCellRenderer defaultRenderer = table.getDefaultRenderer(identifier.getPropertyType());
1377 JComponent component = (JComponent) defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column1);
1378
1379
1380 if (value != null) {
1381 component.setToolTipText(value.toString());
1382 }
1383
1384 return component;
1385 };
1386 }
1387 }
1388
1389 column.setCellEditor(editor);
1390 column.setCellRenderer(renderer);
1391
1392
1393 if (identifier.isMandatory()) {
1394 column.setHeaderValue(t("quadrige3.table.mandatoryColumn.header", t(identifier.getHeaderI18nKey())));
1395 column.setToolTipText(t("quadrige3.table.mandatoryColumn.tip", t(identifier.getHeaderTipI18nKey())));
1396
1397 column.setHideable(false);
1398 } else {
1399
1400 column.setHeaderValue(ApplicationUIUtil.getHtmlString(t(identifier.getHeaderI18nKey())));
1401 column.setToolTipText(ApplicationUIUtil.getHtmlString(t(identifier.getHeaderTipI18nKey())));
1402 }
1403
1404 column.setSortable(false);
1405 }
1406
1407 protected void removeColumns(Collection<? extends TableColumn> columns) {
1408 if (CollectionUtils.isNotEmpty(columns)) {
1409 columns.stream().sorted((o1, o2) -> Integer.compare(o2.getModelIndex(), o1.getModelIndex())).forEach(this::removeColumn);
1410 }
1411 }
1412
1413 @SuppressWarnings("unchecked")
1414 protected void removeColumn(TableColumn column) {
1415 ColumnIdentifier<R> identifier = (ColumnIdentifier<R>) column.getIdentifier();
1416 removeColumnIdentifier(identifier);
1417 getTable().getColumnModel().removeColumn(column);
1418 }
1419
1420
1421
1422
1423
1424
1425 private void removeColumnIdentifier(ColumnIdentifier<R> identifier) {
1426 int index = getTableModel().getIdentifiers().indexOf(identifier);
1427 if (index == -1)
1428 return;
1429
1430 for (int c = index + 1; c < getTable().getColumnModel().getColumnCount(true); c++) {
1431 TableColumnExt column = (TableColumnExt) getTable().getColumnModel().getColumns(true).get(c);
1432 Assert.isTrue(column.getModelIndex() > index, "this model index should be > then the removed identifier index");
1433 column.setModelIndex(column.getModelIndex() - 1);
1434 }
1435
1436 getTableModel().getIdentifiers().remove(identifier);
1437 }
1438
1439 @SuppressWarnings("unchecked")
1440 public <C extends TableColumnExt> C addFixedColumn(Class<C> columnClass) {
1441
1442 Assert.isAssignable(FixedColumn.class, columnClass, "Only column class implementing FixedColumn can be add to the fixed table");
1443
1444 initFixedTable();
1445
1446 try {
1447
1448 C column = BeanUtils.instantiateClass(columnClass.getConstructor(SwingTable.class), getFixedTable());
1449 getFixedTable().getColumnModel().addColumn(column);
1450
1451
1452 if (column.getIdentifier() instanceof ColumnIdentifier) {
1453 ColumnIdentifier<R> identifier = (ColumnIdentifier<R>) column.getIdentifier();
1454
1455
1456 TableColumnExt hiddenColumn = createHiddenColumn(getTable());
1457 hiddenColumn.setIdentifier(identifier);
1458
1459 if (getTableModel().getColumnIndex(identifier.getPropertyName()) == -1) {
1460 getTableModel().getIdentifiers().add(identifier);
1461 }
1462
1463 getFixedTable().addColumnModelTransposition(column.getModelIndex(), hiddenColumn.getModelIndex());
1464
1465
1466 hiddenColumn.setSortable(false);
1467
1468
1469 hiddenColumn.setVisible(false);
1470 }
1471
1472 return column;
1473 } catch (NoSuchMethodException e) {
1474 throw new QuadrigeTechnicalException(e);
1475 }
1476
1477 }
1478
1479 @SuppressWarnings("unchecked")
1480 public void removeFixedColumn(TableColumn fixedColumn) {
1481 Assert.notNull(getFixedTable(), "Can not remove fixed column from absent fixed table");
1482 Assert.isInstanceOf(ColumnIdentifier.class, fixedColumn.getIdentifier());
1483
1484 ColumnIdentifier<R> identifier = (ColumnIdentifier<R>) fixedColumn.getIdentifier();
1485 removeColumnIdentifier(identifier);
1486
1487 getFixedTable().removeColumnModelTransposition(fixedColumn.getModelIndex());
1488
1489
1490 findColumnByPredicate(tableColumn -> tableColumn instanceof HiddenColumn
1491 && ((HiddenColumn) tableColumn).getIdentifier().getPropertyName().equals(identifier.getPropertyName()))
1492 .ifPresent(tableColumn -> getTable().removeColumn(tableColumn));
1493
1494 getFixedTable().removeColumn(fixedColumn);
1495 if (getFixedTable().getColumnCount() == 0)
1496 removeFixedTable();
1497 }
1498
1499 protected void initFixedTable() {
1500 if (fixedTable != null)
1501 return;
1502
1503 JScrollPane scrollPane = getParentScrollPane();
1504 fixedTable = new FixedSwingTable(getTable());
1505
1506
1507 initHighlighters(fixedTable);
1508
1509
1510 fixedTable.setDefaultEditor(String.class, new StringCellEditor());
1511
1512
1513 overrideTabKeyActions(fixedTable, false);
1514
1515
1516 scrollPane.setRowHeaderView(fixedTable);
1517 scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, fixedTable.getTableHeader());
1518
1519 }
1520
1521 protected void removeFixedTable() {
1522 if (fixedTable == null)
1523 return;
1524
1525 JScrollPane scrollPane = getParentScrollPane();
1526 scrollPane.setRowHeaderView(null);
1527 scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, null);
1528 fixedTable = null;
1529 }
1530
1531 public boolean hasRowNumberColumn() {
1532 return hasFixedColumn(tableColumn -> tableColumn instanceof RowNumberColumn);
1533 }
1534
1535 public boolean hasCheckTableColumn() {
1536 return hasFixedColumn(tableColumn -> tableColumn instanceof CheckTableColumn);
1537 }
1538
1539 public boolean hasFixedColumn(Predicate<TableColumn> predicate) {
1540 return getTable().getColumnModel().hasFixedColumn(predicate)
1541 || (getFixedTable() != null && getFixedTable().getColumnModel().hasFixedColumn(predicate));
1542 }
1543
1544 public Optional<TableColumn> findColumnByPredicate(Predicate<TableColumn> predicate) {
1545 Optional<TableColumn> column = getTable().getColumns(true).stream().filter(predicate).findFirst();
1546 if (!column.isPresent() && getFixedTable() != null) {
1547 column = getFixedTable().getColumns(true).stream().filter(predicate).findFirst();
1548 }
1549 return column;
1550 }
1551
1552 protected <B> TableColumnExt addSimpleComboDataColumnToModel(ColumnIdentifier<R> identifier, List<B> data) {
1553 Decorator<?> decorator = getDecorator(identifier.getPropertyType(), identifier.getDecoratorName());
1554
1555 JComboBox<B> comboBox = new JComboBox<>();
1556 comboBox.setRenderer(this.newListCellRender(decorator));
1557 List<B> dataToList = Lists.newArrayList(data);
1558 if (!dataToList.isEmpty() && dataToList.get(0) != null) {
1559 dataToList.add(0, null);
1560 }
1561 SwingUtil.fillComboBox(comboBox, dataToList, null);
1562 ObjectToStringConverter converter = BeanUIUtil.newDecoratedObjectToStringConverter(decorator);
1563 BeanUIUtil.decorate(comboBox, converter);
1564 return addColumn(new ComboBoxCellEditor(comboBox), this.newTableCellRender(decorator), identifier);
1565 }
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575 protected <B> TableColumnExt addFilterableComboDataColumnToModel(ColumnIdentifier<R> identifier,
1576 List<B> data, boolean resettable) {
1577 @SuppressWarnings("unchecked")
1578 Class<B> beanType = (Class<B>) identifier.getPropertyType();
1579 String decoratorName = identifier.getDecoratorName();
1580 FilterableComboBoxCellEditor<B> editor = newFilterableComboBoxCellEditor(data, beanType, decoratorName, resettable);
1581 return addColumn(editor, newTableCellRender(beanType, decoratorName), identifier);
1582 }
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593 protected <B> TableColumnExt addExtendedComboDataColumnToModel(ColumnIdentifier<R> identifier,
1594 List<B> data, boolean resettable) {
1595 @SuppressWarnings("unchecked")
1596 Class<B> beanType = (Class<B>) identifier.getPropertyType();
1597 String decoratorName = identifier.getDecoratorName();
1598 FilterableComboBoxCellEditor<B> editor = newExtendedComboBoxCellEditor(data, beanType, decoratorName, resettable);
1599 return addColumn(editor, newTableCellRender(beanType, decoratorName), identifier);
1600 }
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611 protected <B> FilterableComboBoxCellEditor<B> newFilterableComboBoxCellEditor(List<B> data, Class<B> beanType,
1612 boolean resettable) {
1613 return newFilterableComboBoxCellEditor(data, beanType, null, resettable);
1614 }
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626 protected <B> FilterableComboBoxCellEditor<B> newFilterableComboBoxCellEditor(List<B> data, Class<B> beanType,
1627 String decoratorName, boolean resettable) {
1628
1629 BeanFilterableComboBox<B> comboBox = new BeanFilterableComboBox<>(getUI());
1630 preInitBeanFilterableComboBox(comboBox, beanType, resettable);
1631 initBeanFilterableComboBox(comboBox, data, null, decoratorName);
1632 return new FilterableComboBoxCellEditor<>(comboBox);
1633 }
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644 protected <B> ExtendedComboBoxCellEditor<B> newExtendedComboBoxCellEditor(List<B> data, Class<B> beanType, boolean resettable) {
1645 return newExtendedComboBoxCellEditor(data, beanType, null, resettable);
1646 }
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657 @SuppressWarnings("unchecked")
1658 protected <B> ExtendedComboBoxCellEditor<B> newExtendedComboBoxCellEditor(List<B> data, ColumnIdentifier identifier, boolean resettable) {
1659 Class<B> beanType = identifier.getPropertyType();
1660 return newExtendedComboBoxCellEditor(data, beanType, identifier.getDecoratorName(), resettable);
1661 }
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673 protected <B> ExtendedComboBoxCellEditor<B> newExtendedComboBoxCellEditor(List<B> data, Class<B> beanType,
1674 String decoratorName, boolean resettable) {
1675
1676 ExtendedComboBox<B> comboBox = new ExtendedComboBox<>(getUI());
1677 preInitBeanFilterableComboBox(comboBox, beanType, resettable);
1678 initBeanFilterableComboBox(comboBox, data, null, decoratorName);
1679 return new ExtendedComboBoxCellEditor<>(comboBox);
1680 }
1681
1682 private <B> void preInitBeanFilterableComboBox(BeanFilterableComboBox<B> comboBox, Class<B> beanType, boolean resettable) {
1683 comboBox.setFilterable(true);
1684 comboBox.setShowReset(resettable);
1685 comboBox.setShowDecorator(false);
1686 comboBox.setBeanType(beanType);
1687 comboBox.setAutoFocus(false);
1688 }
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698 protected TableColumnExt addCoordinateColumnToModel(
1699 CoordinateEditor.CoordinateType coordinateType,
1700 ColumnIdentifier<R> identifier) {
1701
1702 TableCellEditor editor = new CoordinateCellEditor(coordinateType);
1703 TableCellRenderer renderer = newNumberCellRenderer(6);
1704 return addColumn(editor, renderer, identifier);
1705 }
1706
1707 protected TableColumnExt addCommentColumn(ColumnIdentifier<R> identifier) {
1708 return addCommentColumn(identifier, true);
1709 }
1710
1711 protected TableColumnExt addCommentColumn(ColumnIdentifier<R> identifier, boolean editable) {
1712 return addCommentColumn(identifier, null, editable);
1713 }
1714
1715 protected TableColumnExt addCommentColumn(ColumnIdentifier<R> identifier, String propertyName, boolean editable) {
1716 TableColumnExt column = addColumn(
1717 CommentCellEditor.newEditor(getContext().getMainUI(), propertyName, identifier.getHeaderI18nKey(), editable),
1718 CommentCellRenderer.newRenderer(),
1719 identifier);
1720 column.setSortable(false);
1721 fixColumnWidth(column, 112);
1722 return column;
1723 }
1724
1725
1726
1727
1728
1729
1730
1731 protected TableColumnExt addDatePickerColumnToModel(ColumnIdentifier<R> identifier, String dateFormat) {
1732 return addDatePickerColumnToModel(identifier, dateFormat, true);
1733 }
1734
1735 protected TableColumnExt addLocalDatePickerColumnToModel(ColumnIdentifier<R> identifier, String dateFormat) {
1736 return addLocalDatePickerColumnToModel(identifier, dateFormat, true);
1737 }
1738
1739
1740
1741
1742
1743
1744
1745
1746 protected TableColumnExt addDatePickerColumnToModel(ColumnIdentifier<R> identifier, String dateFormat, boolean isEditable) {
1747 return addColumn(
1748 isEditable ? newDateCellEditor(dateFormat) : null,
1749 newDateCellRenderer(dateFormat),
1750 identifier);
1751 }
1752
1753 protected TableColumnExt addLocalDatePickerColumnToModel(ColumnIdentifier<R> identifier, String dateFormat, boolean isEditable) {
1754 return addColumn(
1755 isEditable ? newLocalDateCellEditor(dateFormat) : null,
1756 newLocalDateCellRenderer(dateFormat),
1757 identifier);
1758 }
1759
1760
1761
1762
1763
1764
1765
1766 protected TableCellEditor newDateCellEditor(String pattern) {
1767 return new DatePickerCellEditor(pattern);
1768 }
1769
1770 protected TableCellEditor newLocalDateCellEditor(String pattern) {
1771 return new LocalDatePickerCellEditor(pattern);
1772 }
1773
1774
1775
1776
1777
1778
1779
1780 protected TableCellRenderer newDateCellRenderer(String pattern) {
1781 return new DateCellRenderer(getTable().getDefaultRenderer(Date.class), pattern);
1782 }
1783
1784 protected TableCellRenderer newLocalDateCellRenderer(String pattern) {
1785 return new LocalDateCellRenderer(getTable().getDefaultRenderer(Date.class), pattern);
1786 }
1787
1788
1789
1790
1791
1792
1793 public TableCellRenderer newBigDecimalRenderer() {
1794 DefaultTableRenderer defaultRenderer = new DefaultTableRenderer((StringValue) value -> {
1795
1796
1797 if (value instanceof BigDecimal) {
1798 BigDecimal bigDecimal = (BigDecimal) value;
1799 return bigDecimal.toString();
1800 }
1801
1802 return "";
1803 }, JLabel.RIGHT);
1804 return new NumberCellRenderer(defaultRenderer);
1805 }
1806
1807
1808
1809
1810
1811
1812
1813 public TableCellRenderer newNumberCellRenderer(final int nbDecimal) {
1814 DefaultTableRenderer defaultRenderer = new DefaultTableRenderer((StringValue) value -> {
1815 if (value == null) {
1816 return "";
1817 }
1818
1819 int minDecimal = (value instanceof Integer) ? 0 : 1;
1820 int maxDecimal = (value instanceof Integer) ? 0 : nbDecimal;
1821
1822 DecimalFormat decimalFormat = ApplicationUIUtil.getDecimalFormat(minDecimal, maxDecimal);
1823 return decimalFormat.format(value);
1824 }, JLabel.RIGHT);
1825 return new NumberCellRenderer(defaultRenderer);
1826 }
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839 public <N extends Number> TableCellEditor newNumberCellEditor(Class<N> numberClass, boolean useSign, String numberPattern, N minValue, N maxValue) {
1840 return new NumberCellEditor<>(numberClass, useSign, numberPattern, minValue, maxValue);
1841 }
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852 public <N extends Number> TableCellEditor newNumberCellEditor(Class<N> numberClass, boolean useSign, String numberPattern) {
1853 return newNumberCellEditor(numberClass, useSign, numberPattern, null, null);
1854 }
1855
1856
1857
1858
1859
1860
1861
1862 protected TableCellRenderer newTableCellRender(ColumnIdentifier<?> identifier) {
1863 return newTableCellRender(identifier.getPropertyType(), identifier.getDecoratorName());
1864 }
1865
1866
1867
1868
1869 @Override
1870 protected <O> TableCellRenderer newTableCellRender(Class<O> type, String name) {
1871 Decorator<O> decorator = getDecorator(type, name);
1872 return newTableCellRender(decorator, null);
1873 }
1874
1875 protected <O> TableCellRenderer newTableCellRender(Class<O> type, String context, String tooltipContext) {
1876 Decorator<O> decorator = getDecorator(type, context);
1877 Decorator<O> tooltipDecorator = getDecorator(type, tooltipContext);
1878 return newTableCellRender(decorator, tooltipDecorator);
1879 }
1880
1881 @Override
1882 @Deprecated
1883 protected <O> TableCellRenderer newTableCellRender(org.nuiton.decorator.Decorator<O> decorator) {
1884 if (decorator instanceof Decorator) {
1885 return newTableCellRender((Decorator<O>) decorator);
1886 }
1887 LOG.warn("use of deprecated method : fr.ifremer.quadrige3.ui.swing.common.table.AbstractTableUIHandler.newTableCellRender(org.nuiton.decorator.Decorator<O>)");
1888 return super.newTableCellRender(decorator);
1889 }
1890
1891 protected <O> TableCellRenderer newTableCellRender(Decorator<O> decorator) {
1892 return newTableCellRender(decorator, null);
1893 }
1894
1895 protected TableCellRenderer newTableCellRender(Decorator decorator, Decorator tooltipDecorator) {
1896 return new ExtendedDecoratorCellRenderer(decorator, tooltipDecorator);
1897 }
1898
1899
1900
1901
1902 @Override
1903 protected <O> TableCellRenderer newTableCellRender(Class<O> type) {
1904 return this.newTableCellRender(type, null);
1905 }
1906
1907
1908
1909
1910
1911
1912
1913
1914 protected void cleanRowMonitor() {
1915 rowMonitor.clearModified();
1916 }
1917
1918
1919
1920
1921 public void stopCellEditing() {
1922
1923 if (getTable().isEditing()) {
1924 getTable().getCellEditor().stopCellEditing();
1925 }
1926 if (getFixedTable() != null && getFixedTable().isEditing()) {
1927 getFixedTable().getCellEditor().stopCellEditing();
1928 }
1929 }
1930
1931
1932
1933
1934
1935
1936 protected String[] getRowPropertiesToIgnore() {
1937 return ArrayUtils.EMPTY_STRING_ARRAY;
1938 }
1939
1940
1941
1942
1943
1944
1945
1946 protected void fixColumnWidth(TableColumn column, int width) {
1947 column.setMinWidth(width);
1948 column.setMaxWidth(width);
1949 }
1950
1951 protected void fixDefaultColumnWidth(TableColumn column) {
1952 fixColumnWidth(column, DEFAULT_MIN_COLUMN_WIDTH);
1953 }
1954
1955 protected void setDefaultColumnMinWidth(TableColumn column) {
1956 column.setMinWidth(DEFAULT_MIN_COLUMN_WIDTH);
1957 }
1958
1959 protected void forceColumnVisibleAtLastPosition(ColumnIdentifier identifier, boolean visible) {
1960 forceColumnVisible(identifier, visible);
1961 if (visible) moveColumnAtLastPosition(identifier);
1962 }
1963
1964 protected void forceColumnVisible(ColumnIdentifier identifier, boolean visible) {
1965 getTable().getColumns(true).stream()
1966 .filter(column -> column.getIdentifier() == identifier && column instanceof TableColumnExt).findFirst()
1967 .ifPresent(column -> {
1968 TableColumnExt columnExt = (TableColumnExt) column;
1969 columnExt.setHideable(!visible);
1970 columnExt.setVisible(visible);
1971 });
1972 }
1973
1974
1975
1976
1977
1978
1979 protected void moveColumnAtLastPosition(ColumnIdentifier identifier) {
1980 TableColumnExt columnExt = getTable().getColumnExt(identifier);
1981 if (columnExt != null) {
1982 getTable().moveColumn(getTable().convertColumnIndexToView(columnExt.getModelIndex()), getTable().getColumnCount() - 1);
1983 }
1984 }
1985
1986
1987
1988
1989
1990
1991
1992 protected void moveColumnAfter(ColumnIdentifier identifier, ColumnIdentifier anchor) {
1993 TableColumnExt columnToMove = getTable().getColumnExt(identifier);
1994 TableColumnExt anchorColumn = getTable().getColumnExt(anchor);
1995 if (columnToMove != null && columnToMove.isVisible() && anchorColumn != null && anchorColumn.isVisible()) {
1996 int columnToMoveIndex = getTable().convertColumnIndexToView(columnToMove.getModelIndex());
1997 int anchorColumnIndex = getTable().convertColumnIndexToView(anchorColumn.getModelIndex());
1998 int targetIndex = Math.min(anchorColumnIndex + (columnToMoveIndex > anchorColumnIndex ? 1 : 0), getTable().getColumnCount() - 1);
1999 getTable().moveColumn(columnToMoveIndex, targetIndex);
2000 }
2001 }
2002
2003
2004
2005
2006 public void addRowNumbersAction() {
2007 if (getAdditionalTableActions().getActions().stream().noneMatch(action -> action instanceof RowNumberColumnAction)) {
2008 RowNumberColumnAction action = new RowNumberColumnAction(this);
2009 action.putValue(AdditionalTableActions.ACTION_TARGET_GROUP, 0);
2010 getAdditionalTableActions().addAction(action);
2011 }
2012 }
2013
2014
2015
2016
2017
2018
2019 public AdditionalTableActions getAdditionalTableActions() {
2020 Action additionalActions = getTable().getActionMap().get(AdditionalTableActions.ACTION_NAME);
2021 if (!(additionalActions instanceof AdditionalTableActions)) {
2022 additionalActions = new AdditionalTableActions();
2023 getTable().getActionMap().put(AdditionalTableActions.ACTION_NAME, additionalActions);
2024 }
2025 return (AdditionalTableActions) additionalActions;
2026 }
2027
2028
2029
2030
2031
2032
2033 public Component getNextComponentToFocus() {
2034 return null;
2035 }
2036
2037
2038
2039
2040
2041
2042 public Component getPreviousComponentToFocus() {
2043 return null;
2044 }
2045
2046 private JScrollPane getParentScrollPane() {
2047 Container parent = SwingUtilities.getUnwrappedParent(getTable());
2048 JScrollPane scrollPane = null;
2049 if (parent instanceof JViewport) {
2050 JViewport viewport = (JViewport) parent;
2051 Container viewportParent = viewport.getParent();
2052 if (viewportParent instanceof JScrollPane) {
2053 scrollPane = (JScrollPane) viewportParent;
2054 }
2055 }
2056 Assert.notNull(scrollPane, "the table should be in a parent scroll pane");
2057 return scrollPane;
2058 }
2059
2060
2061
2062
2063 protected void fixScrollableHeight() {
2064 if (getFixedTable() != null && getFixedTable().getColumnCount() > 0 && getTable().getColumnCount() == 0) {
2065 SwingUtilities.invokeLater(() ->
2066 getTable().setPreferredSize(
2067 new Dimension(
2068 getTable().getPreferredScrollableViewportSize().width,
2069 getFixedTable().getPreferredSize().height
2070 )
2071 )
2072 );
2073 }
2074 }
2075 }