001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.conflict.pair;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.GridBagLayout;
007import java.util.List;
008
009import javax.swing.AbstractAction;
010import javax.swing.JButton;
011import javax.swing.JComponent;
012import javax.swing.JLabel;
013import javax.swing.JPanel;
014
015import org.openstreetmap.josm.tools.GBC;
016
017/**
018 * A panel used as tab in the merge dialog.
019 * Contains helper methods for creating merge dialog columns.
020 *
021 * @author Michael Zangl
022 * @since 12044
023 */
024public abstract class AbstractMergePanel extends JPanel {
025
026    /**
027     * A helper class to add a row to the layout. Each row has 6 columns.
028     * @author Michael Zangl
029     */
030    protected static class MergeRow {
031        protected int marginTop = 5;
032
033        public MergeRow() {
034            // allow access from subclasses
035        }
036
037        protected JComponent rowTitle() {
038            return null;
039        }
040
041        protected JComponent mineField() {
042            return null;
043        }
044
045        protected JComponent mineButton() {
046            return null;
047        }
048
049        protected JComponent merged() {
050            return null;
051        }
052
053        protected JComponent theirsButton() {
054            return null;
055        }
056
057        protected JComponent theirsField() {
058            return null;
059        }
060
061        void addTo(AbstractMergePanel panel) {
062            JComponent[] buttons = getColumns();
063            for (int columnIndex = 0; columnIndex < buttons.length; columnIndex++) {
064                if (buttons[columnIndex] != null) {
065                    GBC constraints = GBC.std(columnIndex, panel.currentRow);
066                    addConstraints(constraints, columnIndex);
067                    panel.add(buttons[columnIndex], constraints);
068                }
069            }
070            panel.currentRow++;
071        }
072
073        protected JComponent[] getColumns() {
074            return new JComponent[] {
075                    rowTitle(),
076                    mineField(),
077                    mineButton(),
078                    merged(),
079                    theirsButton(),
080                    theirsField()
081            };
082        }
083
084        protected void addConstraints(GBC constraints, int columnIndex) {
085            constraints.anchor(GBC.CENTER);
086            constraints.fill = GBC.BOTH;
087            constraints.weight(0, 0);
088            constraints.insets(3, marginTop, 3, 0);
089            if (columnIndex == 1 || columnIndex == 3 || columnIndex == 5) {
090                // resize those rows
091                constraints.weightx = 1;
092            }
093        }
094    }
095
096    /**
097     * A row that does not contain the merge buttons. Fields in this row fill both the button and filed area.
098     */
099    protected static class MergeRowWithoutButton extends MergeRow {
100        @Override
101        protected JComponent[] getColumns() {
102            return new JComponent[] {
103                    rowTitle(),
104                    mineField(), // width: 2
105                    null,
106                    merged(),
107                    theirsField(), // width: 2
108                    null,
109            };
110        }
111
112        @Override
113        protected void addConstraints(GBC constraints, int columnIndex) {
114            super.addConstraints(constraints, columnIndex);
115
116            if (columnIndex == 1 || columnIndex == 4) {
117                constraints.gridwidth = 2;
118            }
119        }
120    }
121
122    /**
123     * The title for the rows (mine, merged, theirs)
124     */
125    protected static class TitleRow extends MergeRow {
126        public TitleRow() {
127            // allow access from subclasses
128        }
129
130        @Override
131        protected JComponent mineField() {
132            JLabel label = new JLabel(tr("My version (local dataset)"));
133            label.setToolTipText(tr("Properties in my dataset, i.e. the local dataset"));
134            label.setHorizontalAlignment(JLabel.CENTER);
135            return label;
136        }
137
138        @Override
139        protected JComponent merged() {
140            JLabel label = new JLabel(tr("Merged version"));
141            label.setToolTipText(
142                    tr("Properties in the merged element. They will replace properties in my elements when merge decisions are applied."));
143            label.setHorizontalAlignment(JLabel.CENTER);
144            return label;
145        }
146
147        @Override
148        protected JComponent theirsField() {
149            JLabel label = new JLabel(tr("Their version (server dataset)"));
150            label.setToolTipText(tr("Properties in their dataset, i.e. the server dataset"));
151            label.setHorizontalAlignment(JLabel.CENTER);
152            return label;
153        }
154    }
155
156    /**
157     * Add the undecide button to the middle of the merged row.
158     */
159    protected abstract static class AbstractUndecideRow extends AbstractMergePanel.MergeRow {
160        @Override
161        protected JComponent merged() {
162            AbstractAction actUndecide = createAction();
163            JButton button = new JButton(actUndecide);
164            button.setName(getButtonName());
165            return button;
166        }
167
168        protected abstract AbstractAction createAction();
169
170        protected abstract String getButtonName();
171
172        @Override
173        protected void addConstraints(GBC constraints, int columnIndex) {
174            super.addConstraints(constraints, columnIndex);
175            constraints.fill(GBC.NONE);
176        }
177    }
178
179    /**
180     * The current row counter. Used when adding new rows.
181     */
182    protected int currentRow;
183
184    /**
185     * Create a new merge panel.
186     */
187    protected AbstractMergePanel() {
188        super(new GridBagLayout());
189    }
190
191    /**
192     * Add the rows to this component.
193     * This needs to be called in the constructor of the child class. That way, all it's fields are initialized.
194     */
195    protected void buildRows() {
196        getRows().forEach(row -> row.addTo(this));
197    }
198
199    /**
200     * Gets the rows.
201     * @return A list of rows that should be displayed in this dialog.
202     */
203    protected abstract List<? extends MergeRow> getRows();
204
205}