001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.history;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.marktr;
006import static org.openstreetmap.josm.tools.I18n.tr;
007
008import java.awt.BorderLayout;
009import java.awt.FlowLayout;
010import java.awt.event.ActionEvent;
011import java.awt.event.KeyEvent;
012import java.awt.event.WindowAdapter;
013import java.awt.event.WindowEvent;
014
015import javax.swing.AbstractAction;
016import javax.swing.JComponent;
017import javax.swing.JDialog;
018import javax.swing.JLabel;
019import javax.swing.JOptionPane;
020import javax.swing.JPanel;
021import javax.swing.KeyStroke;
022
023import org.openstreetmap.josm.Main;
024import org.openstreetmap.josm.data.osm.PrimitiveId;
025import org.openstreetmap.josm.data.osm.history.History;
026import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
027import org.openstreetmap.josm.data.osm.history.HistoryDataSetListener;
028import org.openstreetmap.josm.gui.SideButton;
029import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
030import org.openstreetmap.josm.gui.help.HelpUtil;
031import org.openstreetmap.josm.tools.ImageProvider;
032
033/**
034 * This is non-modal dialog, always showing on top, which displays history information
035 * about a given {@link org.openstreetmap.josm.data.osm.OsmPrimitive}.
036 * @since 1709
037 */
038public class HistoryBrowserDialog extends JDialog implements HistoryDataSetListener {
039
040    /** the embedded browser */
041    private final HistoryBrowser browser = new HistoryBrowser();
042    private final CloseAction closeAction = new CloseAction();
043    private final JLabel titleLabel = new JLabel("", JLabel.CENTER);
044
045    /**
046     * Constructs a new {@code HistoryBrowserDialog}.
047     *
048     * @param history the history to be displayed
049     */
050    public HistoryBrowserDialog(History history) {
051        super(JOptionPane.getFrameForComponent(Main.parent), false);
052        build();
053        setHistory(history);
054        setTitle(buildTitle(history));
055        pack();
056        if (getInsets().top > 0) {
057            titleLabel.setVisible(false);
058        }
059        HistoryDataSet.getInstance().addHistoryDataSetListener(this);
060        addWindowListener(new WindowClosingAdapter());
061    }
062
063    /**
064     * Constructs the title for this dialog
065     *
066     * @param h the current history
067     * @return the title for this dialog
068     */
069    static String buildTitle(History h) {
070        String title;
071        switch (h.getEarliest().getType()) {
072        case NODE: title = marktr("History for node {0}");
073            break;
074        case WAY: title = marktr("History for way {0}");
075            break;
076        case RELATION: title = marktr("History for relation {0}");
077            break;
078        default: title = "";
079        }
080        return tr(title, Long.toString(h.getId()));
081    }
082
083    @Override
084    public void setTitle(String title) {
085        super.setTitle(title);
086        if (titleLabel != null) {
087            titleLabel.setText(title);
088        }
089    }
090
091    /**
092     * builds the GUI
093     */
094    protected void build() {
095        setLayout(new BorderLayout());
096
097        add(titleLabel, BorderLayout.NORTH);
098
099        add(browser, BorderLayout.CENTER);
100
101        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
102
103        SideButton btn = new SideButton(new ReloadAction());
104        btn.setName("btn.reload");
105        pnl.add(btn);
106
107        btn = new SideButton(closeAction);
108        final String closeHistoryBrowserDialogKey = "CloseHistoryBrowserDialog";
109        KeyStroke escapeKey = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
110        getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(escapeKey, closeHistoryBrowserDialogKey);
111        getRootPane().getActionMap().put(closeHistoryBrowserDialogKey, closeAction);
112        btn.setName("btn.close");
113        pnl.add(btn);
114
115        btn = new SideButton(new ContextSensitiveHelpAction(ht("/Action/ObjectHistory")));
116        btn.setName("btn.help");
117        pnl.add(btn);
118        add(pnl, BorderLayout.SOUTH);
119
120        HelpUtil.setHelpContext(getRootPane(), ht("/Action/ObjectHistory"));
121    }
122
123    /**
124     * Sets the current history.
125     * @param history current history
126     */
127    protected void setHistory(History history) {
128        browser.populate(history);
129    }
130
131    /**
132     * Removes this history browser model as listener for data change and layer change events.
133     */
134    public void unlinkAsListener() {
135        getHistoryBrowser().getModel().unlinkAsListener();
136    }
137
138    /* ---------------------------------------------------------------------------------- */
139    /* interface HistoryDataSetListener                                                   */
140    /* ---------------------------------------------------------------------------------- */
141
142    @Override
143    public void historyUpdated(HistoryDataSet source, PrimitiveId primitiveId) {
144        if (primitiveId == null || primitiveId.equals(browser.getHistory().getPrimitiveId())) {
145            History history = source.getHistory(browser.getHistory().getPrimitiveId());
146            if (history != null) {
147                browser.populate(history);
148            }
149        }
150    }
151
152    @Override
153    public void historyDataSetCleared(HistoryDataSet source) {
154        closeAction.run();
155    }
156
157    class CloseAction extends AbstractAction {
158        CloseAction() {
159            putValue(NAME, tr("Close"));
160            putValue(SHORT_DESCRIPTION, tr("Close the dialog"));
161            putValue(SMALL_ICON, ImageProvider.get("ok"));
162        }
163
164        public void run() {
165            getHistoryBrowser().getModel().unlinkAsListener();
166            HistoryDataSet.getInstance().removeHistoryDataSetListener(HistoryBrowserDialog.this);
167            HistoryBrowserDialogManager.getInstance().hide(HistoryBrowserDialog.this);
168        }
169
170        @Override
171        public void actionPerformed(ActionEvent e) {
172            run();
173        }
174    }
175
176    class ReloadAction extends AbstractAction {
177        ReloadAction() {
178            putValue(NAME, tr("Reload"));
179            putValue(SHORT_DESCRIPTION, tr("Reload the history from the server"));
180            putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh"));
181        }
182
183        @Override
184        public void actionPerformed(ActionEvent e) {
185            HistoryLoadTask task = new HistoryLoadTask();
186            task.add(browser.getHistory());
187            Main.worker.submit(task);
188        }
189    }
190
191    class WindowClosingAdapter extends WindowAdapter {
192        @Override
193        public void windowClosing(WindowEvent e) {
194            closeAction.run();
195        }
196    }
197
198    /**
199     * Replies the history browser.
200     * @return the history browser
201     */
202    public HistoryBrowser getHistoryBrowser() {
203        return browser;
204    }
205}