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