OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 174 Rev 182
Line 1... Line 1...
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
3
 * 
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
5
 * 
5
 * 
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
9
 * language governing permissions and limitations under the License.
Line 18... Line 18...
18
import org.openconcerto.sql.Log;
18
import org.openconcerto.sql.Log;
19
import org.openconcerto.sql.TM;
19
import org.openconcerto.sql.TM;
20
import org.openconcerto.sql.element.SQLComponent;
20
import org.openconcerto.sql.element.SQLComponent;
21
import org.openconcerto.sql.element.SQLElement;
21
import org.openconcerto.sql.element.SQLElement;
22
import org.openconcerto.sql.element.SQLElementDirectory;
22
import org.openconcerto.sql.element.SQLElementDirectory;
-
 
23
import org.openconcerto.sql.model.RowRef;
23
import org.openconcerto.sql.model.SQLField;
24
import org.openconcerto.sql.model.SQLField;
24
import org.openconcerto.sql.model.SQLRow;
25
import org.openconcerto.sql.model.SQLRow;
25
import org.openconcerto.sql.model.SQLRowAccessor;
26
import org.openconcerto.sql.model.SQLRowAccessor;
26
import org.openconcerto.sql.model.SQLRowValues;
27
import org.openconcerto.sql.model.SQLRowValues;
27
import org.openconcerto.sql.model.SQLTable;
28
import org.openconcerto.sql.model.SQLTable;
28
import org.openconcerto.sql.model.Where;
29
import org.openconcerto.sql.model.Where;
-
 
30
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
29
import org.openconcerto.sql.request.ListSQLRequest;
31
import org.openconcerto.sql.request.ListSQLRequest;
30
import org.openconcerto.sql.request.UpdateBuilder;
32
import org.openconcerto.sql.request.UpdateBuilder;
31
import org.openconcerto.sql.users.User;
33
import org.openconcerto.sql.users.User;
32
import org.openconcerto.sql.users.UserManager;
34
import org.openconcerto.sql.users.UserManager;
33
import org.openconcerto.sql.users.rights.TableAllRights;
35
import org.openconcerto.sql.users.rights.TableAllRights;
34
import org.openconcerto.sql.view.FileTransfertHandler;
36
import org.openconcerto.sql.view.FileTransfertHandler;
35
import org.openconcerto.sql.view.IListener;
37
import org.openconcerto.sql.view.IListener;
-
 
38
import org.openconcerto.sql.view.RowMetadata;
-
 
39
import org.openconcerto.sql.view.RowMetadataCache;
36
import org.openconcerto.sql.view.list.IListeAction.ButtonsBuilder;
40
import org.openconcerto.sql.view.list.IListeAction.ButtonsBuilder;
37
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
41
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
38
import org.openconcerto.sql.view.list.IListeAction.PopupBuilder;
42
import org.openconcerto.sql.view.list.IListeAction.PopupBuilder;
39
import org.openconcerto.sql.view.list.IListeAction.PopupEvent;
43
import org.openconcerto.sql.view.list.IListeAction.PopupEvent;
40
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
44
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
-
 
45
import org.openconcerto.sql.view.list.action.ListEvent;
-
 
46
import org.openconcerto.sql.view.list.action.SQLRowValuesAction;
41
import org.openconcerto.ui.FontUtils;
47
import org.openconcerto.ui.FontUtils;
42
import org.openconcerto.ui.FormatEditor;
48
import org.openconcerto.ui.FormatEditor;
43
import org.openconcerto.ui.MenuUtils;
49
import org.openconcerto.ui.MenuUtils;
44
import org.openconcerto.ui.PopupMouseListener;
50
import org.openconcerto.ui.PopupMouseListener;
45
import org.openconcerto.ui.SwingThreadUtils;
51
import org.openconcerto.ui.SwingThreadUtils;
Line 89... Line 95...
89
import java.sql.Time;
95
import java.sql.Time;
90
import java.sql.Timestamp;
96
import java.sql.Timestamp;
91
import java.text.DateFormat;
97
import java.text.DateFormat;
92
import java.text.Format;
98
import java.text.Format;
93
import java.util.ArrayList;
99
import java.util.ArrayList;
94
import java.util.Calendar;
-
 
95
import java.util.Collection;
100
import java.util.Collection;
96
import java.util.Collections;
101
import java.util.Collections;
97
import java.util.Date;
102
import java.util.Date;
98
import java.util.EventObject;
103
import java.util.EventObject;
99
import java.util.HashMap;
104
import java.util.HashMap;
100
import java.util.HashSet;
105
import java.util.HashSet;
-
 
