001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.gui.dialogs;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.Component;
007    import java.awt.event.ActionEvent;
008    import java.awt.event.KeyEvent;
009    import java.awt.event.MouseAdapter;
010    import java.awt.event.MouseEvent;
011    import java.util.ArrayList;
012    import java.util.Arrays;
013    import java.util.Collection;
014    import java.util.Collections;
015    import java.util.List;
016    
017    import javax.swing.AbstractAction;
018    import javax.swing.Action;
019    import javax.swing.DefaultListSelectionModel;
020    import javax.swing.JComponent;
021    import javax.swing.JLabel;
022    import javax.swing.JTable;
023    import javax.swing.ListSelectionModel;
024    import javax.swing.event.ListSelectionEvent;
025    import javax.swing.event.ListSelectionListener;
026    import javax.swing.table.DefaultTableCellRenderer;
027    import javax.swing.table.DefaultTableColumnModel;
028    import javax.swing.table.DefaultTableModel;
029    import javax.swing.table.TableCellRenderer;
030    import javax.swing.table.TableColumn;
031    
032    import org.openstreetmap.josm.Main;
033    import org.openstreetmap.josm.data.SelectionChangedListener;
034    import org.openstreetmap.josm.data.osm.DataSet;
035    import org.openstreetmap.josm.data.osm.OsmPrimitive;
036    import org.openstreetmap.josm.data.osm.PrimitiveId;
037    import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
038    import org.openstreetmap.josm.data.osm.history.HistoryDataSetListener;
039    import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
040    import org.openstreetmap.josm.gui.SideButton;
041    import org.openstreetmap.josm.gui.help.HelpUtil;
042    import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
043    import org.openstreetmap.josm.gui.history.HistoryLoadTask;
044    import org.openstreetmap.josm.tools.ImageProvider;
045    import org.openstreetmap.josm.tools.InputMapUtils;
046    import org.openstreetmap.josm.tools.Shortcut;
047    
048    /**
049     * HistoryDialog displays a list of the currently selected primitives and provides
050     * two actions for (1) (re)loading the history of the selected primitives and (2)
051     * for launching a history browser for each selected primitive.
052     *
053     */
054    public class HistoryDialog extends ToggleDialog implements HistoryDataSetListener {
055    
056        /** the table model */
057        protected HistoryItemTableModel model;
058        /** the table with the history items */
059        protected JTable historyTable;
060    
061        protected ShowHistoryAction showHistoryAction;
062        protected ReloadAction reloadAction;
063    
064        public HistoryDialog() {
065            super(tr("History"), "history", tr("Display the history of all selected items."),
066                    Shortcut.registerShortcut("subwindow:history", tr("Toggle: {0}", tr("History")), KeyEvent.VK_H,
067                            Shortcut.ALT_SHIFT), 150);
068            build();
069            HelpUtil.setHelpContext(this, HelpUtil.ht("/Dialog/History"));
070        }
071    
072        /**
073         * builds the GUI
074         */
075        protected void build() {
076            DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
077            historyTable = new JTable(
078                    model = new HistoryItemTableModel(selectionModel),
079                    new HistoryTableColumnModel(),
080                    selectionModel
081            );
082            historyTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
083            final TableCellRenderer oldRenderer = historyTable.getTableHeader().getDefaultRenderer();
084            historyTable.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer(){
085                @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
086                    JComponent c = (JComponent)oldRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
087                    if (!"".equals(value))
088                        return c;
089                    JLabel l = new JLabel(ImageProvider.get("misc","showhide"));
090                    l.setForeground(c.getForeground());
091                    l.setBackground(c.getBackground());
092                    l.setFont(c.getFont());
093                    l.setBorder(c.getBorder());
094                    l.setOpaque(true);
095                    return l;
096                }
097            });
098            historyTable.addMouseListener(new ShowHistoryMouseAdapter());
099            historyTable.setTableHeader(null);
100    
101            createLayout(historyTable, true, Arrays.asList(new SideButton[] {
102                new SideButton(reloadAction = new ReloadAction()),
103                new SideButton(showHistoryAction = new ShowHistoryAction())
104            }));
105    
106            // wire actions
107            //
108            historyTable.getSelectionModel().addListSelectionListener(showHistoryAction);
109            historyTable.getSelectionModel().addListSelectionListener(reloadAction);
110            
111            // Show history dialog on Enter/Spacebar
112            InputMapUtils.addEnterAction(historyTable, showHistoryAction);
113            InputMapUtils.addSpacebarAction(historyTable, showHistoryAction);
114        }
115    
116        @Override
117        public void showNotify() {
118            HistoryDataSet.getInstance().addHistoryDataSetListener(this);
119            DataSet.addSelectionListener(model);
120            if (Main.main.getCurrentDataSet() == null) {
121                model.selectionChanged(null);
122            } else {
123                model.selectionChanged(Main.main.getCurrentDataSet().getAllSelected());
124            }
125        }
126    
127        @Override
128        public void hideNotify() {
129            HistoryDataSet.getInstance().removeHistoryDataSetListener(this);
130            DataSet.removeSelectionListener(model);
131        }
132    
133        /* ----------------------------------------------------------------------------- */
134        /* interface HistoryDataSetListener                                              */
135        /* ----------------------------------------------------------------------------- */
136        public void historyUpdated(HistoryDataSet source, PrimitiveId primitiveId) {
137            model.refresh();
138        }
139    
140        public void historyDataSetCleared(HistoryDataSet source) {
141            model.refresh();
142        }
143    
144        /**
145         * The table model with the history items
146         *
147         */
148        static class HistoryItemTableModel extends DefaultTableModel implements SelectionChangedListener{
149            private ArrayList<OsmPrimitive> data;
150            private DefaultListSelectionModel selectionModel;
151    
152            public HistoryItemTableModel(DefaultListSelectionModel selectionModel) {
153                data = new ArrayList<OsmPrimitive>();
154                this.selectionModel = selectionModel;
155            }
156    
157            @Override
158            public int getRowCount() {
159                if (data == null)
160                    return 0;
161                return data.size();
162            }
163    
164            @Override
165            public Object getValueAt(int row, int column) {
166                return data.get(row);
167            }
168    
169            @Override
170            public boolean isCellEditable(int row, int column) {
171                return false;
172            }
173    
174            protected List<OsmPrimitive> getSelectedPrimitives() {
175                ArrayList<OsmPrimitive> ret = new ArrayList<OsmPrimitive>();
176                for (int i=0; i< data.size(); i++) {
177                    if (selectionModel.isSelectedIndex(i)) {
178                        ret.add(data.get(i));
179                    }
180                }
181                return ret;
182            }
183    
184            protected void selectPrimitives(Collection<OsmPrimitive> primitives) {
185                for (OsmPrimitive p: primitives) {
186                    int idx = data.indexOf(p);
187                    if (idx < 0) {
188                        continue;
189                    }
190                    selectionModel.addSelectionInterval(idx, idx);
191                }
192            }
193    
194            public void refresh() {
195                List<OsmPrimitive> selectedPrimitives = getSelectedPrimitives();
196                data.clear();
197                if (Main.main.getCurrentDataSet() == null)
198                    return;
199                for (OsmPrimitive primitive: Main.main.getCurrentDataSet().getAllSelected()) {
200                    if (primitive.isNew()) {
201                        continue;
202                    }
203                    data.add(primitive);
204                }
205                fireTableDataChanged();
206                selectPrimitives(selectedPrimitives);
207            }
208    
209            public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
210                data.clear();
211                selectionModel.clearSelection();
212                if (newSelection != null && !newSelection.isEmpty()) {
213                    for (OsmPrimitive primitive: newSelection) {
214                        if (primitive.isNew()) {
215                            continue;
216                        }
217                        data.add(primitive);
218                    }
219                }
220                fireTableDataChanged();
221                selectionModel.addSelectionInterval(0, data.size()-1);
222            }
223    
224            public List<OsmPrimitive> getPrimitives(int [] rows) {
225                if (rows == null || rows.length == 0) return Collections.emptyList();
226                ArrayList<OsmPrimitive> ret = new ArrayList<OsmPrimitive>(rows.length);
227                for (int row: rows) {
228                    ret.add(data.get(row));
229                }
230                return ret;
231            }
232    
233            public OsmPrimitive getPrimitive(int row) {
234                return data.get(row);
235            }
236        }
237    
238        /**
239         * The column model
240         */
241        static class HistoryTableColumnModel extends DefaultTableColumnModel {
242            protected void createColumns() {
243                TableColumn col = null;
244                OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
245                // column 0 - History item
246                col = new TableColumn(0);
247                col.setHeaderValue(tr("Object with history"));
248                col.setCellRenderer(renderer);
249                addColumn(col);
250            }
251    
252            public HistoryTableColumnModel() {
253                createColumns();
254            }
255        }
256    
257        /**
258         * The action for reloading history information of the currently selected primitives.
259         *
260         */
261        class ReloadAction extends AbstractAction implements ListSelectionListener {
262    
263            public ReloadAction() {
264                putValue(Action.SMALL_ICON, ImageProvider.get("dialogs","refresh"));
265                putValue(Action.NAME, tr("Reload"));
266                putValue(Action.SHORT_DESCRIPTION, tr("Reload all currently selected objects and refresh the list."));
267                updateEnabledState();
268            }
269    
270            public void actionPerformed(ActionEvent e) {
271                int [] rows = historyTable.getSelectedRows();
272                if (rows == null || rows.length == 0) return;
273    
274                List<OsmPrimitive> selectedItems = model.getPrimitives(rows);
275                HistoryLoadTask task = new HistoryLoadTask();
276                task.add(selectedItems);
277                Main.worker.execute(task);
278            }
279    
280            protected void updateEnabledState() {
281                setEnabled(historyTable.getSelectedRowCount() > 0);
282            }
283    
284            public void valueChanged(ListSelectionEvent e) {
285                updateEnabledState();
286            }
287        }
288    
289        class ShowHistoryMouseAdapter extends MouseAdapter {
290            @Override
291            public void mouseClicked(MouseEvent e) {
292                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
293                    int row = historyTable.rowAtPoint(e.getPoint());
294                    HistoryBrowserDialogManager.getInstance().showHistory(Collections.singletonList(model.getPrimitive(row)));
295                }
296            }
297        }
298    
299        /**
300         * The action for showing history information of the current history item.
301         */
302        class ShowHistoryAction extends AbstractAction implements ListSelectionListener {
303            public ShowHistoryAction() {
304                putValue(Action.SMALL_ICON, ImageProvider.get("dialogs","history"));
305                putValue(Action.NAME, tr("Show"));
306                putValue(Action.SHORT_DESCRIPTION, tr("Display the history of the selected objects."));
307                updateEnabledState();
308            }
309    
310            public void actionPerformed(ActionEvent e) {
311                int [] rows = historyTable.getSelectedRows();
312                if (rows == null || rows.length == 0) return;
313                HistoryBrowserDialogManager.getInstance().showHistory(model.getPrimitives(rows));
314            }
315    
316            protected void updateEnabledState() {
317                setEnabled(historyTable.getSelectedRowCount() > 0);
318            }
319    
320            public void valueChanged(ListSelectionEvent e) {
321                updateEnabledState();
322            }
323        }
324    }