Changeset 15226 in josm


Ignore:
Timestamp:
2019-07-07T18:36:38+02:00 (7 weeks ago)
Author:
Don-vip
Message:

fix #14208 - add dedicated buttons in filter dialog to sort/reverse filters order.

Major overhaul/harmonization of our reorderable/sortable models.

Location:
trunk
Files:
5 added
9 edited
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/Filter.java

    r14932 r15226  
    22package org.openstreetmap.josm.data.osm;
    33
     4import java.util.Comparator;
    45import java.util.Objects;
    56
     
    1516 * @since 2125
    1617 */
    17 public class Filter extends SearchSetting {
     18public class Filter extends SearchSetting implements Comparable<Filter> {
    1819    private static final String version = "1";
    1920
     
    167168        return e;
    168169    }
     170
     171    @Override
     172    public int compareTo(Filter o) {
     173        return Comparator
     174                .<Filter, String>comparing(f -> f.text)
     175                .thenComparing(f -> f.mode)
     176                .thenComparing(f -> f.caseSensitive)
     177                .thenComparing(f -> f.regexSearch)
     178                .thenComparing(f -> f.mapCSSSearch)
     179                .thenComparing(f -> f.enable)
     180                .thenComparing(f -> f.hiding)
     181                .thenComparing(f -> f.inverted)
     182                .compare(this, o);
     183    }
    169184}
  • trunk/src/org/openstreetmap/josm/data/osm/FilterModel.java

    r14992 r15226  
    88import java.util.ArrayList;
    99import java.util.Collection;
     10import java.util.Collections;
    1011import java.util.HashSet;
    1112import java.util.LinkedList;
     
    1617import javax.swing.JOptionPane;
    1718
     19import org.openstreetmap.josm.data.SortableModel;
    1820import org.openstreetmap.josm.data.StructUtils;
    1921import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry;
     
    2931 * @since 12400
    3032 */
    31 public class FilterModel {
     33public class FilterModel implements SortableModel<Filter> {
    3234
    3335    /**
     
    236238
    237239    /**
     240     * Moves the filters in the given rows by a number of positions.
     241     * @param delta negative or positive increment
     242     * @param rowIndexes The filter rows
     243     * @return true if the filters have been moved down
     244     * @since 15226
     245     */
     246    public boolean moveFilters(int delta, int... rowIndexes) {
     247        if (!canMove(delta, filters::size, rowIndexes))
     248            return false;
     249        doMove(delta, rowIndexes);
     250        updateFilterMatcher();
     251        return true;
     252    }
     253
     254    /**
    238255     * Moves down the filter in the given row.
    239256     * @param rowIndex The filter row
     
    241258     */
    242259    public boolean moveDownFilter(int rowIndex) {
    243         if (rowIndex >= filters.size() - 1)
    244             return false;
    245         filters.add(rowIndex + 1, filters.remove(rowIndex));
    246         updateFilterMatcher();
    247         return true;
     260        return moveFilters(1, rowIndex);
    248261    }
    249262
     
    254267     */
    255268    public boolean moveUpFilter(int rowIndex) {
    256         if (rowIndex <= 0 || rowIndex >= filters.size())
    257             return false;
    258         filters.add(rowIndex - 1, filters.remove(rowIndex));
    259         updateFilterMatcher();
    260         return true;
     269        return moveFilters(-1, rowIndex);
    261270    }
    262271
     
    277286     * @param filter The filter that should be placed in that row
    278287     * @return the filter previously at the specified position
    279      */
     288     * @deprecated Use {@link #setValue}
     289     */
     290    @Deprecated
    280291    public Filter setFilter(int rowIndex, Filter filter) {
     292        return setValue(rowIndex, filter);
     293    }
     294
     295    @Override
     296    public Filter setValue(int rowIndex, Filter filter) {
    281297        Filter result = filters.set(rowIndex, filter);
    282298        updateFilterMatcher();
     
    288304     * @param rowIndex The row index
    289305     * @return The filter in that row
    290      */
     306     * @deprecated Use {@link #getValue}
     307     */
     308    @Deprecated
    291309    public Filter getFilter(int rowIndex) {
     310        return getValue(rowIndex);
     311    }
     312
     313    @Override
     314    public Filter getValue(int rowIndex) {
    292315        return filters.get(rowIndex);
    293316    }
     
    418441        return result;
    419442    }
     443
     444    @Override
     445    public void sort() {
     446        Collections.sort(filters);
     447        updateFilterMatcher();
     448    }
     449
     450    @Override
     451    public void reverse() {
     452        Collections.reverse(filters);
     453        updateFilterMatcher();
     454    }
    420455}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java

    r15176 r15226  
    1212import java.util.Arrays;
    1313import java.util.List;
     14import java.util.stream.Collectors;
    1415
    1516import javax.swing.AbstractAction;
    1617import javax.swing.DefaultCellEditor;
     18import javax.swing.DefaultListSelectionModel;
    1719import javax.swing.JCheckBox;
    1820import javax.swing.JTable;
     
    6264
    6365    private JTable userTable;
    64     private final FilterTableModel filterModel = new FilterTableModel();
    65 
    66     private final EnableFilterAction enableFilterAction;
    67     private final HidingFilterAction hidingFilterAction;
     66    private final FilterTableModel filterModel = new FilterTableModel(new DefaultListSelectionModel());
     67
     68    private final AddAction addAction = new AddAction();
     69    private final EditAction editAction = new EditAction();
     70    private final DeleteAction deleteAction = new DeleteAction();
     71    private final MoveUpAction moveUpAction = new MoveUpAction();
     72    private final MoveDownAction moveDownAction = new MoveDownAction();
     73    private final SortAction sortAction = new SortAction();
     74    private final ReverseAction reverseAction = new ReverseAction();
     75    private final EnableFilterAction enableFilterAction = new EnableFilterAction();
     76    private final HidingFilterAction hidingFilterAction = new HidingFilterAction();
    6877
    6978    /**
     
    7584                        KeyEvent.VK_F, Shortcut.ALT_SHIFT), 162);
    7685        build();
    77         enableFilterAction = new EnableFilterAction();
    78         hidingFilterAction = new HidingFilterAction();
    7986        MultikeyActionsHandler.getInstance().addAction(enableFilterAction);
    8087        MultikeyActionsHandler.getInstance().addAction(hidingFilterAction);
     
    112119    };
    113120
     121    private abstract class FilterAction extends AbstractAction implements IEnabledStateUpdating {
     122
     123        FilterAction(String name, String description, String icon) {
     124            putValue(NAME, name);
     125            putValue(SHORT_DESCRIPTION, description);
     126            new ImageProvider("dialogs", icon).getResource().attachImageIcon(this, true);
     127        }
     128
     129        @Override
     130        public void updateEnabledState() {
     131            setEnabled(!filterModel.getSelectionModel().isSelectionEmpty());
     132        }
     133    }
     134
     135    private class AddAction extends FilterAction {
     136        AddAction() {
     137            super(tr("Add"), tr("Add filter."), /* ICON(dialogs/) */ "add");
     138        }
     139
     140        @Override
     141        public void actionPerformed(ActionEvent e) {
     142            SearchSetting searchSetting = SearchAction.showSearchDialog(new Filter());
     143            if (searchSetting != null) {
     144                filterModel.addFilter(new Filter(searchSetting));
     145            }
     146        }
     147
     148        @Override
     149        public void updateEnabledState() {
     150            // Do nothing
     151        }
     152    }
     153
     154    private class EditAction extends FilterAction {
     155        EditAction() {
     156            super(tr("Edit"), tr("Edit filter."), /* ICON(dialogs/) */ "edit");
     157        }
     158
     159        @Override
     160        public void actionPerformed(ActionEvent e) {
     161            int index = filterModel.getSelectionModel().getMinSelectionIndex();
     162            if (index < 0) return;
     163            Filter f = filterModel.getValue(index);
     164            SearchSetting searchSetting = SearchAction.showSearchDialog(f);
     165            if (searchSetting != null) {
     166                filterModel.setValue(index, new Filter(searchSetting));
     167            }
     168        }
     169    }
     170
     171    private class DeleteAction extends FilterAction {
     172        DeleteAction() {
     173            super(tr("Delete"), tr("Delete filter."), /* ICON(dialogs/) */ "delete");
     174        }
     175
     176        @Override
     177        public void actionPerformed(ActionEvent e) {
     178            int index = filterModel.getSelectionModel().getMinSelectionIndex();
     179            if (index >= 0) {
     180                filterModel.removeFilter(index);
     181            }
     182        }
     183    }
     184
     185    private class MoveUpAction extends FilterAction {
     186        MoveUpAction() {
     187            super(tr("Up"), tr("Move filter up."), /* ICON(dialogs/) */ "up");
     188        }
     189
     190        @Override
     191        public void actionPerformed(ActionEvent e) {
     192            int index = userTable.convertRowIndexToModel(userTable.getSelectionModel().getMinSelectionIndex());
     193            if (index >= 0 && filterModel.moveUp(index)) {
     194                filterModel.getSelectionModel().setSelectionInterval(index-1, index-1);
     195            }
     196        }
     197
     198        @Override
     199        public void updateEnabledState() {
     200            setEnabled(filterModel.canMoveUp());
     201        }
     202    }
     203
     204    private class MoveDownAction extends FilterAction {
     205        MoveDownAction() {
     206            super(tr("Down"), tr("Move filter down."), /* ICON(dialogs/) */ "down");
     207        }
     208
     209        @Override
     210        public void actionPerformed(ActionEvent e) {
     211            int index = userTable.convertRowIndexToModel(userTable.getSelectionModel().getMinSelectionIndex());
     212            if (index >= 0 && filterModel.moveDown(index)) {
     213                filterModel.getSelectionModel().setSelectionInterval(index+1, index+1);
     214            }
     215        }
     216
     217        @Override
     218        public void updateEnabledState() {
     219            setEnabled(filterModel.canMoveDown());
     220        }
     221    }
     222
     223    private class SortAction extends FilterAction {
     224        SortAction() {
     225            super(tr("Sort"), tr("Sort filters."), /* ICON(dialogs/) */ "sort");
     226        }
     227
     228        @Override
     229        public void actionPerformed(ActionEvent e) {
     230            filterModel.sort();
     231        }
     232
     233        @Override
     234        public void updateEnabledState() {
     235            setEnabled(filterModel.getRowCount() > 1);
     236        }
     237    }
     238
     239    private class ReverseAction extends FilterAction {
     240        ReverseAction() {
     241            super(tr("Reverse"), tr("Reverse the filters order."), /* ICON(dialogs/) */ "reverse");
     242        }
     243
     244        @Override
     245        public void actionPerformed(ActionEvent e) {
     246            filterModel.reverse();
     247        }
     248
     249        @Override
     250        public void updateEnabledState() {
     251            setEnabled(filterModel.getRowCount() > 1);
     252        }
     253    }
     254
    114255    /**
    115256     * Builds the GUI.
     
    120261        userTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    121262        userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    122         userTable.setAutoCreateRowSorter(true);
     263        userTable.setSelectionModel(filterModel.getSelectionModel());
    123264
    124265        TableHelper.adjustColumnWidth(userTable, 0, false);
     
    130271        userTable.setDefaultRenderer(String.class, new StringRenderer());
    131272        userTable.setDefaultEditor(String.class, new DefaultCellEditor(new DisableShortcutsOnFocusGainedTextField()));
    132 
    133         SideButton addButton = new SideButton(new AbstractAction() {
    134             {
    135                 putValue(NAME, tr("Add"));
    136                 putValue(SHORT_DESCRIPTION, tr("Add filter."));
    137                 new ImageProvider("dialogs", "add").getResource().attachImageIcon(this, true);
    138             }
    139 
    140             @Override
    141             public void actionPerformed(ActionEvent e) {
    142                 SearchSetting searchSetting = SearchAction.showSearchDialog(new Filter());
    143                 if (searchSetting != null) {
    144                     filterModel.addFilter(new Filter(searchSetting));
    145                 }
    146             }
    147         });
    148         SideButton editButton = new SideButton(new AbstractAction() {
    149             {
    150                 putValue(NAME, tr("Edit"));
    151                 putValue(SHORT_DESCRIPTION, tr("Edit filter."));
    152                 new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this, true);
    153             }
    154 
    155             @Override
    156             public void actionPerformed(ActionEvent e) {
    157                 int index = userTable.getSelectionModel().getMinSelectionIndex();
    158                 if (index < 0) return;
    159                 Filter f = filterModel.getFilter(index);
    160                 SearchSetting searchSetting = SearchAction.showSearchDialog(f);
    161                 if (searchSetting != null) {
    162                     filterModel.setFilter(index, new Filter(searchSetting));
    163                 }
    164             }
    165         });
    166         SideButton deleteButton = new SideButton(new AbstractAction() {
    167             {
    168                 putValue(NAME, tr("Delete"));
    169                 putValue(SHORT_DESCRIPTION, tr("Delete filter."));
    170                 new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this, true);
    171             }
    172 
    173             @Override
    174             public void actionPerformed(ActionEvent e) {
    175                 int index = userTable.getSelectionModel().getMinSelectionIndex();
    176                 if (index >= 0) {
    177                     filterModel.removeFilter(index);
    178                 }
    179             }
    180         });
    181         SideButton upButton = new SideButton(new AbstractAction() {
    182             {
    183                 putValue(NAME, tr("Up"));
    184                 putValue(SHORT_DESCRIPTION, tr("Move filter up."));
    185                 new ImageProvider("dialogs", "up").getResource().attachImageIcon(this, true);
    186             }
    187 
    188             @Override
    189             public void actionPerformed(ActionEvent e) {
    190                 int index = userTable.getSelectionModel().getMinSelectionIndex();
    191                 if (index >= 0) {
    192                     filterModel.moveUpFilter(index);
    193                     userTable.getSelectionModel().setSelectionInterval(index-1, index-1);
    194                 }
    195             }
    196         });
    197         SideButton downButton = new SideButton(new AbstractAction() {
    198             {
    199                 putValue(NAME, tr("Down"));
    200                 putValue(SHORT_DESCRIPTION, tr("Move filter down."));
    201                 new ImageProvider("dialogs", "down").getResource().attachImageIcon(this, true);
    202             }
    203 
    204             @Override
    205             public void actionPerformed(ActionEvent e) {
    206                 int index = userTable.getSelectionModel().getMinSelectionIndex();
    207                 if (index >= 0) {
    208                     filterModel.moveDownFilter(index);
    209                     userTable.getSelectionModel().setSelectionInterval(index+1, index+1);
    210                 }
    211             }
    212         });
    213273
    214274        // Toggle filter "enabled" on Enter
     
    218278                int index = userTable.getSelectedRow();
    219279                if (index >= 0) {
    220                     Filter filter = filterModel.getFilter(index);
     280                    Filter filter = filterModel.getValue(index);
    221281                    filterModel.setValueAt(!filter.enable, index, FilterTableModel.COL_ENABLED);
    222282                }
     
    230290                int index = userTable.getSelectedRow();
    231291                if (index >= 0) {
    232                     Filter filter = filterModel.getFilter(index);
     292                    Filter filter = filterModel.getValue(index);
    233293                    filterModel.setValueAt(!filter.hiding, index, FilterTableModel.COL_HIDING);
    234294                }
     
    236296        });
    237297
    238         createLayout(userTable, true, Arrays.asList(
    239                 addButton, editButton, deleteButton, upButton, downButton
    240         ));
     298        List<FilterAction> actions = Arrays.asList(addAction, editAction, deleteAction, moveUpAction, moveDownAction, sortAction, reverseAction);
     299        for (FilterAction action : actions) {
     300            TableHelper.adaptTo(action, filterModel);
     301            TableHelper.adaptTo(action, filterModel.getSelectionModel());
     302            action.updateEnabledState();
     303        }
     304        createLayout(userTable, true, actions.stream().map(a -> new SideButton(a, false)).collect(Collectors.toList()));
    241305    }
    242306
     
    384448
    385449            for (int i = 0; i < filterModel.getRowCount(); i++) {
    386                 Filter filter = filterModel.getFilter(i);
    387                 MultikeyInfo info = new MultikeyInfo(i, filter.text);
    388                 result.add(info);
     450                result.add(new MultikeyInfo(i, filterModel.getValue(i).text));
    389451            }
    390452
     
    420482        public void executeMultikeyAction(int index, boolean repeatLastAction) {
    421483            if (index >= 0 && index < filterModel.getRowCount()) {
    422                 Filter filter = filterModel.getFilter(index);
     484                Filter filter = filterModel.getValue(index);
    423485                filterModel.setValueAt(!filter.enable, index, FilterTableModel.COL_ENABLED);
    424486                lastFilter = filter;
     
    444506        public void executeMultikeyAction(int index, boolean repeatLastAction) {
    445507            if (index >= 0 && index < filterModel.getRowCount()) {
    446                 Filter filter = filterModel.getFilter(index);
     508                Filter filter = filterModel.getValue(index);
    447509                filterModel.setValueAt(!filter.hiding, index, FilterTableModel.COL_HIDING);
    448510                lastFilter = filter;
  • trunk/src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java

    r15065 r15226  
    99import java.util.List;
    1010
     11import javax.swing.ListSelectionModel;
    1112import javax.swing.table.AbstractTableModel;
    1213
     
    1718import org.openstreetmap.josm.gui.MapFrame;
    1819import org.openstreetmap.josm.gui.autofilter.AutoFilterManager;
     20import org.openstreetmap.josm.gui.util.SortableTableModel;
    1921import org.openstreetmap.josm.gui.widgets.OSDLabel;
    2022import org.openstreetmap.josm.tools.Logging;
     
    2426 *
    2527 * @author Petr_Dlouhý
     28 * @since 2125
    2629 */
    27 public class FilterTableModel extends AbstractTableModel {
     30public class FilterTableModel extends AbstractTableModel implements SortableTableModel<Filter> {
    2831
    2932    /**
     
    5053
    5154    /**
    52      * A helper for {@link #drawOSDText(Graphics2D)}.
     55     * The selection model
     56     */
     57    final ListSelectionModel selectionModel;
     58
     59    /**
     60     * A helper for {@link #drawOSDText(Graphics2D)}
    5361     */
    5462    private final OSDLabel lblOSD = new OSDLabel("");
     
    5664    /**
    5765     * Constructs a new {@code FilterTableModel}.
    58      */
    59     public FilterTableModel() {
     66     * @param listSelectionModel selection model
     67     */
     68    public FilterTableModel(ListSelectionModel listSelectionModel) {
     69        this.selectionModel = listSelectionModel;
    6070        loadPrefs();
    6171    }
     
    134144    }
    135145
     146    @Override
     147    public boolean doMove(int delta, int... selectedRows) {
     148        return model.moveFilters(delta, selectedRows);
     149    }
     150
     151    @Override
     152    public boolean move(int delta, int... selectedRows) {
     153        if (!SortableTableModel.super.move(delta, selectedRows))
     154            return false;
     155        savePrefs();
     156        updateFilters();
     157        int rowIndex = selectedRows[0];
     158        if (delta < 0)
     159            fireTableRowsUpdated(rowIndex + delta, rowIndex);
     160        else if (delta > 0)
     161            fireTableRowsUpdated(rowIndex, rowIndex + delta);
     162        return true;
     163    }
     164
    136165    /**
    137166     * Moves down the filter in the given row.
    138167     * @param rowIndex The filter row
    139      */
     168     * @deprecated Use {@link #moveDown(int...)}
     169     */
     170    @Deprecated
    140171    public void moveDownFilter(int rowIndex) {
    141         if (model.moveDownFilter(rowIndex)) {
    142             savePrefs();
    143             updateFilters();
    144             fireTableRowsUpdated(rowIndex, rowIndex + 1);
    145         }
     172        moveDown(rowIndex);
    146173    }
    147174
     
    149176     * Moves up the filter in the given row
    150177     * @param rowIndex The filter row
    151      */
     178     * @deprecated Use {@link #moveUp(int...)}
     179     */
     180    @Deprecated
    152181    public void moveUpFilter(int rowIndex) {
    153         if (model.moveUpFilter(rowIndex)) {
    154             savePrefs();
    155             updateFilters();
    156             fireTableRowsUpdated(rowIndex - 1, rowIndex);
    157         }
     182        moveUp(rowIndex);
    158183    }
    159184
     
    174199     * @param rowIndex The row index
    175200     * @param filter The filter that should be placed in that row
    176      */
     201     * @deprecated Use {@link #setValue}
     202     */
     203    @Deprecated
    177204    public void setFilter(int rowIndex, Filter filter) {
    178         model.setFilter(rowIndex, filter);
     205        setValue(rowIndex, filter);
     206    }
     207
     208    @Override
     209    public Filter setValue(int rowIndex, Filter filter) {
     210        Filter result = model.setValue(rowIndex, filter);
    179211        savePrefs();
    180212        updateFilters();
    181213        fireTableRowsUpdated(rowIndex, rowIndex);
     214        return result;
    182215    }
    183216
     
    186219     * @param rowIndex The row index
    187220     * @return The filter in that row
    188      */
     221     * @deprecated Use {@link #getValue}
     222     */
     223    @Deprecated
    189224    public Filter getFilter(int rowIndex) {
    190         return model.getFilter(rowIndex);
     225        return getValue(rowIndex);
     226    }
     227
     228    @Override
     229    public Filter getValue(int rowIndex) {
     230        return model.getValue(rowIndex);
     231    }
     232
     233    @Override
     234    public ListSelectionModel getSelectionModel() {
     235        return selectionModel;
    191236    }
    192237
     
    225270     */
    226271    public boolean isCellEnabled(int row, int column) {
    227         return model.getFilter(row).enable || column == 0;
     272        return model.getValue(row).enable || column == 0;
    228273    }
    229274
     
    238283            return;
    239284        }
    240         Filter f = model.getFilter(row);
     285        Filter f = model.getValue(row);
    241286        switch (column) {
    242287        case COL_ENABLED:
     
    254299        default: // Do nothing
    255300        }
    256         setFilter(row, f);
     301        setValue(row, f);
    257302    }
    258303
     
    262307            return null;
    263308        }
    264         Filter f = model.getFilter(row);
     309        Filter f = model.getValue(row);
    265310        switch (column) {
    266311        case COL_ENABLED:
     
    308353        return model.getFilters();
    309354    }
     355
     356    @Override
     357    public void sort() {
     358        model.sort();
     359        fireTableDataChanged();
     360    }
     361
     362    @Override
     363    public void reverse() {
     364        model.reverse();
     365        fireTableDataChanged();
     366    }
    310367}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java

    r14993 r15226  
    1717import java.util.ArrayList;
    1818import java.util.Arrays;
    19 import java.util.Collections;
    2019import java.util.List;
    2120import java.util.Objects;
     
    7473import org.openstreetmap.josm.gui.util.MultikeyActionsHandler;
    7574import org.openstreetmap.josm.gui.util.MultikeyShortcutAction.MultikeyInfo;
     75import org.openstreetmap.josm.gui.util.ReorderableTableModel;
     76import org.openstreetmap.josm.gui.util.TableHelper;
    7677import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
    7778import org.openstreetmap.josm.gui.widgets.JosmTextField;
     
    7980import org.openstreetmap.josm.gui.widgets.ScrollableTable;
    8081import org.openstreetmap.josm.spi.preferences.Config;
     82import org.openstreetmap.josm.tools.ArrayUtils;
    8183import org.openstreetmap.josm.tools.ImageProvider;
    8284import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
     
    268270        // -- move up action
    269271        MoveUpAction moveUpAction = new MoveUpAction(model);
    270         adaptTo(moveUpAction, model);
    271         adaptTo(moveUpAction, selectionModel);
     272        TableHelper.adaptTo(moveUpAction, model);
     273        TableHelper.adaptTo(moveUpAction, selectionModel);
    272274
    273275        // -- move down action
    274276        MoveDownAction moveDownAction = new MoveDownAction(model);
    275         adaptTo(moveDownAction, model);
    276         adaptTo(moveDownAction, selectionModel);
     277        TableHelper.adaptTo(moveDownAction, model);
     278        TableHelper.adaptTo(moveDownAction, selectionModel);
    277279
    278280        // -- activate action
     
    280282        activateLayerAction.updateEnabledState();
    281283        MultikeyActionsHandler.getInstance().addAction(activateLayerAction);
    282         adaptTo(activateLayerAction, selectionModel);
     284        TableHelper.adaptTo(activateLayerAction, selectionModel);
    283285
    284286        JumpToMarkerActions.initialize();
     
    287289        showHideLayerAction = new ShowHideLayerAction(model);
    288290        MultikeyActionsHandler.getInstance().addAction(showHideLayerAction);
    289         adaptTo(showHideLayerAction, selectionModel);
     291        TableHelper.adaptTo(showHideLayerAction, selectionModel);
    290292
    291293        LayerVisibilityAction visibilityAction = new LayerVisibilityAction(model);
    292         adaptTo(visibilityAction, selectionModel);
     294        TableHelper.adaptTo(visibilityAction, selectionModel);
    293295        SideButton visibilityButton = new SideButton(visibilityAction, false);
    294296        visibilityAction.setCorrespondingSideButton(visibilityButton);
     
    297299        DeleteLayerAction deleteLayerAction = new DeleteLayerAction(model);
    298300        layerList.getActionMap().put("deleteLayer", deleteLayerAction);
    299         adaptTo(deleteLayerAction, selectionModel);
     301        TableHelper.adaptTo(deleteLayerAction, selectionModel);
    300302        getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
    301303                KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete"
     
    365367     * @param listener  the listener
    366368     * @param listSelectionModel  the source emitting {@link ListSelectionEvent}s
    367      */
     369     * @deprecated Use {@link TableHelper#adaptTo}
     370     */
     371    @Deprecated
    368372    protected void adaptTo(final IEnabledStateUpdating listener, ListSelectionModel listSelectionModel) {
    369         listSelectionModel.addListSelectionListener(e -> listener.updateEnabledState());
     373        TableHelper.adaptTo(listener, listSelectionModel);
    370374    }
    371375
     
    377381     * @param listener the listener
    378382     * @param listModel the source emitting {@link ListDataEvent}s
    379      */
     383     * @deprecated Use {@link TableHelper#adaptTo}
     384     */
     385    @Deprecated
    380386    protected void adaptTo(final IEnabledStateUpdating listener, LayerListModel listModel) {
    381         listModel.addTableModelListener(e -> listener.updateEnabledState());
     387        TableHelper.adaptTo(listener, listModel);
    382388    }
    383389
     
    679685     */
    680686    public static final class LayerListModel extends AbstractTableModel
    681             implements LayerChangeListener, ActiveLayerChangeListener, PropertyChangeListener {
     687            implements LayerChangeListener, ActiveLayerChangeListener, PropertyChangeListener, ReorderableTableModel<Layer> {
    682688        /** manages list selection state*/
    683689        private final DefaultListSelectionModel selectionModel;
     
    801807         */
    802808        public List<Integer> getSelectedRows() {
    803             List<Integer> selected = new ArrayList<>();
    804             for (int i = 0; i < getLayers().size(); i++) {
    805                 if (selectionModel.isSelectedIndex(i)) {
    806                     selected.add(i);
    807                 }
    808             }
    809             return selected;
     809            return ArrayUtils.toList(TableHelper.getSelectedIndices(selectionModel));
    810810        }
    811811
     
    820820            layer.removePropertyChangeListener(this);
    821821            final int size = getRowCount();
    822             final List<Integer> rows = getSelectedRows();
    823 
    824             if (rows.isEmpty() && size > 0) {
     822            final int[] rows = TableHelper.getSelectedIndices(selectionModel);
     823
     824            if (rows.length == 0 && size > 0) {
    825825                selectionModel.setSelectionInterval(size-1, size-1);
    826826            }
     
    876876        }
    877877
    878         /**
    879          * Replies true if the currently selected layers can move up by one position
    880          *
    881          * @return true if the currently selected layers can move up by one position
    882          */
    883         public boolean canMoveUp() {
    884             List<Integer> sel = getSelectedRows();
    885             return !sel.isEmpty() && sel.get(0) > 0;
    886         }
    887 
    888         /**
    889          * Move up the currently selected layers by one position
    890          *
    891          */
    892         public void moveUp() {
    893             if (!canMoveUp())
    894                 return;
    895             List<Integer> sel = getSelectedRows();
    896             List<Layer> layers = getLayers();
    897             MapView mapView = MainApplication.getMap().mapView;
    898             for (int row : sel) {
    899                 Layer l1 = layers.get(row);
    900                 mapView.moveLayer(l1, row-1);
    901             }
    902             fireTableDataChanged();
    903             selectionModel.setValueIsAdjusting(true);
    904             selectionModel.clearSelection();
    905             for (int row : sel) {
    906                 selectionModel.addSelectionInterval(row-1, row-1);
    907             }
    908             selectionModel.setValueIsAdjusting(false);
     878        @Override
     879        public DefaultListSelectionModel getSelectionModel() {
     880            return selectionModel;
     881        }
     882
     883        @Override
     884        public Layer getValue(int index) {
     885            return getLayer(index);
     886        }
     887
     888        @Override
     889        public Layer setValue(int index, Layer value) {
     890            throw new UnsupportedOperationException();
     891        }
     892
     893        @Override
     894        public boolean doMove(int delta, int... selectedRows) {
     895            if (delta != 0) {
     896                List<Layer> layers = getLayers();
     897                MapView mapView = MainApplication.getMap().mapView;
     898                if (delta < 0) {
     899                    for (int row : selectedRows) {
     900                        mapView.moveLayer(layers.get(row), row + delta);
     901                    }
     902                } else if (delta > 0) {
     903                    for (int i = selectedRows.length - 1; i >= 0; i--) {
     904                        mapView.moveLayer(layers.get(selectedRows[i]), selectedRows[i] + delta);
     905                    }
     906                }
     907                fireTableDataChanged();
     908            }
     909            return delta != 0;
     910        }
     911
     912        @Override
     913        public boolean move(int delta, int... selectedRows) {
     914            if (!ReorderableTableModel.super.move(delta, selectedRows))
     915                return false;
    909916            ensureSelectedIsVisible();
    910         }
    911 
    912         /**
    913          * Replies true if the currently selected layers can move down by one position
    914          *
    915          * @return true if the currently selected layers can move down by one position
    916          */
    917         public boolean canMoveDown() {
    918             List<Integer> sel = getSelectedRows();
    919             return !sel.isEmpty() && sel.get(sel.size()-1) < getLayers().size()-1;
    920         }
    921 
    922         /**
    923          * Move down the currently selected layers by one position
    924          */
    925         public void moveDown() {
    926             if (!canMoveDown())
    927                 return;
    928             List<Integer> sel = getSelectedRows();
    929             Collections.reverse(sel);
    930             List<Layer> layers = getLayers();
    931             MapView mapView = MainApplication.getMap().mapView;
    932             for (int row : sel) {
    933                 Layer l1 = layers.get(row);
    934                 mapView.moveLayer(l1, row+1);
    935             }
    936             fireTableDataChanged();
    937             selectionModel.setValueIsAdjusting(true);
    938             selectionModel.clearSelection();
    939             for (int row : sel) {
    940                 selectionModel.addSelectionInterval(row+1, row+1);
    941             }
    942             selectionModel.setValueIsAdjusting(false);
    943             ensureSelectedIsVisible();
     917            return true;
    944918        }
    945919
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java

    r14339 r15226  
    33
    44import java.util.ArrayList;
    5 import java.util.Arrays;
    65import java.util.BitSet;
    76import java.util.Collection;
     
    4544import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
    4645import org.openstreetmap.josm.gui.util.GuiHelper;
     46import org.openstreetmap.josm.gui.util.SortableTableModel;
    4747import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTableModel;
     48import org.openstreetmap.josm.tools.ArrayUtils;
    4849import org.openstreetmap.josm.tools.JosmRuntimeException;
    4950import org.openstreetmap.josm.tools.bugreport.BugReport;
     
    5354 */
    5455public class MemberTableModel extends AbstractTableModel
    55 implements TableModelListener, DataSelectionListener, DataSetListener, OsmPrimitivesTableModel {
     56implements TableModelListener, DataSelectionListener, DataSetListener, OsmPrimitivesTableModel, SortableTableModel<RelationMember> {
    5657
    5758    /**
     
    181182    /* --------------------------------------------------------------------------- */
    182183
     184    /**
     185     * Add a new member model listener.
     186     * @param listener member model listener to add
     187     */
    183188    public void addMemberModelListener(IMemberModelListener listener) {
    184189        if (listener != null) {
     
    187192    }
    188193
     194    /**
     195     * Remove a member model listener.
     196     * @param listener member model listener to remove
     197     */
    189198    public void removeMemberModelListener(IMemberModelListener listener) {
    190199        listeners.remove(listener);
     
    260269    }
    261270
    262     /**
    263      * Move up selected rows, if possible.
    264      * @param selectedRows rows to move up
    265      * @see #canMoveUp
    266      */
    267     public void moveUp(int... selectedRows) {
    268         if (!canMoveUp(selectedRows))
    269             return;
    270 
    271         for (int row : selectedRows) {
    272             RelationMember member1 = members.get(row);
    273             RelationMember member2 = members.get(row - 1);
    274             members.set(row, member2);
    275             members.set(row - 1, member1);
    276         }
     271    @Override
     272    public boolean move(int delta, int... selectedRows) {
     273        if (!canMove(delta, this::getRowCount, selectedRows))
     274            return false;
     275        doMove(delta, selectedRows);
    277276        fireTableDataChanged();
    278         getSelectionModel().setValueIsAdjusting(true);
    279         getSelectionModel().clearSelection();
     277        final ListSelectionModel selectionModel = getSelectionModel();
     278        selectionModel.setValueIsAdjusting(true);
     279        selectionModel.clearSelection();
    280280        BitSet selected = new BitSet();
    281281        for (int row : selectedRows) {
     
    284284        }
    285285        addToSelectedMembers(selected);
    286         getSelectionModel().setValueIsAdjusting(false);
    287         fireMakeMemberVisible(selectedRows[0] - 1);
    288     }
    289 
    290     /**
    291      * Move down selected rows, if possible.
    292      * @param selectedRows rows to move down
    293      * @see #canMoveDown
    294      */
    295     public void moveDown(int... selectedRows) {
    296         if (!canMoveDown(selectedRows))
    297             return;
    298 
    299         for (int i = selectedRows.length - 1; i >= 0; i--) {
    300             int row = selectedRows[i];
    301             RelationMember member1 = members.get(row);
    302             RelationMember member2 = members.get(row + 1);
    303             members.set(row, member2);
    304             members.set(row + 1, member1);
    305         }
    306         fireTableDataChanged();
    307         getSelectionModel();
    308         getSelectionModel().setValueIsAdjusting(true);
    309         getSelectionModel().clearSelection();
    310         BitSet selected = new BitSet();
    311         for (int row : selectedRows) {
    312             row++;
    313             selected.set(row);
    314         }
    315         addToSelectedMembers(selected);
    316         getSelectionModel().setValueIsAdjusting(false);
    317         fireMakeMemberVisible(selectedRows[0] + 1);
     286        selectionModel.setValueIsAdjusting(false);
     287        fireMakeMemberVisible(selectedRows[0] + delta);
     288        return true;
    318289    }
    319290
     
    338309
    339310    /**
    340      * Checks that a range of rows can be moved up.
    341      * @param rows indexes of rows to move up
    342      * @return {@code true} if rows can be moved up
    343      */
    344     public boolean canMoveUp(int... rows) {
    345         if (rows == null || rows.length == 0)
    346             return false;
    347         Arrays.sort(rows);
    348         return rows[0] > 0 && rows[rows.length - 1] < members.size();
    349     }
    350 
    351     /**
    352      * Checks that a range of rows can be moved down.
    353      * @param rows indexes of rows to move down
    354      * @return {@code true} if rows can be moved down
    355      */
    356     public boolean canMoveDown(int... rows) {
    357         if (rows == null || rows.length == 0)
    358             return false;
    359         Arrays.sort(rows);
    360         return rows[0] >= 0 && rows[rows.length - 1] < members.size() - 1;
    361     }
    362 
    363     /**
    364311     * Checks that a range of rows can be removed.
    365312     * @param rows indexes of rows to remove
     
    370317    }
    371318
    372     /**
    373      * Returns the selection model.
    374      * @return the selection model (never null)
    375      */
     319    @Override
    376320    public DefaultListSelectionModel getSelectionModel() {
    377321        if (listSelectionModel == null) {
     
    382326    }
    383327
     328    @Override
     329    public RelationMember getValue(int index) {
     330        return members.get(index);
     331    }
     332
     333    @Override
     334    public RelationMember setValue(int index, RelationMember value) {
     335        return members.set(index, value);
     336    }
     337
     338    /**
     339     * Remove members referring to the given list of primitives.
     340     * @param primitives list of OSM primitives
     341     */
    384342    public void removeMembersReferringTo(List<? extends OsmPrimitive> primitives) {
    385343        if (primitives == null)
    386344            return;
    387         members.removeIf(member -> primitives.contains(member.getMember()));
    388         fireTableDataChanged();
     345        if (members.removeIf(member -> primitives.contains(member.getMember())))
     346            fireTableDataChanged();
    389347    }
    390348
     
    398356    }
    399357
     358    /**
     359     * Determines if this model has the same members as the given relation.
     360     * @param relation relation
     361     * @return {@code true} if this model has the same members as {@code relation}
     362     */
    400363    public boolean hasSameMembersAs(Relation relation) {
    401         if (relation == null)
    402             return false;
    403         if (relation.getMembersCount() != members.size())
     364        if (relation == null || relation.getMembersCount() != members.size())
    404365            return false;
    405366        for (int i = 0; i < relation.getMembersCount(); i++) {
     
    464425        }
    465426        return false;
    466     }
    467 
    468     protected List<Integer> getSelectedIndices() {
    469         List<Integer> selectedIndices = new ArrayList<>();
    470         for (int i = 0; i < members.size(); i++) {
    471             if (getSelectionModel().isSelectedIndex(i)) {
    472                 selectedIndices.add(i);
    473             }
    474         }
    475         return selectedIndices;
    476427    }
    477428
     
    773724        addToSelectedMembers(selected);
    774725        getSelectionModel().setValueIsAdjusting(false);
    775         if (!getSelectedIndices().isEmpty()) {
    776             fireMakeMemberVisible(getSelectedIndices().get(0));
     726        int[] selectedIndices = getSelectedIndices();
     727        if (selectedIndices.length > 0) {
     728            fireMakeMemberVisible(selectedIndices[0]);
    777729        }
    778730    }
     
    793745     * Sort the selected relation members by the way they are linked.
    794746     */
     747    @Override
    795748    public void sort() {
    796749        List<RelationMember> selectedMembers = new ArrayList<>(getSelectedMembers());
     
    802755        } else {
    803756            sortedMembers = relationSorter.sortMembers(selectedMembers);
    804             List<Integer> selectedIndices = getSelectedIndices();
     757            List<Integer> selectedIndices = ArrayUtils.toList(getSelectedIndices());
    805758            newMembers = new ArrayList<>();
    806759            boolean inserted = false;
     
    861814     * Reverse the relation members.
    862815     */
     816    @Override
    863817    public void reverse() {
    864         List<Integer> selectedIndices = getSelectedIndices();
    865         List<Integer> selectedIndicesReversed = getSelectedIndices();
     818        List<Integer> selectedIndices = ArrayUtils.toList(getSelectedIndices());
     819        List<Integer> selectedIndicesReversed = ArrayUtils.toList(getSelectedIndices());
    866820
    867821        if (selectedIndices.size() <= 1) {
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ReverseAction.java

    r14030 r15226  
    2323
    2424        putValue(SHORT_DESCRIPTION, tr("Reverse the order of the relation members"));
    25         new ImageProvider("dialogs/relation", "reverse").getResource().attachImageIcon(this, true);
     25        new ImageProvider("dialogs/", "reverse").getResource().attachImageIcon(this, true);
    2626        putValue(NAME, tr("Reverse"));
    2727        updateEnabledState();
  • trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java

    r15214 r15226  
    8888import org.openstreetmap.josm.gui.util.FileFilterAllFiles;
    8989import org.openstreetmap.josm.gui.util.GuiHelper;
     90import org.openstreetmap.josm.gui.util.ReorderableTableModel;
    9091import org.openstreetmap.josm.gui.util.TableHelper;
    9192import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
     
    627628     * Table model of active sources.
    628629     */
    629     protected class ActiveSourcesModel extends AbstractTableModel {
     630    protected class ActiveSourcesModel extends AbstractTableModel implements ReorderableTableModel<SourceEntry> {
    630631        private transient List<SourceEntry> data;
    631632        private final DefaultListSelectionModel selectionModel;
     
    767768        }
    768769
    769         public boolean canMove(int i) {
    770             int[] sel = tblActiveSources.getSelectedRows();
    771             if (sel.length == 0)
    772                 return false;
    773             if (i < 0)
    774                 return sel[0] >= -i;
    775                 else if (i > 0)
    776                     return sel[sel.length-1] <= getRowCount()-1 - i;
    777                 else
    778                     return true;
    779         }
    780 
    781         public void move(int i) {
    782             if (!canMove(i)) return;
    783             int[] sel = tblActiveSources.getSelectedRows();
    784             for (int row: sel) {
    785                 SourceEntry t1 = data.get(row);
    786                 SourceEntry t2 = data.get(row + i);
    787                 data.set(row, t2);
    788                 data.set(row + i, t1);
    789             }
    790             selectionModel.setValueIsAdjusting(true);
    791             selectionModel.clearSelection();
    792             for (int row: sel) {
    793                 selectionModel.addSelectionInterval(row + i, row + i);
    794             }
    795             selectionModel.setValueIsAdjusting(false);
     770        @Override
     771        public DefaultListSelectionModel getSelectionModel() {
     772            return selectionModel;
     773        }
     774
     775        @Override
     776        public SourceEntry getValue(int index) {
     777            return data.get(index);
     778        }
     779
     780        @Override
     781        public SourceEntry setValue(int index, SourceEntry value) {
     782            return data.set(index, value);
    796783        }
    797784    }
  • trunk/src/org/openstreetmap/josm/gui/util/TableHelper.java

    r15177 r15226  
    55
    66import javax.swing.JTable;
     7import javax.swing.ListSelectionModel;
     8import javax.swing.event.ListDataEvent;
     9import javax.swing.event.ListSelectionEvent;
     10import javax.swing.table.AbstractTableModel;
    711import javax.swing.table.TableCellRenderer;
    812import javax.swing.table.TableColumn;
     13
     14import org.openstreetmap.josm.gui.dialogs.IEnabledStateUpdating;
    915
    1016/**
     
    1622    private TableHelper() {
    1723        // Hide default constructor for utils classes
     24    }
     25
     26    /**
     27     * Wires <code>listener</code> to <code>listSelectionModel</code> in such a way, that
     28     * <code>listener</code> receives a {@link IEnabledStateUpdating#updateEnabledState()}
     29     * on every {@link ListSelectionEvent}.
     30     *
     31     * @param listener  the listener
     32     * @param listSelectionModel  the source emitting {@link ListSelectionEvent}s
     33     * @since 15226
     34     */
     35    public static void adaptTo(final IEnabledStateUpdating listener, ListSelectionModel listSelectionModel) {
     36        listSelectionModel.addListSelectionListener(e -> listener.updateEnabledState());
     37    }
     38
     39    /**
     40     * Wires <code>listener</code> to <code>listModel</code> in such a way, that
     41     * <code>listener</code> receives a {@link IEnabledStateUpdating#updateEnabledState()}
     42     * on every {@link ListDataEvent}.
     43     *
     44     * @param listener the listener
     45     * @param listModel the source emitting {@link ListDataEvent}s
     46     * @since 15226
     47     */
     48    public static void adaptTo(final IEnabledStateUpdating listener, AbstractTableModel listModel) {
     49        listModel.addTableModelListener(e -> listener.updateEnabledState());
    1850    }
    1951
     
    83115        }
    84116    }
     117
     118    /**
     119     * Returns an array of all of the selected indices in the selection model, in increasing order.
     120     * Unfortunately this method is not available in OpenJDK before version 11, see
     121     * https://bugs.openjdk.java.net/browse/JDK-8199395
     122     * Code taken from OpenJDK 11. To be removed when we switch to Java 11 or later.
     123     *
     124     * @param selectionModel list selection model.
     125     *
     126     * @return all of the selected indices, in increasing order,
     127     *         or an empty array if nothing is selected
     128     * @since 15226
     129     */
     130    public static int[] getSelectedIndices(ListSelectionModel selectionModel) {
     131        int iMin = selectionModel.getMinSelectionIndex();
     132        int iMax = selectionModel.getMaxSelectionIndex();
     133
     134        if (iMin < 0 || iMax < 0) {
     135            return new int[0];
     136        }
     137
     138        int[] rvTmp = new int[1 + iMax - iMin];
     139        int n = 0;
     140        for (int i = iMin; i <= iMax; i++) {
     141            if (selectionModel.isSelectedIndex(i)) {
     142                rvTmp[n++] = i;
     143            }
     144        }
     145        int[] rv = new int[n];
     146        System.arraycopy(rvTmp, 0, rv, 0, n);
     147        return rv;
     148    }
    85149}
Note: See TracChangeset for help on using the changeset viewer.