106
import java.util.IdentityHashMap;
101
import java.util.LinkedHashMap;
107
import java.util.LinkedHashMap;
102
import java.util.List;
108
import java.util.List;
103
import java.util.Locale;
109
import java.util.Locale;
104
import java.util.Map;
110
import java.util.Map;
105
import java.util.Map.Entry;
111
import java.util.Map.Entry;
106
import java.util.Set;
112
import java.util.Set;
107
import java.util.concurrent.ExecutionException;
113
import java.util.concurrent.ExecutionException;
-
 
114
import java.util.function.BiFunction;
108
 
115
 
109
import javax.swing.AbstractAction;
116
import javax.swing.AbstractAction;
110
import javax.swing.Action;
117
import javax.swing.Action;
111
import javax.swing.DropMode;
118
import javax.swing.DropMode;
112
import javax.swing.InputMap;
119
import javax.swing.InputMap;
Line 144... Line 151...
144
 * 
151
 * 
145
 * @author ILM Informatique
152
 * @author ILM Informatique
146
 */
153
 */
147
public final class IListe extends JPanel {
154
public final class IListe extends JPanel {
148
 
155
 
149
    static private final class LockAction extends RowAction {
156
    static private final class LockAction extends SQLRowValuesAction {
150
        private final boolean lock;
157
        private final boolean lock;
151
 
158
 
152
        public LockAction(final boolean lock) {
159
        public LockAction(final boolean lock) {
153
            super(new AbstractAction(TM.tr(lock ? "ilist.lockRows" : "ilist.unlockRows")) {
-
 
154
                @Override
160
            super(false, true, (e) -> {
155
                public void actionPerformed(ActionEvent e) {
-
 
156
                    final IListe list = IListe.get(e);
-
 
157
                    final List<Integer> ids = list.getSelection().getSelectedIDs();
161
                final List<Number> ids = e.getSelectedIDs();
158
                    final SQLTable t = list.getSource().getPrimaryTable();
162
                final SQLTable t = e.getTable();
159
                    final UpdateBuilder update = new UpdateBuilder(t);
163
                final UpdateBuilder update = new UpdateBuilder(t);
160
                    update.setObject(SQLComponent.READ_ONLY_FIELD, lock ? SQLComponent.READ_ONLY_VALUE : SQLComponent.READ_WRITE_VALUE);
164
                update.setObject(SQLComponent.READ_ONLY_FIELD, lock ? SQLComponent.READ_ONLY_VALUE : SQLComponent.READ_WRITE_VALUE);
161
                    final User user = UserManager.getUser();
165
                final User user = UserManager.getUser();
162
                    if (user != null)
166
                if (user != null)
163
                        update.setObject(SQLComponent.READ_ONLY_USER_FIELD, user.getId());
167
                    update.setObject(SQLComponent.READ_ONLY_USER_FIELD, user.getId());
164
                    update.setWhere(new Where(t.getKey(), ids));
168
                update.setWhere(new Where(t.getKey(), ids));
165
                    t.getDBSystemRoot().getDataSource().execute(update.asString());
169
                t.getDBSystemRoot().getDataSource().execute(update.asString());
166
                    // don't fire too many times, as each one will cause UpdateQueue to issue a
170
                // don't fire too many times, as each one will cause UpdateQueue to issue a
167
                    // request
171
                // request
168
                    final Collection<? extends Number> fireIDs = ids.size() < 12 ? ids : Collections.singleton(SQLRow.NONEXISTANT_ID);
172
                final Collection<? extends Number> fireIDs = ids.size() < 12 ? ids : Collections.singleton(SQLRow.NONEXISTANT_ID);
169
                    for (final Number fireID : fireIDs)
173
                for (final Number fireID : fireIDs)
170
                        t.fireTableModified(fireID.intValue(), update.getFieldsNames());
174
                    t.fireTableModified(fireID.intValue(), update.getFieldsNames());
171
                }
175
            });
172
            }, false, true);
176
            this.setName(TM.tr(lock ? "ilist.lockRows" : "ilist.unlockRows"));
173
            this.lock = lock;
177
            this.lock = lock;
174
        }
178
        }
175
 
179
 
176
        @Override
180
        @Override
177
        public boolean enabledFor(IListeEvent evt) {
181
        public boolean enabledFor(ListEvent evt) {
178
            boolean hasRight = TableAllRights.currentUserHasRight(this.lock ? TableAllRights.USER_UI_LOCK_ROW : TableAllRights.USER_UI_UNLOCK_ROW, evt.getTable());
182
            boolean hasRight = TableAllRights.currentUserHasRight(this.lock ? TableAllRights.USER_UI_LOCK_ROW : TableAllRights.USER_UI_UNLOCK_ROW, evt.getTable());
179
            return !evt.getSelectedRows().isEmpty() && hasRight;
183
            return !evt.getSelectedRowAccessors().isEmpty() && hasRight;
180
        }
184
        }
181
    }
185
    }
182
 
186
 
183
    private static LockAction LOCK_ACTION;
187
    private static LockAction LOCK_ACTION;
184
    private static LockAction UNLOCK_ACTION;
188
    private static LockAction UNLOCK_ACTION;
Line 197... Line 201...
197
        if (UNLOCK_ACTION == null)
201
        if (UNLOCK_ACTION == null)
198
            UNLOCK_ACTION = new LockAction(false);
202
            UNLOCK_ACTION = new LockAction(false);
199
        return UNLOCK_ACTION;
203
        return UNLOCK_ACTION;
200
    }
204
    }
