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