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