201
 
205
 
-
 
206
    private static final int MD_BATCH_SIZE = 100;
-
 
207
    private static final RowMetadataCache MD_CACHE = new RowMetadataCache(120, 5000, IListe.class.getName());
-
 
208
 
202
    /**
209
    /**
203
     * When this system property is set, table {@link JTableStateManager state} is never read nor
210
     * When this system property is set, table {@link JTableStateManager state} is never read nor
204
     * written. I.e. the user can change the table state but it will be reset at each launch.
211
     * written. I.e. the user can change the table state but it will be reset at each launch.
205
     */
212
     */
206
    public static final String STATELESS_TABLE_PROP = "org.openconcerto.sql.list.statelessTable";
213
    public static final String STATELESS_TABLE_PROP = "org.openconcerto.sql.list.statelessTable";
Line 331... Line 338...
331
        this.naListeners = new ArrayList<IListener>();
338
        this.naListeners = new ArrayList<IListener>();
332
        this.modelPCListeners = new ArrayList<PropertyChangeListener>();
339
        this.modelPCListeners = new ArrayList<PropertyChangeListener>();
333
 
340
 
334
        this.sorter = new TableSorter();
341
        this.sorter = new TableSorter();
335
        this.jTable = new JTable(this.sorter) {
342
        this.jTable = new JTable(this.sorter) {
-
 
343
 
-
 
344
            // By default the tooltip doesn't follow the mouse if the string remains the same
-
 
345
            // (probably for performance reasons)
-
 
346
            private final boolean followMouseWorkaround = !Boolean.getBoolean("jtable.tooltip_follow_mouse.disable");
-
 
347
 
336
            @Override
348
            @Override
337
            public String getToolTipText(MouseEvent event) {
349
            public String getToolTipText(MouseEvent event) {
338
                final String original = super.getToolTipText(event);
350
                final String original = super.getToolTipText(event);
339
 
351
 
340
                // Locate the row under the event location
352
                // Locate the row under the event location
Line 351... Line 363...
351
                        infoL.add(original.substring(html.length(), original.length() - html.length() - 1));
363
                        infoL.add(original.substring(html.length(), original.length() - html.length() - 1));
352
                    else
364
                    else
353
                        infoL.add(original);
365
                        infoL.add(original);
354
                }
366
                }
355
 
367
 
356
                final SQLRowValues row = ITableModel.getLine(this.getModel(), rowIndex).getRow();
368
                final SQLRowAccessor row = ITableModel.getLine(this.getModel(), rowIndex).getRowAccessor();
-
 
369
 
-
 
370
                final RowRef cacheKey = row.getRowRef();
-
 
371
                final RowMetadata md = MD_CACHE.get(cacheKey);
-
 
372
                if (md != null) {
-
 
373
                    final String create = getLine(true, md);
-
 
374
                    final String modif = getLine(false, md);
-
 
375
                    if (create == null && modif == null) {
-
 
376
                        infoL.add(TM.tr("ilist.metadata.na"));
-
 
377
                    } else {
-
 
378
                        if (create != null)
-
 
379
                            infoL.add(create);
-
 
380
                        if (modif != null)
-
 
381
                            infoL.add(modif);
-
 
382
                    }
-
 
383
                    // TODO locked by
-
 
384
 
-
 
385
                } else {
-
 
386
                    final int half = MD_BATCH_SIZE / 2;
-
 
387
                    final int firstIndex = Math.max(0, rowIndex - half);
-
 
388
                    final int lastIndex = Math.min(getRowCount(), rowIndex + half);
-
 
389
                    final Set<Number> ids = CollectionUtils.newHashSet(MD_BATCH_SIZE);
-
 
390
                    for (int i = firstIndex; i < lastIndex; i++) {
-
 
391
                        ids.add(ITableModel.getLine(this.getModel(), i).getRowAccessor().getIDNumber());
-
 
392
                    }
-
 
393
                    MD_CACHE.fetch(cacheKey, ids);
357
 
394
 
358
                final String create = getLine(true, row, getSource().getPrimaryTable().getCreationUserField(), getSource().getPrimaryTable().getCreationDateField());
-
 
359
                if (create != null)
-
 
360
                    infoL.add(create);
395
                    infoL.add(TM.tr("ilist.metadata.loading"));
361
                final String modif = getLine(false, row, getSource().getPrimaryTable().getModifUserField(), getSource().getPrimaryTable().getModifDateField());
-
 
362
                if (modif != null)
-
 
363
                    infoL.add(modif);
-
 
364
                // TODO locked by
396
                }
365
 
397
 
366
                final String info;
398
                final String info;
367
                if (infoL.size() == 0)
399
                if (infoL.size() == 0) {
368
                    info = null;
400
                    info = null;
369
                else
401
                } else {
-
 
402
                    final StringBuilder sb = new StringBuilder(256);
-
 
403
                    sb.append("<html>");
370
                    info = "<html>" + CollectionUtils.join(infoL, "<br/>") + "</html>";
404
                    sb.append(CollectionUtils.join(infoL, "<br/>"));
-
 
405
                    sb.append("</html>");
-
 
406
                    if (this.followMouseWorkaround) {
371
                // ATTN doesn't follow the mouse if info remains the same, MAYBE add an identifier
407
                        // This force the JRE to repaint the tooltip at the mouse location :
-
 
408
                        // 1. even without mainInfo changing (e.g. "unavailable")
-
 
409
                        // 2. but only when changing row
-
 
410
                        // Otherwise (e.g. adding or not a space at the end, for each call) the
-
 
411
                        // tooltip is drawn continuously and CPU load is quite heavy.
-
 
412
                        sb.append("<!--");
-
 
413
                        sb.append(rowIndex);
-
 
414
                        sb.append("-->");
-
 
415
                    }
-
 
416
                    info = sb.toString();
-
 
417
                }
-
 
418
 
372
                return info;
419
                return info;
373
            }
420
            }
