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