001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.changeset;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.BorderLayout;
007import java.awt.Component;
008import java.awt.FlowLayout;
009import java.awt.Rectangle;
010import java.awt.event.ActionEvent;
011import java.beans.PropertyChangeEvent;
012import java.beans.PropertyChangeListener;
013import java.util.Collections;
014
015import javax.swing.AbstractAction;
016import javax.swing.BorderFactory;
017import javax.swing.JPanel;
018import javax.swing.JScrollPane;
019import javax.swing.JTable;
020import javax.swing.JToolBar;
021
022import org.openstreetmap.josm.Main;
023import org.openstreetmap.josm.data.osm.Changeset;
024import org.openstreetmap.josm.io.OnlineResource;
025
026/**
027 * The panel which displays the public discussion around a changeset in a scrollable table.
028 *
029 * It listens to property change events for {@link ChangesetCacheManagerModel#CHANGESET_IN_DETAIL_VIEW_PROP}
030 * and updates its view accordingly.
031 *
032 * @since 7704
033 */
034public class ChangesetDiscussionPanel extends JPanel implements PropertyChangeListener {
035
036    private final UpdateChangesetDiscussionAction actUpdateChangesets = new UpdateChangesetDiscussionAction();
037
038    private final ChangesetDiscussionTableModel model = new ChangesetDiscussionTableModel();
039
040    private JTable table;
041
042    private transient Changeset current;
043
044    protected JPanel buildActionButtonPanel() {
045        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
046
047        JToolBar tb = new JToolBar(JToolBar.VERTICAL);
048        tb.setFloatable(false);
049
050        // -- changeset discussion update
051        tb.add(actUpdateChangesets);
052        actUpdateChangesets.initProperties(current);
053
054        pnl.add(tb);
055        return pnl;
056    }
057
058    /**
059     * Updates the current changeset discussion from the OSM server
060     */
061    class UpdateChangesetDiscussionAction extends AbstractAction {
062        UpdateChangesetDiscussionAction() {
063            putValue(NAME, tr("Update changeset discussion"));
064            putValue(SMALL_ICON, ChangesetCacheManager.UPDATE_CONTENT_ICON);
065            putValue(SHORT_DESCRIPTION, tr("Update the changeset discussion from the OSM server"));
066        }
067
068        @Override
069        public void actionPerformed(ActionEvent evt) {
070            if (current == null) return;
071            Main.worker.submit(
072                    new ChangesetHeaderDownloadTask(
073                            ChangesetDiscussionPanel.this,
074                            Collections.singleton(current.getId()),
075                            true /* include discussion */
076                    )
077            );
078        }
079
080        public void initProperties(Changeset cs) {
081            setEnabled(cs != null && !Main.isOffline(OnlineResource.OSM_API));
082        }
083    }
084
085    /**
086     * Constructs a new {@code ChangesetDiscussionPanel}.
087     */
088    public ChangesetDiscussionPanel() {
089        build();
090    }
091
092    protected void setCurrentChangeset(Changeset cs) {
093        current = cs;
094        if (cs == null) {
095            clearView();
096        } else {
097            updateView(cs);
098        }
099        actUpdateChangesets.initProperties(current);
100        if (cs != null && cs.getDiscussion().size() < cs.getCommentsCount()) {
101            actUpdateChangesets.actionPerformed(null);
102        }
103    }
104
105    protected final void build() {
106        setLayout(new BorderLayout());
107        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
108        add(buildActionButtonPanel(), BorderLayout.WEST);
109        add(buildDiscussionPanel(), BorderLayout.CENTER);
110    }
111
112    private Component buildDiscussionPanel() {
113        JPanel pnl = new JPanel(new BorderLayout());
114        table = new JTable(model, new ChangesetDiscussionTableColumnModel());
115        table.getColumnModel().getColumn(2).addPropertyChangeListener(new PropertyChangeListener() {
116            @Override
117            public void propertyChange(PropertyChangeEvent evt) {
118                if ("width".equals(evt.getPropertyName())) {
119                    updateRowHeights();
120                }
121            }
122        });
123        pnl.add(new JScrollPane(table), BorderLayout.CENTER);
124        return pnl;
125    }
126
127    protected void clearView() {
128        model.populate(null);
129    }
130
131    protected void updateView(Changeset cs) {
132        model.populate(cs.getDiscussion());
133        updateRowHeights();
134    }
135
136    protected void updateRowHeights() {
137        int intercellWidth = table.getIntercellSpacing().width;
138        int colWidth = table.getColumnModel().getColumn(2).getWidth();
139        // Update row heights
140        for (int row = 0; row < table.getRowCount(); row++) {
141            int rowHeight = table.getRowHeight();
142
143            Component comp = table.prepareRenderer(table.getCellRenderer(row, 2), row, 2);
144            // constrain width of component
145            comp.setBounds(new Rectangle(0, 0, colWidth - intercellWidth, Integer.MAX_VALUE));
146            rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
147
148            table.setRowHeight(row, rowHeight);
149        }
150    }
151
152    /* ---------------------------------------------------------------------------- */
153    /* interface PropertyChangeListener                                             */
154    /* ---------------------------------------------------------------------------- */
155    @Override
156    public void propertyChange(PropertyChangeEvent evt) {
157        if (!evt.getPropertyName().equals(ChangesetCacheManagerModel.CHANGESET_IN_DETAIL_VIEW_PROP))
158            return;
159        setCurrentChangeset((Changeset) evt.getNewValue());
160    }
161}