374
 
421
 
375
            public String getLine(final boolean created, final SQLRowValues row, final SQLField userF, final SQLField dateF) {
422
            public String getLine(final boolean created, final RowMetadata md) {
376
                final Calendar date = dateF == null ? null : row.getDate(dateF.getName());
423
                final Date date = created ? md.getCreation() : md.getModification();
377
                final SQLRowAccessor user = userF == null || row.getObject(userF.getName()) == null || row.isForeignEmpty(userF.getName()) ? null : row.getForeign(userF.getName());
424
                final Integer userID = created ? md.getUserCreate() : md.getUserModify();
378
                if (user == null && date == null)
425
                if (userID == null && date == null)
379
                    return null;
426
                    return null;
380
 
427
 
381
                final int userParam;
428
                final int userParam;
382
                final String firstName, lastName;
429
                final String firstName, lastName;
383
                if (user != null) {
430
                if (userID != null) {
384
                    userParam = 1;
431
                    userParam = 1;
-
 
432
                    final User user = UserManager.getInstance().getUser(userID);
385
                    firstName = user.getString("PRENOM");
433
                    firstName = user.getFirstName();
386
                    lastName = user.getString("NOM");
434
                    lastName = user.getName();
387
                } else {
435
                } else {
388
                    userParam = 0;
436
                    userParam = 0;
389
                    firstName = null;
437
                    firstName = null;
390
                    lastName = null;
438
                    lastName = null;
391
                }
439
                }
392
 
440
 
393
                return TM.tr("ilist.metadata", created ? 1 : 0, userParam, firstName, lastName, date == null ? 0 : 1, date == null ? null : date.getTime());
441
                return TM.tr("ilist.metadata", created ? 1 : 0, userParam, firstName, lastName, date == null ? 0 : 1, date);
394
            }
442
            }
395
 
443
 
396
            @Override
444
            @Override
