001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.download;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.GridBagConstraints;
007import java.awt.GridBagLayout;
008import java.awt.Insets;
009import java.awt.event.ActionEvent;
010import java.awt.event.MouseAdapter;
011import java.awt.event.MouseEvent;
012import java.util.List;
013
014import javax.swing.AbstractAction;
015import javax.swing.DefaultListModel;
016import javax.swing.JButton;
017import javax.swing.JOptionPane;
018import javax.swing.JPanel;
019import javax.swing.JScrollPane;
020import javax.swing.SwingUtilities;
021import javax.swing.event.ListSelectionEvent;
022import javax.swing.event.ListSelectionListener;
023
024import org.openstreetmap.josm.data.Bounds;
025import org.openstreetmap.josm.data.UserIdentityManager;
026import org.openstreetmap.josm.gui.MainApplication;
027import org.openstreetmap.josm.gui.download.BookmarkList.Bookmark;
028import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
029import org.openstreetmap.josm.gui.widgets.JosmTextArea;
030import org.openstreetmap.josm.tools.ImageProvider;
031import org.openstreetmap.josm.tools.Utils;
032
033/**
034 * DownloadAreaSelector which manages a list of "bookmarks", i.e. a list of
035 * name download areas.
036 *
037 */
038public class BookmarkSelection implements DownloadSelection {
039
040    /** the currently selected download area. One can add bookmarks for this
041     * area, if not null
042     */
043    private Bounds currentArea;
044    /** the list of bookmarks */
045    private BookmarkList bookmarks;
046
047    /** the parent download GUI */
048    private DownloadDialog parent;
049
050    /** displays information about the current download area */
051    private final JMultilineLabel lblCurrentDownloadArea = new JMultilineLabel("");
052    private final JosmTextArea bboxDisplay = new JosmTextArea();
053    /** the add action */
054    private final AddAction actAdd = new AddAction();
055
056    /**
057     * Creates the panel with the action buttons on the left
058     *
059     * @return the panel with the action buttons on the left
060     */
061    protected JPanel buildButtonPanel() {
062        JPanel pnl = new JPanel(new GridBagLayout());
063        GridBagConstraints gc = new GridBagConstraints();
064        gc.gridy = 0;
065        RemoveAction removeAction = new RemoveAction();
066        bookmarks.addListSelectionListener(removeAction);
067        pnl.add(new JButton(removeAction), gc);
068
069        gc.gridy = 1;
070        RenameAction renameAction = new RenameAction();
071        bookmarks.addListSelectionListener(renameAction);
072        pnl.add(new JButton(renameAction), gc);
073
074        gc.gridy = 2;
075        RefreshAction refreshAction = new RefreshAction();
076        pnl.add(new JButton(refreshAction), gc);
077
078        gc.fill = GridBagConstraints.BOTH;
079        gc.weightx = 1.0;
080        gc.weighty = 1.0;
081        gc.gridy = 3;
082        pnl.add(new JPanel(), gc); // just a filler
083        return pnl;
084    }
085
086    protected JPanel buildDownloadAreaAddPanel() {
087        JPanel pnl = new JPanel(new GridBagLayout());
088
089        GridBagConstraints gc = new GridBagConstraints();
090        gc.anchor = GridBagConstraints.NORTHWEST;
091        gc.insets = new Insets(5, 5, 5, 5);
092        pnl.add(lblCurrentDownloadArea, gc);
093
094        gc.weightx = 1.0;
095        gc.weighty = 1.0;
096        bboxDisplay.setEditable(false);
097        bboxDisplay.setBackground(pnl.getBackground());
098        bboxDisplay.addFocusListener(new BoundingBoxSelection.SelectAllOnFocusHandler(bboxDisplay));
099        pnl.add(bboxDisplay, gc);
100
101        gc.anchor = GridBagConstraints.NORTHEAST;
102        gc.fill = GridBagConstraints.HORIZONTAL;
103        gc.weightx = 0.0;
104        gc.weighty = 0.0;
105        gc.insets = new Insets(5, 5, 5, 5);
106        pnl.add(new JButton(actAdd), gc);
107        return pnl;
108    }
109
110    @Override
111    public void addGui(final DownloadDialog gui) {
112        JPanel dlg = new JPanel(new GridBagLayout());
113        if (gui != null)
114            gui.addDownloadAreaSelector(dlg, tr("Bookmarks"));
115        GridBagConstraints gc = new GridBagConstraints();
116
117        bookmarks = new BookmarkList();
118        bookmarks.getSelectionModel().addListSelectionListener(e -> {
119            Bookmark b = bookmarks.getSelectedValue();
120            if (b != null && gui != null) {
121                gui.boundingBoxChanged(b.getArea(), this);
122            }
123        });
124        bookmarks.addMouseListener(new DoubleClickAdapter());
125
126        gc.fill = GridBagConstraints.HORIZONTAL;
127        gc.weightx = 1.0;
128        gc.weighty = 0.0;
129        gc.gridwidth = 2;
130        dlg.add(buildDownloadAreaAddPanel(), gc);
131
132        gc.gridwidth = 1;
133        gc.gridx = 0;
134        gc.gridy = 1;
135        gc.fill = GridBagConstraints.VERTICAL;
136        gc.weightx = 0.0;
137        gc.weighty = 1.0;
138        dlg.add(buildButtonPanel(), gc);
139
140        gc.gridwidth = 1;
141        gc.gridx = 1;
142        gc.gridy = 1;
143        gc.fill = GridBagConstraints.BOTH;
144        gc.weightx = 1.0;
145        gc.weighty = 1.0;
146        gc.gridx = 1;
147        dlg.add(new JScrollPane(bookmarks), gc);
148
149        this.parent = gui;
150    }
151
152    protected void updateDownloadAreaLabel() {
153        if (currentArea == null) {
154            lblCurrentDownloadArea.setText(tr("<html>There is currently no download area selected.</html>"));
155        } else {
156            lblCurrentDownloadArea.setText(tr("<html><strong>Current download area</strong> (minlon, minlat, maxlon, maxlat): </html>"));
157            bboxDisplay.setText(currentArea.toBBox().toStringCSV(","));
158        }
159    }
160
161    /**
162     * Sets the current download area
163     *
164     * @param area the download area.
165     */
166    @Override
167    public void setDownloadArea(Bounds area) {
168        if (area == null)
169            return;
170        this.currentArea = area;
171        bookmarks.clearSelection();
172        updateDownloadAreaLabel();
173        actAdd.setEnabled(true);
174    }
175
176    /**
177     * The action to add a new bookmark for the current download area.
178     *
179     */
180    class AddAction extends AbstractAction {
181        AddAction() {
182            putValue(NAME, tr("Create bookmark"));
183            new ImageProvider("dialogs", "bookmark-new").getResource().attachImageIcon(this, true);
184            putValue(SHORT_DESCRIPTION, tr("Add a bookmark for the currently selected download area"));
185        }
186
187        @Override
188        public void actionPerformed(ActionEvent e) {
189            if (currentArea == null) {
190                JOptionPane.showMessageDialog(
191                        MainApplication.getMainFrame(),
192                        tr("Currently, there is no download area selected. Please select an area first."),
193                        tr("Information"),
194                        JOptionPane.INFORMATION_MESSAGE
195                );
196                return;
197            }
198            Bookmark b = new Bookmark();
199            b.setName(
200                    JOptionPane.showInputDialog(
201                            MainApplication.getMainFrame(), tr("Please enter a name for the bookmarked download area."),
202                            tr("Name of location"),
203                            JOptionPane.QUESTION_MESSAGE)
204            );
205            b.setArea(currentArea);
206            if (!Utils.isEmpty(b.getName())) {
207                ((DefaultListModel<BookmarkList.Bookmark>) bookmarks.getModel()).addElement(b);
208                bookmarks.save();
209            }
210        }
211    }
212
213    class RemoveAction extends AbstractAction implements ListSelectionListener {
214        /**
215         * Constructs a new {@code RemoveAction}.
216         */
217        RemoveAction() {
218            new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this, true);
219            putValue(SHORT_DESCRIPTION, tr("Remove the currently selected bookmarks"));
220            updateEnabledState();
221        }
222
223        @Override
224        public void actionPerformed(ActionEvent e) {
225            List<Bookmark> sels = bookmarks.getSelectedValuesList();
226            if (Utils.isEmpty(sels))
227                return;
228            for (Object sel: sels) {
229                ((DefaultListModel<Bookmark>) bookmarks.getModel()).removeElement(sel);
230            }
231            bookmarks.save();
232        }
233
234        protected final void updateEnabledState() {
235            setEnabled(bookmarks.getSelectedIndices().length > 0);
236        }
237
238        @Override
239        public void valueChanged(ListSelectionEvent e) {
240            updateEnabledState();
241        }
242    }
243
244    class RenameAction extends AbstractAction implements ListSelectionListener {
245        /**
246         * Constructs a new {@code RenameAction}.
247         */
248        RenameAction() {
249            new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this, true);
250            putValue(SHORT_DESCRIPTION, tr("Rename the currently selected bookmark"));
251            updateEnabledState();
252        }
253
254        @Override
255        public void actionPerformed(ActionEvent e) {
256            List<Bookmark> sels = bookmarks.getSelectedValuesList();
257            if (sels == null || sels.size() != 1)
258                return;
259            Bookmark b = sels.get(0);
260            Object value =
261                JOptionPane.showInputDialog(
262                        MainApplication.getMainFrame(), tr("Please enter a name for the bookmarked download area."),
263                        tr("Name of location"),
264                        JOptionPane.QUESTION_MESSAGE,
265                        null,
266                        null,
267                        b.getName()
268                );
269            if (value != null) {
270                b.setName(value.toString());
271                bookmarks.save();
272                bookmarks.repaint();
273            }
274        }
275
276        protected final void updateEnabledState() {
277            setEnabled(bookmarks.getSelectedIndices().length == 1);
278        }
279
280        @Override
281        public void valueChanged(ListSelectionEvent e) {
282            updateEnabledState();
283        }
284    }
285
286    class RefreshAction extends AbstractAction {
287        /**
288         * Constructs a new {@code RefreshAction}.
289         */
290        RefreshAction() {
291            new ImageProvider("dialogs/changeset", "downloadchangeset").getResource().attachImageIcon(this, true);
292            putValue(SHORT_DESCRIPTION, tr("Download bookmarks for my {0} last changesets", BookmarkList.MAX_CHANGESET_BOOKMARKS.get()));
293            setEnabled(!UserIdentityManager.getInstance().isAnonymous());
294        }
295
296        @Override
297        public void actionPerformed(ActionEvent e) {
298            bookmarks.refreshChangesetBookmarks();
299        }
300    }
301
302    class DoubleClickAdapter extends MouseAdapter {
303        @Override
304        public void mouseClicked(MouseEvent e) {
305            if (!(SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2))
306                return;
307            int idx = bookmarks.locationToIndex(e.getPoint());
308            if (idx < 0 || idx >= bookmarks.getModel().getSize())
309                return;
310            Bookmark b = bookmarks.getModel().getElementAt(idx);
311            if (b != null) {
312                parent.startDownload(b.getArea());
313            }
314        }
315    }
316}