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