397
            protected TableColumnModel createDefaultColumnModel() {
445
            protected TableColumnModel createDefaultColumnModel() {
398
                // allow to hide columns
446
                // allow to hide columns
Line 599... Line 647...
599
        final RowAction res = new PredicateRowAction(action, false, true, id).setPredicate(IListeEvent.getSingleSelectionPredicate());
647
        final RowAction res = new PredicateRowAction(action, false, true, id).setPredicate(IListeEvent.getSingleSelectionPredicate());
600
        this.addIListeAction(res);
648
        this.addIListeAction(res);
601
        return res;
649
        return res;
602
    }
650
    }
603
 
651
 
-
 
652
    // Transitional class while we convert RowAction to SQLRowValuesAction
-
 
653
    @Deprecated
-
 
654
    static public final class ConvertedAction extends SQLRowValuesAction {
-
 
655
        private final RowAction rowAction;
-
 
656
 
-
 
657
        public ConvertedAction(final RowAction a) {
-
 
658
            super(a.inHeader(), a.inPopupMenu(), a.getID(), (evt) -> {
-
 
659
                a.getAction().actionPerformed(new ActionEvent(evt.getSource(), ActionEvent.ACTION_PERFORMED, null));
-
 
660
            });
-
 
661
            this.rowAction = a;
-
 
662
            if (a.getAction().getValue(Action.NAME) != null)
-
 
663
                this.setName(String.valueOf(a.getAction().getValue(Action.NAME)));
-
 
664
        }
-
 
665
 
-
 
666
        @Override
-
 
667
        public boolean enabledFor(ListEvent evt) {
-
 
668
            return this.getRowAction().enabledFor(evt);
-
 
669
        }
-
 
670
 
-
 
671
        public final RowAction getRowAction() {
-
 
672
            return this.rowAction;
-
 
673
        }
-
 
674
    }
-
 
675
 
-
 
676
    public final RowAction addRowValuesAction(SQLRowValuesAction a) {
-
 
677
        final RowAction action;
-
 
678
        if (a instanceof ConvertedAction) {
-
 
679
            action = ((ConvertedAction) a).getRowAction();
-
 
680
        } else {
-
 
681
            action = new RowAction(new AbstractAction(a.getName()) {
-
 
682
                @Override
-
 
683
                public void actionPerformed(ActionEvent e) {
-
 
684
                    a.getAction().accept(IListe.get(e).createListEvent());
-
 
685
                }
-
 
686
            }, a.inHeader(), a.inPopupMenu(), a.getID()) {
-
 
687
 
-
 
688
                @Override
-
 
689
                public boolean enabledFor(ListEvent evt) {
-
 
690
                    return a.enabledFor(evt);
-
 
691
                }
-
 
692
 
-
 
693
            };
-
 
694
        }
-
 
695
        if (this.addIListeAction(action))
-
 
696
            return action;
-
 
697
        else
-
 
698
            return null;
-
 
699
    }
-
 
700
 
-
 
701
    public final Map<SQLRowValuesAction, IListeAction> addRowValuesActions(Collection<? extends SQLRowValuesAction> actions) {
-
 
702
        final Map<SQLRowValuesAction, IListeAction> res = new IdentityHashMap<>();
-
 
703
        for (final SQLRowValuesAction a : actions) {
-
 
704
            final RowAction action = addRowValuesAction(a);
-
 
705
            if (action != null)
-
 
706
                res.put(a, action);
-
 
707
        }
-
 
708
        return res;
-
 
709
    }
-
 
710
 
604
    public final void addIListeActions(Collection<? extends IListeAction> actions) {
711
    public final void addIListeActions(Collection<? extends IListeAction> actions) {
605
        for (final IListeAction a : actions)
712
        for (final IListeAction a : actions)
606
            this.addIListeAction(a);
713
            this.addIListeAction(a);
607
    }
714
    }
608
 
715
 
Line 617... Line 724...
617
            }
724
            }
618
        }
725
        }
619
        return -1;
726
        return -1;
620
    }
727
    }
621
 
728
 
