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.Component;
007import java.util.ArrayList;
008import java.util.Arrays;
009import java.util.Collection;
010import java.util.Collections;
011import java.util.LinkedList;
012import java.util.List;
013import java.util.Locale;
014import java.util.Objects;
015
016import javax.swing.DefaultListModel;
017import javax.swing.JLabel;
018import javax.swing.JList;
019import javax.swing.ListCellRenderer;
020import javax.swing.UIManager;
021
022import org.openstreetmap.josm.Main;
023import org.openstreetmap.josm.data.Bounds;
024import org.openstreetmap.josm.tools.ImageProvider;
025
026/**
027 * List class that read and save its content from the bookmark file.
028 * @since 6340
029 */
030public class BookmarkList extends JList<BookmarkList.Bookmark> {
031
032    /**
033     * Class holding one bookmarkentry.
034     */
035    public static class Bookmark implements Comparable<Bookmark> {
036        private String name;
037        private Bounds area;
038
039        /**
040         * Constructs a new {@code Bookmark} with the given contents.
041         * @param list Bookmark contents as a list of 5 elements.
042         * First item is the name, then come bounds arguments (minlat, minlon, maxlat, maxlon)
043         * @throws NumberFormatException if the bounds arguments are not numbers
044         * @throws IllegalArgumentException if list contain less than 5 elements
045         */
046        public Bookmark(Collection<String> list) {
047            List<String> array = new ArrayList<>(list);
048            if (array.size() < 5)
049                throw new IllegalArgumentException(tr("Wrong number of arguments for bookmark"));
050            name = array.get(0);
051            area = new Bounds(Double.parseDouble(array.get(1)), Double.parseDouble(array.get(2)),
052                              Double.parseDouble(array.get(3)), Double.parseDouble(array.get(4)));
053        }
054
055        /**
056         * Constructs a new empty {@code Bookmark}.
057         */
058        public Bookmark() {
059            area = null;
060            name = null;
061        }
062
063        /**
064         * Constructs a new unamed {@code Bookmark} for the given area.
065         * @param area The bookmark area
066         */
067        public Bookmark(Bounds area) {
068            this.area = area;
069        }
070
071        @Override public String toString() {
072            return name;
073        }
074
075        @Override
076        public int compareTo(Bookmark b) {
077            return name.toLowerCase(Locale.ENGLISH).compareTo(b.name.toLowerCase(Locale.ENGLISH));
078        }
079
080        @Override
081        public int hashCode() {
082            return Objects.hash(name, area);
083        }
084
085        @Override
086        public boolean equals(Object obj) {
087            if (this == obj) return true;
088            if (obj == null || getClass() != obj.getClass()) return false;
089            Bookmark bookmark = (Bookmark) obj;
090            return Objects.equals(name, bookmark.name) &&
091                    Objects.equals(area, bookmark.area);
092        }
093
094        /**
095         * Returns the bookmark area
096         * @return The bookmark area
097         */
098        public Bounds getArea() {
099            return area;
100        }
101
102        /**
103         * Returns the bookmark name
104         * @return The bookmark name
105         */
106        public String getName() {
107            return name;
108        }
109
110        /**
111         * Sets the bookmark name
112         * @param name The bookmark name
113         */
114        public void setName(String name) {
115            this.name = name;
116        }
117
118        /**
119         * Sets the bookmark area
120         * @param area The bookmark area
121         */
122        public void setArea(Bounds area) {
123            this.area = area;
124        }
125    }
126
127    /**
128     * Creates a bookmark list as well as the Buttons add and remove.
129     */
130    public BookmarkList() {
131        setModel(new DefaultListModel<Bookmark>());
132        load();
133        setVisibleRowCount(7);
134        setCellRenderer(new BookmarkCellRenderer());
135    }
136
137    /**
138     * Loads the bookmarks from file.
139     */
140    public final void load() {
141        DefaultListModel<Bookmark> model = (DefaultListModel<Bookmark>) getModel();
142        model.removeAllElements();
143        Collection<Collection<String>> args = Main.pref.getArray("bookmarks", null);
144        if (args != null) {
145            List<Bookmark> bookmarks = new LinkedList<>();
146            for (Collection<String> entry : args) {
147                try {
148                    bookmarks.add(new Bookmark(entry));
149                } catch (IllegalArgumentException e) {
150                    Main.error(e, tr("Error reading bookmark entry: %s", e.getMessage()));
151                }
152            }
153            Collections.sort(bookmarks);
154            for (Bookmark b : bookmarks) {
155                model.addElement(b);
156            }
157        }
158    }
159
160    /**
161     * Saves all bookmarks to the preferences file
162     */
163    public final void save() {
164        List<Collection<String>> coll = new LinkedList<>();
165        for (Object o : ((DefaultListModel<Bookmark>) getModel()).toArray()) {
166            String[] array = new String[5];
167            Bookmark b = (Bookmark) o;
168            array[0] = b.getName();
169            Bounds area = b.getArea();
170            array[1] = String.valueOf(area.getMinLat());
171            array[2] = String.valueOf(area.getMinLon());
172            array[3] = String.valueOf(area.getMaxLat());
173            array[4] = String.valueOf(area.getMaxLon());
174            coll.add(Arrays.asList(array));
175        }
176        Main.pref.putArray("bookmarks", coll);
177    }
178
179    static class BookmarkCellRenderer extends JLabel implements ListCellRenderer<BookmarkList.Bookmark> {
180
181        /**
182         * Constructs a new {@code BookmarkCellRenderer}.
183         */
184        BookmarkCellRenderer() {
185            setOpaque(true);
186            setIcon(ImageProvider.get("dialogs", "bookmark"));
187        }
188
189        protected void renderColor(boolean selected) {
190            if (selected) {
191                setBackground(UIManager.getColor("List.selectionBackground"));
192                setForeground(UIManager.getColor("List.selectionForeground"));
193            } else {
194                setBackground(UIManager.getColor("List.background"));
195                setForeground(UIManager.getColor("List.foreground"));
196            }
197        }
198
199        protected String buildToolTipText(Bookmark b) {
200            Bounds area = b.getArea();
201            StringBuilder sb = new StringBuilder(128);
202            sb.append("<html>min[latitude,longitude]=<strong>[")
203              .append(area.getMinLat()).append(',').append(area.getMinLon()).append("]</strong>"+
204                      "<br>max[latitude,longitude]=<strong>[")
205              .append(area.getMaxLat()).append(',').append(area.getMaxLon()).append("]</strong>"+
206                      "</html>");
207            return sb.toString();
208        }
209
210        @Override
211        public Component getListCellRendererComponent(JList<? extends Bookmark> list, Bookmark value, int index, boolean isSelected,
212                boolean cellHasFocus) {
213            renderColor(isSelected);
214            setText(value.getName());
215            setToolTipText(buildToolTipText(value));
216            return this;
217        }
218    }
219}