622
    public final void addIListeAction(IListeAction action) {
729
    public final boolean addIListeAction(IListeAction action) {
623
        // we need to handle addition of an already added action at least for setDefaultRowAction()
730
        // we need to handle addition of an already added action at least for setDefaultRowAction()
624
        if (this.rowActions.containsKey(action))
731
        if (this.rowActions.containsKey(action))
625
            return;
732
            return false;
626
        final ButtonsBuilder headerBtns = action.getHeaderButtons();
733
        final ButtonsBuilder headerBtns = action.getHeaderButtons();
627
        this.rowActions.put(action, headerBtns);
734
        this.rowActions.put(action, headerBtns);
628
        if (headerBtns.getContent().size() > 0) {
735
        if (headerBtns.getContent().size() > 0) {
629
            updateButton(headerBtns, new IListeEvent(this));
736
            updateButton(headerBtns, this.createListEvent());
630
            for (final JButton headerBtn : headerBtns.getContent().keySet()) {
737
            for (final JButton headerBtn : headerBtns.getContent().keySet()) {
631
                headerBtn.setOpaque(false);
738
                headerBtn.setOpaque(false);
632
                this.btnPanel.add(headerBtn, findGroupIndex((String) headerBtn.getClientProperty(ButtonsBuilder.GROUPNAME_PROPNAME)));
739
                this.btnPanel.add(headerBtn, findGroupIndex((String) headerBtn.getClientProperty(ButtonsBuilder.GROUPNAME_PROPNAME)));
633
            }
740
            }
634
            this.btnPanel.setVisible(true);
741
            this.btnPanel.setVisible(true);
635
        }
742
        }
-
 
743
        return true;
636
    }
744
    }
637
 
745
 
638
    public final void removeIListeActions(Collection<? extends IListeAction> actions) {
746
    public final void removeIListeActions(Collection<? extends IListeAction> actions) {
639
        for (final IListeAction a : actions)
747
        for (final IListeAction a : actions)
640
            this.removeIListeAction(a);
748
            this.removeIListeAction(a);
Line 654... Line 762...
654
        if (action.equals(this.defaultRowAction))
762
        if (action.equals(this.defaultRowAction))
655
            this.setDefaultRowAction(null);
763
            this.setDefaultRowAction(null);
656
    }
764
    }
657
 
765
 
658
    private void updateButtons() {
766
    private void updateButtons() {
659
        final IListeEvent evt = new IListeEvent(this);
767
        final IListeEvent evt = this.createListEvent();
660
        for (final ButtonsBuilder btns : this.rowActions.values()) {
768
        for (final ButtonsBuilder btns : this.rowActions.values()) {
661
            this.updateButton(btns, evt);
769
            this.updateButton(btns, evt);
662
        }
770
        }
663
    }
771
    }
664
 
772
 
Line 668... Line 776...
668
        }
776
        }
669
    }
777
    }
670
 
778
 
671
    private JPopupMenu updatePopupMenu(final boolean onRows) {
779
    private JPopupMenu updatePopupMenu(final boolean onRows) {
672
        this.popup.removeAll();
780
        this.popup.removeAll();
673
        final PopupEvent evt = new PopupEvent(this, onRows);
781
        final PopupEvent evt = this.createPopupEvent(onRows);
674
        final Action defaultAction = this.defaultRowAction != null ? this.defaultRowAction.getDefaultAction(evt) : null;
782
        final Action defaultAction = this.defaultRowAction != null ? this.defaultRowAction.getDefaultAction(evt) : null;
675
        final VirtualMenu menu = VirtualMenu.createRoot(null);
783
        final VirtualMenu menu = VirtualMenu.createRoot(null);
676
        for (final IListeAction a : this.rowActions.keySet()) {
784
        for (final IListeAction a : this.rowActions.keySet()) {
677
            final PopupBuilder popupContent = a.getPopupContent(evt);
785
            final PopupBuilder popupContent = a.getPopupContent(evt);
678
            if (defaultAction != null && a == this.defaultRowAction) {
786
            if (defaultAction != null && a == this.defaultRowAction) {
Line 719... Line 827...
719
 
827
 
720
    private void performDefaultAction(MouseEvent e) {
828
    private void performDefaultAction(MouseEvent e) {
721
        // special method needed since sometimes getPopupContent() can access the DB (optionally
829
        // special method needed since sometimes getPopupContent() can access the DB (optionally
722
        // creating threads) or be slow
830
        // creating threads) or be slow
723
        if (this.defaultRowAction != null) {
831
        if (this.defaultRowAction != null) {
724
            final Action defaultAction = this.defaultRowAction.getDefaultAction(new IListeEvent(this));
832
            final Action defaultAction = this.defaultRowAction.getDefaultAction(this.createListEvent());
725
            if (defaultAction != null)
833
            if (defaultAction != null)
726
                defaultAction.actionPerformed(new ActionEvent(e.getSource(), e.getID(), null, e.getWhen(), e.getModifiers()));
834
                defaultAction.actionPerformed(new ActionEvent(e.getSource(), e.getID(), null, e.getWhen(), e.getModifiers()));
727
        }
835
        }
728
    }
836
    }
729
 
837
 
-
 
838
    final IListeEvent createListEvent() {
-
 
839
        return createEvent((vals, accessors) -> new IListeEvent(this, vals, accessors));
-
 
840
    }
-
 
841
 
-
 
842
    final PopupEvent createPopupEvent(final boolean onRows) {
-
 
843
        return createEvent((vals, accessors) -> new PopupEvent(this, vals, accessors, onRows));
-
 
844
    }
-
 
845
 
-
 
846
    private final <E extends ListEvent> E createEvent(final BiFunction<List<SQLRowValues>, List<? extends SQLRowAccessor>, E> ctor) {
-
 
847
        final List<SQLRowValues> vals;
-
 
848
        final List<? extends SQLRowAccessor> accessors;
-
 
849
        if (this.getSource().getKeepMode() == KeepMode.GRAPH) {
-
 
850
            vals = this.getSelectedRows();
-
 
851
            accessors = vals;
-
 
852
        } else {
-
 
853
            vals = null;
-
 
854
            accessors = this.getSelectedRowAccessors();
-
 
855
        }
-
 
856
        return ctor.apply(vals, accessors);
-
 
857
    }
-
 
858
 
730
    private void uiInit() {
859
    private void uiInit() {
731
        // * filter
860
        // * filter
732
        this.filter.addMouseListener(new MouseAdapter() {
861
        this.filter.addMouseListener(new MouseAdapter() {
733
            @Override
862
            @Override
734
            public void mouseClicked(MouseEvent e) {
863
            public void mouseClicked(MouseEvent e) {
Line 778... Line 907...
778
                        final XTableColumnModel colModel = (XTableColumnModel) getJTable().getColumnModel();
907
                        final XTableColumnModel colModel = (XTableColumnModel) getJTable().getColumnModel();
779
                        final JComponent cb = (JComponent) e.getSource();
908
                        final JComponent cb = (JComponent) e.getSource();
780
                        final int columnIndex = ((Number) cb.getClientProperty(COL_INDEX_KEY)).intValue();
909
                        final int columnIndex = ((Number) cb.getClientProperty(COL_INDEX_KEY)).intValue();
781
                        final TableColumn col = colModel.getColumn(columnIndex, false);
910
                        final TableColumn col = colModel.getColumn(columnIndex, false);
782
                        final boolean newValue = !colModel.isColumnVisible(col);
911
                        final boolean newValue = !colModel.isColumnVisible(col);
-
 
912
                        // Workaround for crash on linux Java 16 with Nimbus L&F or Flat L&F
-
 
913
                        IListe.this.jTable.getTableHeader().setDraggedColumn(null);
-
 
914
 
783
                        // don't remove last column
915
                        // don't remove last column
784
                        if (newValue || colModel.getColumnCount(true) > 1)
916
                        if (newValue || colModel.getColumnCount(true) > 1)
785
                            colModel.setColumnVisible(col, newValue);
917
                            colModel.setColumnVisible(col, newValue);
786
                    }
918
                    }
787
                };
919
                };
Line 947... Line 1079...
947
 
1079
 
948
        this.setOpaque(false);
1080
        this.setOpaque(false);
949
        this.setTransferHandler(new FileTransfertHandler(getSource().getPrimaryTable()));
1081
        this.setTransferHandler(new FileTransfertHandler(getSource().getPrimaryTable()));
950
 
1082
 
951
        if (this.getSource().getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_FIELD) != null) {
1083
        if (this.getSource().getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_FIELD) != null) {
952
            this.addIListeAction(getUnlockAction());
1084
            this.addRowValuesAction(getUnlockAction());
953
            this.addIListeAction(getLockAction());
1085
            this.addRowValuesAction(getLockAction());
954
        }
1086
        }
955
    }
1087
    }
956
 
1088
 
957
    protected synchronized final void invertDebug() {
1089
    protected synchronized final void invertDebug() {
958
        this.setDebug(!this.debugFilter);
1090
        this.setDebug(!this.debugFilter);
Line 1147... Line 1279...
1147
        final ListSQLLine line = this.getLine(index);
1279
        final ListSQLLine line = this.getLine(index);
1148
        final Object toCast;
1280
        final Object toCast;
1149
        if (clazz == SQLRowValues.class) {
1281
        if (clazz == SQLRowValues.class) {
1150
            toCast = line.getRow().toImmutable();
1282
            toCast = line.getRow().toImmutable();
1151
        } else if (clazz == SQLRow.class) {
1283
        } else if (clazz == SQLRow.class) {
-
 
1284
            toCast = line.getRowAccessor().asRow();
-
 
1285
        } else if (clazz == SQLRowAccessor.class) {
1152
            toCast = line.getRow().asRow();
1286
            toCast = line.getRowAccessor();
1153
        } else if (clazz == ListSQLLine.class) {
1287
        } else if (clazz == ListSQLLine.class) {
1154
            toCast = line;
1288
            toCast = line;
1155
        } else {
1289
        } else {
1156
            throw new IllegalArgumentException("Not implemented : " + clazz);
1290
            throw new IllegalArgumentException("Not implemented : " + clazz);
1157
        }
1291
        }
Line 1171... Line 1305...
1171
 
1305
 
1172
    public SQLRowValues getSelectedRow() {
1306
    public SQLRowValues getSelectedRow() {
1173
        return this.getSelectedRow(SQLRowValues.class);
1307
        return this.getSelectedRow(SQLRowValues.class);
1174
    }
1308
    }
1175
 
1309
 
-
 
1310
    public SQLRowAccessor getSelectedRowAccessor() {
-
 
1311
        return this.getSelectedRow(SQLRowAccessor.class);
-
 
1312
    }
-
 
1313
 
1176
    // selected row cannot be inferred from iterateSelectedRows() since the user might have selected
1314
    // selected row cannot be inferred from iterateSelectedRows() since the user might have selected
1177
    // the last row anywhere in the selection
1315
    // the last row anywhere in the selection
1178
    private final <R extends SQLRowAccessor> R getSelectedRow(final Class<R> clazz) {
1316
    private final <R extends SQLRowAccessor> R getSelectedRow(final Class<R> clazz) {
1179
        final int selectedIndex = this.state.getSelectedIndex().intValue();
1317
        final int selectedIndex = this.state.getSelectedIndex().intValue();
1180
        if (selectedIndex == BaseListStateModel.INVALID_INDEX)
1318
        if (selectedIndex == BaseListStateModel.INVALID_INDEX)
Line 1193... Line 1331...
1193
 
1331
 
1194
    public final List<ListSQLLine> getSelectedLines() {
1332
    public final List<ListSQLLine> getSelectedLines() {
1195
        return iterateSelectedRows(ListSQLLine.class);
1333
        return iterateSelectedRows(ListSQLLine.class);
1196
    }
1334
    }
1197
 
1335
 
-
 
1336
    public final List<SQLRowAccessor> getSelectedRowAccessors() {
-
 
1337
        return iterateSelectedRows(SQLRowAccessor.class);
-
 
1338
    }
-
 
1339
 
1198
    private final <R> List<R> iterateSelectedRows(final Class<R> clazz) {
1340
    private final <R> List<R> iterateSelectedRows(final Class<R> clazz) {
1199
        final ListSelectionModel selectionModel = this.getJTable().getSelectionModel();
1341
        final ListSelectionModel selectionModel = this.getJTable().getSelectionModel();
1200
        if (selectionModel.isSelectionEmpty())
1342
        if (selectionModel.isSelectionEmpty())
1201
            return Collections.emptyList();
1343
            return Collections.emptyList();
1202
 
1344
 
Line 1469... Line 1611...
1469
                this.tableStateManager.beginAutoSave();
1611
                this.tableStateManager.beginAutoSave();
1470
            loadTableState();
1612
            loadTableState();
1471
        }
1613
        }
1472
    }
1614
    }
1473
 
1615
 
-
 
1616
    public final boolean saveTableState() throws IOException {
-
 
1617
        final boolean hasFile = this.getConfigFile() != null;
-
 
1618
        if (hasFile)
-
 
1619
            this.tableStateManager.saveState();
-
 
1620
        return hasFile;
-
 
1621
    }
-
 
1622
 
1474
    private boolean loadTableState() {
1623
    private boolean loadTableState() {
1475
        // - if configFile changes setConfigFile() calls us
1624
        // - if configFile changes setConfigFile() calls us
1476
        // - if the model changes, fireTableStructureChanged() is called and thus
1625
        // - if the model changes, fireTableStructureChanged() is called and thus
1477
        // JTable.createDefaultColumnsFromModel() which calls us
1626
        // JTable.createDefaultColumnsFromModel() which calls us
1478
        if (this.getConfigFile() != null && this.getModel() != null)
1627
        if (this.getConfigFile() != null && this.getModel() != null)